Skip to content

JavaScript API Reference

Complete reference for the baselode npm package (v0.1.x).

All exports are available as named imports from 'baselode':

js
import { loadCollars, desurveyTraces, TracePlot } from 'baselode';

Data Model Constants

js
import {
  HOLE_ID, LATITUDE, LONGITUDE, ELEVATION,
  AZIMUTH, DIP, FROM, TO, MID, DEPTH,
  PROJECT_ID, EASTING, NORTHING, CRS, STRIKE,
  BASELODE_DATA_MODEL_DRILL_COLLAR,
  BASELODE_DATA_MODEL_DRILL_SURVEY,
  BASELODE_DATA_MODEL_DRILL_ASSAY,
  BASELODE_DATA_MODEL_STRUCTURAL_POINT,
  DEFAULT_COLUMN_MAP
} from 'baselode';

These are string constants ("hole_id", "latitude", etc.) that match the Python data model.


Data Layer

Column utilities

normalizeFieldName(name)

Normalise a column name to lowercase, trimmed string.

standardizeColumns(rows, columnMap?)

Rename fields in an array of row objects using the default column map (and an optional override).

js
const normalised = standardizeColumns(rawRows);

standardizeRowArray(rows)

Apply column standardisation to an array of rows.


Collar / survey / assay loaders

loadCollars(source, options?)

Load collar data from a CSV text string or row array. Returns a normalised array of collar objects.

loadSurveys(source, options?)

Load survey data. Returns a normalised array sorted by hole_id, depth.

loadAssays(source, options?)

Load assay interval data. Computes a mid field. Returns a normalised array.

loadTable(source, options?)

Low-level loader shared by all high-level loaders.

assembleDataset({ collars, surveys, assays, structures, geotechnical, metadata? })

Assemble pre-loaded arrays into a dataset object.

js
const dataset = assembleDataset({ collars, surveys, assays });
// { collars, surveys, assays, structures, geotechnical, metadata }

joinAssaysToTraces(assays, traces, onCols?)

Left-join 3D trace positions onto assay rows.

filterByProject(rows, projectId)

Filter rows to a specific project_id.

coerceNumeric(rows, columns)

Convert listed fields to numbers (or NaN) for each row.


Assay-focused loaders

parseAssayHoleIds(csvText)

Parse a CSV and return the list of unique hole_id values.

parseAssayHoleIdsWithAssays(csvText)

Parse a CSV and return { holeIds, assayColumns }.

parseAssayHole(csvText, holeId)

Parse all rows for a single hole.

parseAssaysCSV(csvText)

Parse the full assay CSV.

loadAssayMetadata(csvText)

Extract column metadata without loading all rows.

loadAssayHole(csvText, holeId)

Load assay rows for one hole.

buildAssayState(csvText)

Build a full assay state object (hole IDs + column names + data).

loadAssayFile(csvText)

Load all assay data into memory.

reorderHoleIds(holeIds)

Sort hole IDs in a natural order.

deriveAssayProps(rows)

Derive the list of assay property columns from a row array.


CSV row utilities

normalizeCsvRow(row)

Normalise a single parsed CSV row (trim strings, coerce numbers).

pickFirstPresent(row, candidates)

Return the first non-null value found in row by key list.


Error utilities

toError(value)

Coerce any value to an Error instance.

withDataErrorContext(fn, context)

Wrap a function with contextual error messages.

logDataWarning(message, ...args)

Log a data-related warning.

logDataInfo(message, ...args)

Log a data-related info message.


Field sets

ASSAY_NON_VALUE_FIELDS

Set of column names that are structural/metadata (not analyte values).


Drillhole loaders

parseDrillholesCSV(csvText)

Parse a desurveyed-trace CSV (hole_id, easting/x, northing/y, elevation/z, md/depth, …). Returns { holes: [{ id, points: [...] }] }.

Source column-name aliases are standardised on read — md, measured_depth, survey_depth all collapse to the canonical depth field. Each output point carries both point.depth (canonical) and point.md (preserved alias) so consumers reading either name get a finite value.


Block model loaders

parseBlockModelCSV(csvText)

Parse a block model CSV. Returns an array of block objects.

normalizeBlockRow(row)

Normalise a single block model row.

loadBlockModelMetadata(csvText)

Extract metadata (column names, coordinate ranges) from a block model CSV.

calculatePropertyStats(blocks, property)

Compute min/max/mean for a property across all blocks.

getBlockStats(blocks, property)

Return { min, max, mean } for a property.

filterBlocks(blocks, { property, min, max })

Filter blocks by a property value range.

calculateBlockVolume(block)

Compute the volume of a single block.

getColorForValue(value, colorScale)

Get the hex colour for a value from a color scale.


Grade block loaders

loadGradeBlocksFromJson(input)

Parse and validate a grade block set from a JSON object or JSON string. Throws if schema_version is not "1.0" or required fields are missing. Returns a GradeBlockSet:

js
{
  schema_version: "1.0",
  units: "m",
  blocks: [{ id, name, vertices, triangles, attributes, material }, ...]
}

gradeBlockToThreeGeometry(block)

Convert a single grade block to a THREE.BufferGeometry (positions + triangle indices). No normal attribute is computed — flatShading: true on the material handles per-face lighting correctly for hard-edged polyhedral geometry.

addGradeBlocksToScene(scene, blockSet, options?)

Create THREE.Mesh objects for all blocks and add them to a THREE.Scene. Each mesh uses a flat-shaded MeshStandardMaterial and carries a hidden THREE.LineSegments child (EdgesGeometry, 15° threshold) for per-edge highlight on selection. Returns the THREE.Group containing all meshes.

options: { defaultOpacity } — fallback opacity when block.material.opacity is not set (default 1.0).

mesh.userData per mesh:

FieldDescription
idBlock id from JSON
attributesArbitrary attributes object from JSON

Structural loaders

parseStructuralPointsCSV(csvText)

Parse a structural points CSV. Returns an array of { hole_id, depth, dip, azimuth, alpha, beta, comments }.

parseStructuralIntervalsCSV(csvText)

Parse a structural intervals CSV.

parseStructuralCSV(csvText)

Auto-detect point vs interval schema and parse accordingly.

validateStructuralPoints(rows)

Validate structural point rows. Returns { valid, errors }.

groupRowsByHole(rows)

Group an array of rows into a Map<holeId, row[]>.


Unified loader

parseAssayCsvTextToHoles(csvText)

Parse an assay CSV and group rows by hole.

parseUnifiedDataset(assaysCsvText, structuralCsvText)

Load and merge assays and structural data. Returns a combined array tagged with _source.


Structural positions

interpolateTrace(trace, depth)

Interpolate a 3D position along a hole trace at the given measured depth.

alphaBetaToNormal(alpha, beta, traceOrientation)

Convert alpha/beta angles to a 3D normal vector.

computeStructuralPositions(structuralRows, traces)

Attach 3D positions and normal vectors to all structural measurement rows.


DrillholeSet

Composition root for the drilling tables of a project. Mirrors the Python baselode.drill.DrillholeSet class — holds collar + survey + N interval tables and exposes the existing function-based API as methods. No new logic; the trace is cached after the first desurvey() call.

js
import { DrillholeSet } from 'baselode';

const db = new DrillholeSet(collarRows, surveyRows, {
  crs: 'EPSG:32750',
  project: 'goldfields-2026',
});

db.addTable('assay', assayRows).addTable('geology', lithoRows, 'litho');

const report = db.validate();
const traces = db.desurvey({ step: 1 });
Method / propertyMirrors Python
new DrillholeSet(collar, survey, { crs, project, holeCol })DrillholeSet(collar, survey, crs=..., project=..., hole_col=...)
db.addTable(name, rows, kind?)db.add_table(name, df, kind=...)
db.holesdb.holes
db.tracesdb.traces
db.desurvey({ method, step, force })db.desurvey(method=..., step=..., force=...)
db.validate(options)db.validate(**kwargs)
db.get(name) / db.has(name)db[name] / name in db
db.toString()repr(db)

OMF export is Python-only (db.to_omf), per the TRK-111 scope.


Drillhole DB Validation

Mirror of baselode.drill.validate.validate_drillhole_db. Returns a structured { summary, issues } report covering duplicate collar IDs, single-station surveys, az/dip range, orphan intervals, gaps/overlaps, beyond-max_depth intervals, and <MDL sentinels.

js
import {
  validateDrillholeDb,
  fixSingleStationSurveys,
  normalizeAzimuth,
  dropOrphanIntervals,
  swapInvertedIntervals,
  replaceBelowDetectionLimit,
  SEVERITY_ERROR,
  SEVERITY_WARNING,
  SEVERITY_INFO,
} from 'baselode';

validateDrillholeDb({ collar, survey, intervalTables }, options?)

Run all checks in one pass.

js
const report = validateDrillholeDb({
  collar: collarRows,
  survey: surveyRows,
  intervalTables: { assay: assayRows, geology: geologyRows },
});
// {
//   summary: { error, warning, info },
//   issues: [{ check, severity, hole_id, table, row_index, message, fix }, ...]
// }

options accepts column-name overrides (holeCol, depthCol, azimuthCol, dipCol, fromCol, toCol, maxDepthCol — defaults from the data-model constants) plus allowFullCircle (default false). When true, azimuth = 360 is accepted as valid (closed interval [0, 360]); otherwise the strict mathematical convention [0, 360) is used and 360 is reported as an error.

fixSingleStationSurveys(survey, collar?, options?)

Append a synthetic second station for any hole with only one row. Uses collar.max_depth when present, otherwise depth + 1.0. Returns a new sorted array.

dropOrphanIntervals(table, collar, options?)

Drop interval rows whose hole_id is not in collar. Complement of the orphan_intervals validation check. options.holeCol overrides the column name.

swapInvertedIntervals(table, options?)

Swap from/to where to < from. Fixes the common data-entry typo; rows where to === from (zero-length) are left untouched for manual review. All other fields preserved. options.fromCol / options.toCol override the column names.

normalizeAzimuth(survey, options?)

Wrap survey azimuths into [0, 360) via value mod 360. Folds 360 to 0, normalizes negatives, idempotent for valid values. null / non-numeric cells are left untouched. options.azimuthCol overrides the column name (default AZIMUTH).

replaceBelowDetectionLimit(rows, options?)

Replace <MDL string sentinels (e.g. "<0.005") with MDL * sentinelFactor (default 0.5).

OptionTypeDefaultDescription
columnsstring[] | nullnullColumns to scan; null = scan every column
sentinelFactornumber0.5Multiplier applied to the detected limit

Severity constants

SEVERITY_ERROR "error", SEVERITY_WARNING "warning", SEVERITY_INFO "info" — re-exported so consumer code can group issues by severity without string literals.


Interval Algebra

Pure from-to interval primitives, mirroring the Python baselode.drill.intervals module. Each function takes an array of row objects keyed by hole_id, from, to. All field-name defaults come from the data model constants (HOLE_ID, FROM, TO); override via the options object.

js
import {
  intervalLength,
  fromToMidpoints,
  detectGaps,
  detectOverlaps,
  splitAt,
  clip,
  mergeTables,
} from 'baselode';

intervalLength(rows, options?)

Per-row length (to - from). Returns number[].

fromToMidpoints(rows, options?)

Per-row midpoint depth ((from + to) / 2). Returns number[].

detectGaps(rows, options?)

Uncovered downhole ranges between consecutive intervals, per hole. Returns an array of { hole_id, from, to, length }. options.minGap (default 0) sets the minimum gap length to report.

detectOverlaps(rows, options?)

All overlapping interval pairs per hole. Returns an array of { hole_id, from, to, length, first_index, second_index }, where the indices are positional into the input array.

splitAt(rows, depths, options?)

Split intervals at boundary depths. Any depth strictly inside (from, to) becomes a new boundary; the row is replaced by sub-intervals inheriting all other fields.

depths accepts:

  • a number — applied to every hole
  • a number[] — applied to every hole
  • { [hole_id]: number[] } — per-hole boundaries
  • an array of { hole_id, depth } rows

clip(rows, fromDepth, toDepth, options?)

Clip intervals to a downhole window. Intervals entirely outside [fromDepth, toDepth] are dropped; straddling intervals have their from/to pulled to the boundary. Pass null/undefined for either bound to disable that side.

mergeTables(tables, options?)

Left-join multiple interval tables onto a common from-to support via boundary intersection. tables is an object literal where the first key is the left (anchoring) table. Each output row carries <table>_<col> values looked up at the sub-interval midpoint; columns from a later table missing a covering row are null.

js
const merged = mergeTables({ assay: assayRows, litho: lithoRows });
// each row: { hole_id, from, to, assay_<col>, ..., litho_<col>, ... }

Options object

js
{
  fromCol: string,   // default: FROM ("from")
  toCol:   string,   // default: TO ("to")
  holeCol: string,   // default: HOLE_ID ("hole_id")
  minGap:  number,   // detectGaps only; default: 0
}

3D Interpolation Volumes

GPU-rendered IDW (inverse-distance-weighted) interpolation volumes for visualising how a drilling attribute varies between sample points in 3D. See the guide section for an end-to-end example.

IDWVolumeLayer(options)

One-call wrapper that builds the spatial index, evaluates the IDW field over a voxel grid, and renders the result as a Three.js mesh.

OptionTypeDefaultMeaning
samplesArray<InterpSamplePoint>[]Sample points: { id, x, y, z, value }. Use buildInterpSamplesFromAssays to derive from raw assay rows
boundsVolumeBounds | nullautoAxis-aligned bbox to interpolate over. Auto-computed from samples if omitted
boundsPaddingnumber10Extra world units added on every side of the auto-computed bounds
idw.powernumber2Distance exponent for the IDW weight 1 / d^p
idw.searchRadiusnumber50Maximum distance (world units) to query for neighbours per voxel
idw.maxNeighborsnumberunsetCap on neighbour count per query; null = unlimited
idw.minNeighborsnumber1Below this, the voxel is marked no-data
grid.dims[nx, ny, nz][32, 32, 32]Voxel count per axis. Mutually exclusive with grid.voxelSize
grid.voxelSize[vx, vy, vz]Voxel size in world units; the grid count is derived from the bounds
displayMin / displayMaxnumber0 / 1Value range mapped to the colour gradient
opacitynumber0.4[0, 1] per-voxel alpha; 1 makes the volume fully opaque
thresholdnumber | nullnullNormalised [0, 1] cutoff; voxels below this are skipped
blockModebooleantrueCrisp voxel-block look (true) vs trilinear-smoothed (false)
stepsnumber64Ray-march samples per fragment
colorLow / colorHigh[r, g, b]blue / redTwo-stop colour gradient

Methods:

  • await layer.rebuild({ onProgress?, }) — re-build the spatial index + voxel grid. Returns when the field is uploaded to the GPU. Honours an internal cancellation token (back-to-back rebuild() calls abort the previous build).
  • layer.setOpacity(v) / setThreshold(v) / setBlockMode(b) — display-only knobs, no rebuild.
  • layer.setClipBounds(min, max) — per-axis clip in normalised [0, 1] box-local space. setClipBounds([0,0,0], [1,1,0.6]) slices the top 40% of Z off the rendered volume.
  • layer.setSamples(samples) / setIDWOptions(opts) / setGridOptions(opts) — update inputs; call rebuild() afterwards.
  • layer.getValueAt(x, y, z) — interpolate a single point in world coords without going through the GPU.
  • layer.dispose() — release the GPU resources. Remove object3D from the scene first.

Property:

  • layer.object3D — the Three.js mesh to add to the scene.

IDWVolumeRenderer()

Lower-level renderer used internally by the layer. Useful when you want to drive the voxel grid yourself.

  • renderer.setGrid(grid, options?) — upload a VoxelGrid to a Data3DTexture and configure display options.
  • renderer.setDisplayOptions(options) — update display knobs without re-uploading the texture.
  • renderer.setClipBounds(min, max) — same as the layer method.
  • renderer.setVisible(visible) — show/hide the volume.
  • renderer.dispose() — release the texture + material.
  • renderer.object3D — the Three.js mesh.

IDWSampler(samples, options?)

Pure scalar-field sampler. No Three.js dependency — reusable for slice/probe tools or CPU-side analytics.

  • new IDWSampler(samples, { power, searchRadius, maxNeighbors, minNeighbors, nodataValue, epsilon, cellSize })
  • sampler.getValueAt(x, y, z)number | NaN
  • sampler.setPower(p) / setSearchRadius(r) / setMaxNeighbors(n) / setSamples(arr) — runtime configuration; no rebuild needed for the first three.

SpatialHash3D({ cellSize? })

Uniform 3D spatial hash for radius queries. Used internally by IDWSampler but exported in case callers need a fast queryRadius(x, y, z, r) on their own point sets.

  • index.build(points) — points must expose numeric x, y, z.
  • index.queryRadius(x, y, z, radius)Array<T>
  • index.queryKNearest(x, y, z, k)Array<T>

buildVoxelGrid(sampler, bounds, dims, options?)

Async builder that walks the sampler over a regular grid. Returns Promise<VoxelGrid> with { bounds, dims, voxelSize, values, nodataMask }.

OptionDefaultMeaning
syncfalseDisable the periodic await new Promise(setTimeout) yields — set when calling from a Web Worker
onProgress({ completed, total }) => void callback fired after each XZ slab
cancellationTokenObject with .cancelled — set to true mid-build to abort

Also exported:

  • voxelGridStats(bounds, dims){ voxelSize, total } — compute size + count without building.

computeVolumeBounds(points, padding?) / buildVolumeBoundsFromMinMax(...)

Compute VolumeBounds = { min, max, size, center } from a point set, or directly from explicit min/max values.

buildInterpSamplesFromAssays(assayRows, attributeName)

Convert raw assay-interval rows into the InterpSamplePoint shape the IDW pipeline expects. Uses desurveyed x/y/z when present on the rows, falls back to (x_collar, y_collar, z_collar - midMd) for vertical-hole approximations when desurvey isn't available.


Compositing

Length-weighted compositing of downhole intervals. Soft + hard boundary modes; true-thickness is Python-only because it depends on a desurveyed trace.

compositeIntervals(intervals, valueCol, options?)

Composite an array of interval rows into fixed-length downhole bins.

ParameterTypeDefaultMeaning
intervalsArray<Object>Interval rows. Each row must carry hole_id, from, to and valueCol; in mode === 'hard' also boundaryCol
valueColstringProperty to composite
options.lengthnumber1.0Composite length (positive, finite)
options.method'average' | 'sum''average'Length-weighted average or total contribution
options.mode'soft' | 'hard''soft'Soft = bins extend across the full hole and may cross contacts. Hard = bins reset at every change in boundaryCol
options.boundaryColstringDomain column for hard mode (required when mode === 'hard')
options.residual'discard' | 'add_to_previous' | 'distribute''discard'Tail-of-domain handling in hard mode
options.fromCol / options.toCol / options.holeColstring'from' / 'to' / 'hole_id'Column-name overrides

Returns: Array<Object> of composites with the same key names as the input plus (in hard mode) the boundary column carrying the originating domain value.

Throws if length is not a positive finite number, if method isn't 'average' or 'sum', if mode is unknown, or if mode === 'hard' without a boundaryCol.

Mirrors baselode.drill.composite.composite_intervals in Python (mass-balance semantics, residual rules, run-grouping).


Column Metadata

classifyColumns(rows)

Classify each column in a row array as one of: DISPLAY_NUMERIC | DISPLAY_CATEGORICAL | DISPLAY_COMMENT | DISPLAY_TADPOLE | DISPLAY_HIDDEN

getChartOptions(colName, rows)

Return available chart types for a column.

defaultChartType(colName, rows)

Return the default chart type for a column.

Constants

ConstantValueDescription
DISPLAY_NUMERIC"numeric"Numeric assay column
DISPLAY_CATEGORICAL"categorical"Categorical / lithology column
DISPLAY_COMMENT"comment"Free-text comment column
DISPLAY_HIDDEN"hidden"Non-display metadata column
DISPLAY_TADPOLE"tadpole"Structural alpha/beta tadpole symbol
CHART_OPTIONSMap of chart type labels
HIDDEN_COLUMNSSet<string>Column names hidden by default
COMMENT_COLUMN_NAMESSet<string>Column names treated as comments

Visualization Layer

Analytics plots (TRK-52)

Plotly config builders for the foundational analyte-exploration plot family. Each returns { data, layout } ready for Plotly.react(container, data, layout). All accept categorical colouring via the built-in commodity / lithology maps or an explicit {category: cssColour} object.

js
import {
  buildScatterPlotConfig,
  buildHistogramPlotConfig,
  buildBoxPlotConfig,
  buildViolinPlotConfig,
  buildTernaryPlotConfig,
} from 'baselode';

const { data, layout } = buildScatterPlotConfig(rows, {
  xProp: 'au_ppm',
  yProp: 'ag_ppm',
  colorBy: 'lithology',
  colourMap: 'lithology',
});
BuilderRequired optionsOptional
buildScatterPlotConfigxProp, yPropcolorBy, colourMap, markerColor/Size/Opacity, log: { x, y }, title, xTitle, yTitle, template
buildHistogramPlotConfigpropgroupBy (overlays per category), colourMap, bins, opacity, log, title, xTitle, template
buildBoxPlotConfigpropgroupBy, colourMap, showOutliers, log, title, yTitle, template
buildViolinPlotConfigpropgroupBy, colourMap, showBox, showMeanLine, log, title, yTitle, template
buildTernaryPlotConfigaProp, bProp, cPropcolorBy, colourMap, markerColor/Size/Opacity, title, template

Helpers exported from the same family:

js
import {
  asNumeric,             // coerce value → number | null
  collectNumericValues,  // pull numeric values out of rows, skipping null/NaN
  groupRowsBy,           // partition rows by a categorical property
  buildCategoricalColourResolver,
  NULLISH_CATEGORY,
} from 'baselode';

Analytics tool-UI components

Drop-in React wrappers — each takes rows + the same options as the config builder, mounts Plotly, and resizes responsively.

jsx
import {
  BaselodeScatterPlotToolUI,
  BaselodeHistogramPlotToolUI,
  BaselodeBoxPlotToolUI,
  BaselodeViolinPlotToolUI,
  BaselodeTernaryPlotToolUI,
} from 'baselode/tool-ui';

<BaselodeScatterPlotToolUI
  rows={assayRows}
  xProp="au_ppm"
  yProp="ag_ppm"
  colorBy="lithology"
  colourMap="lithology"
  template="baselode"
  height={520}
/>

Zod schemas for tool-call serialisation (paired with safeParseSerializableBaselode<PlotName>):

  • SerializableBaselodeScatterPlotSchema
  • SerializableBaselodeHistogramPlotSchema
  • SerializableBaselodeBoxPlotSchema
  • SerializableBaselodeViolinPlotSchema
  • SerializableBaselodeTernaryPlotSchema

drillholeViz

buildIntervalPoints(rows, property)

Convert assay rows into interval point objects for plotting.

buildPlotConfig(points, property, options?)

Build a Plotly trace/layout config for a property.

holeHasData(rows, property)

Return true if the hole has at least one non-null value for property.

Constants

NUMERIC_LINE_COLOR, NUMERIC_MARKER_COLOR, ERROR_COLOR


React component — TracePlot

jsx
import { TracePlot } from 'baselode';

<TracePlot
  rows={holeRows}           // array of row objects for one hole
  properties={['au_ppm']}   // columns to render
  height={600}              // optional height in px
/>

Renders a multi-track Plotly strip log with depth increasing downward.


React hook — useDrillholeTraceGrid

jsx
import { useDrillholeTraceGrid } from 'baselode';

const { plots } = useDrillholeTraceGrid({ holes, property });

Returns an array of plot configs for a drill-hole comparison grid.


Color scales

buildEqualRangeColorScale(values, palette?)

Build an equal-range color scale from a numeric array.

getEqualRangeBinIndex(scale, value)

Return the bin index for value in a pre-built scale.

getEqualRangeColor(scale, value)

Return the hex color for value in a pre-built scale.

ASSAY_COLOR_PALETTE_10

Default 10-colour palette array for assay visualisation — a sequential magma-style ramp from deep purple (low) to bright cream/gold (high). Used by both buildEqualRangeColorScale (legend) and the 3D scene's internal colour pipeline, so the legend and rendered geometry stay in lockstep. Pass a custom array to buildEqualRangeColorScale to override.


2D projections

projectTraceToSection(traces, origin, azimuth)

Project 3D trace points onto a vertical cross-section.

sectionWindow(traces, origin, azimuth, width)

Filter traces to points within width metres of the section plane.

planView(traces, depthSlice?, colorBy?)

Prepare traces for a plan (top-down) view.

sectionView(traces, origin, azimuth, width, colorBy?)

Prepare traces for a vertical section view.


3D payload builders

tracesAsSegments(traces, colorBy?)

Convert trace rows into segment arrays for the 3D scene.

intervalsAsTubes(intervals, options?)

Convert interval rows into tube descriptor objects for 3D rendering.

annotationsFromIntervals(intervals)

Build annotation label objects from interval rows.


Structural visualisation

buildTadpoleConfig(structuralRows, holeRows)

Build a Plotly trace config for tadpole symbols on a strip log.

buildStructuralStripConfig(structuralRows)

Build a Plotly config for a structural strip track.

buildCommentsConfig(rows, commentsColumn)

Build a Plotly config for a free-text comments track.

buildStrikeDipSymbol(dip, azimuth)

Return SVG path data for a strike/dip symbol.


Structural scene

dipAzimuthToNormal(dip, azimuth)

Convert dip/azimuth to a Three.js-compatible normal vector.

buildStructuralDiscs(structuralPoints, traces)

Build disc descriptor objects for the 3D scene.


3D Scene

Baselode3DScene

js
import { Baselode3DScene } from 'baselode';

Thin Three.js orchestrator. Rendering is delegated to internal domain modules (drillholeScene, stripLogScene, blockModelScene, structuralScene, sceneClickHandler, selectionGlow).

Lifecycle

MethodDescription
new Baselode3DScene()Construct (does not create WebGL context yet)
init(container)Attach renderer to a DOM container element and start animation loop
resize()Update camera/renderer when the container is resized
dispose()Cancel animation, remove DOM element, free all GPU resources

Drillholes

MethodDescription
setDrillholes(holes, options?)Render desurveyed hole traces as cylinders; fits camera
setDrillholeClickHandler(fn)Register ({ holeId, project }) or { type:'structure', ... } click callback

options: { preserveView, assayIntervalsByHole, selectedAssayVariable }

Strip logs (floating 3D traces)

Floating line traces rendered beside drillholes in 3D space. Each trace is a solid ribbon mesh with no background panel — the scene is fully visible through it. Traces are depth-registered: depth = 0 anchors at the collar and each sample appears at its true position along the hole axis, aligned with the drillstring geometry.

MethodDescription
setStripLogs(holes, stripLogs)Add floating line traces beside drillholes; clears previous traces first
clearStripLogs()Remove all traces and free GPU resources

setStripLogs(holes, stripLogs)holes is the same desurveyed array passed to setDrillholes. stripLogs is an array of objects:

PropertyTypeDescription
holeIdstringMust match a hole.id from the holes array
depthsnumber[]Measured downhole depths for each sample (metres from collar)
valuesnumber[]Numeric value at each depth
options.panelWidthnumberScene-unit horizontal extent of the value axis (default 20)
options.lateralOffsetnumberScene-unit offset from the hole collar perpendicular to the hole axis (default 15)
options.colorstringCSS/hex ribbon colour (default '#00bcd4')
options.valueMinnumberExplicit minimum for horizontal scaling (auto if omitted)
options.valueMaxnumberExplicit maximum for horizontal scaling (auto if omitted)

The trace orientation is derived from the collar→toe vector of each hole. The lateral offset direction is perpendicular to that vector in the horizontal plane. Depth positions are normalised against the hole's measured depth at the toe (read from point.md), so dense geophysics logs (e.g. gamma at 0.1 m intervals) render correctly alongside sparse assay intervals.

Pure helpers (exported for testing)

SymbolDescription
normalizeStripLogOptions(options?)Apply defaults to a strip log options object
getHoleVerticalExtent(points)Return { topZ, botZ, height } from desurveyed hole points
buildStripLogLinePoints(depths, values, panelWidth, panelHeight, valueMin, valueMax, depthScale?)Map depth/value arrays to panel-local THREE.Vector3 points. depthScale (hole measured depth at toe) anchors depth 0 at the collar; omit to auto-scale across the data range.
buildStripLogGroup(hole, stripLog)Build the THREE.Group for one hole/log pair
STRIP_LOG_DEFAULT_PANEL_WIDTH20
STRIP_LOG_DEFAULT_LATERAL_OFFSET15
STRIP_LOG_DEFAULT_COLOR'#00bcd4'

Block model

MethodDescription
setBlocks(data, selectedProperty, stats, options?)Render a merged exterior-face block mesh with vertex colours
setBlockOpacity(opacity)Update opacity of all rendered blocks (0–1)
setBlockClickHandler(fn)Register (blockRow) => void click callback

options: { autoCenter, opacity, offset }

Structural discs

MethodDescription
setStructuralDiscs(structures, holes, opts?)Render disc meshes oriented to dip/azimuth or alpha/beta
setStructuralDiscsVisible(visible)Show or hide all structural discs

opts: { radius, discThickness, opacity, segments, colorMap, maxDiscs }

Camera

MethodDescription
recenterCameraToOrigin(distance?)Move camera to origin
lookDown(distance?)Point camera straight down
pan(dx, dy)Screen-space pan
dolly(scale)Zoom in/out by scale factor
focusOnLastBounds(padding?)Return to last data bounds
setCameraFov(fovDeg)Set FOV while preserving apparent scale
setControlMode(mode)Switch 'orbit' / 'fly'
getViewState()Serialise current camera state
setViewState(state)Restore a saved camera state
setViewChangeHandler(fn)Register callback for camera movement (throttled 250 ms)

Selection glow

MethodDescription
selectObject(object|null)Programmatically apply/clear glow
getSelectedObject()Return currently glowing object, or null
setSelectableObjects(objects[])Override the raycast candidate list
disposeGlow()Free EffectComposer GPU resources

React component — Baselode3DControls

jsx
import { Baselode3DControls } from 'baselode';
import 'baselode/style.css';

<Baselode3DControls
  traces={segments}
  structuralDiscs={discs}
  colorBy="au_ppm"
/>

Drop-in React component wrapping Baselode3DScene with orbit controls, viewport gizmo, and a controls panel.


React component — BlockModelWidget

jsx
import { BlockModelWidget } from 'baselode';
import 'baselode/style.css';

<BlockModelWidget
  blocks={blocks}
  colorProperty="grade"
/>

Interactive 3D block model viewer.


Raster Overlays

normalizeBounds(bounds)

Normalise a bounds descriptor to the canonical { minX, minY, maxX, maxY } form.

Accepts either:

  • { minX, minY, maxX, maxY } — explicit corners
  • { x, y, width, height } — origin + size

Throws if the resulting width or height is zero or negative.


createRasterOverlay(options)

Create a raster overlay layer from an image source. Returns a Promise<layer>.

OptionTypeDefaultDescription
sourceobjectrequiredImage source (see below)
boundsobjectrequiredPlacement bounds in scene coordinates
idstringautoUnique identifier
namestringidHuman-readable display name
elevationnumber0Z position in scene units (metres)
opacitynumber1Initial opacity clamped to [0, 1]
visiblebooleantrueInitial visibility
renderOrdernumber0Three.js renderOrder for draw-order control

Source types:

source.typeExtra fieldsDescription
'url'url: stringLoad from a URL or data URI
'file'file: FileLoad from a browser File object
'texture'texture: THREE.TextureUse a pre-built Three.js texture

The returned layer object has shape { id, name, mesh, texture, bounds, elevation, opacity, visible }.


Scene-level raster functions

These functions are also available as methods on Baselode3DScene (without the sceneCtx argument):

addRasterOverlay(sceneCtx, layer)

Add a layer (from createRasterOverlay) to the scene. If a layer with the same id already exists it is removed first.

removeRasterOverlay(sceneCtx, id)

Remove a layer from the scene and dispose its geometry, material, and texture.

setRasterOverlayOpacity(sceneCtx, id, opacity)

Update opacity at runtime. Value is clamped to [0, 1].

setRasterOverlayVisibility(sceneCtx, id, visible)

Show or hide a layer without destroying it.

setRasterOverlayElevation(sceneCtx, id, elevation)

Update the Z position of a layer at runtime.

getRasterOverlay(sceneCtx, id)

Return the layer descriptor for id, or undefined if not found.

listRasterOverlays(sceneCtx)

Return all layers as an array in insertion order.

clearRasterOverlays(sceneCtx)

Remove all layers from the scene and dispose all GPU resources.


Baselode3DScene — raster overlay methods

These delegate to the functions above with sceneCtx = scene:

MethodDescription
scene.addRasterOverlay(layer)Add a layer to the scene
scene.removeRasterOverlay(id)Remove and dispose a layer
scene.setRasterOverlayOpacity(id, opacity)Update opacity (0–1)
scene.setRasterOverlayVisibility(id, visible)Show or hide
scene.setRasterOverlayElevation(id, elevation)Update Z position
scene.getRasterOverlay(id)Get layer by id
scene.listRasterOverlays()Get all layers
scene.clearRasterOverlays()Remove all layers

Camera Controls

Camera control helpers operate on a Baselode3DScene instance.

FunctionDescription
fitCameraToBounds(scene, bounds)Fit camera to a bounding box
recenterCameraToOrigin(scene)Move camera to the scene origin
lookDown(scene)Rotate camera to look straight down
pan(scene, dx, dy)Pan camera by screen-space delta
dolly(scene, delta)Zoom camera in/out
focusOnLastBounds(scene)Return camera to the last computed bounds
setControlMode(scene, mode)Switch orbit/pan/zoom control mode
setFov(scene, degrees)Set the camera field of view
buildViewSignature(scene)Serialise the current view state
getViewState(scene)Return the current view state object
setViewState(scene, state)Restore a previously saved view state
emitViewChangeIfNeeded(scene)Fire a view-change event if the camera moved

Constants: FOV_MIN_DEG, FOV_MAX_DEG