
Thatopen Impl Highlighting
- 5 installs
- 17 repo stars
- Updated July 8, 2026
- openaec-foundation/thatopen-claude-skill-package
Helps with ai & agent building tasks.
About
thatopen-impl-highlighting is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- thatopen-impl-highlighting
- AI & Agent Building
- AI-coding skill
Thatopen Impl Highlighting by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/thatopen-claude-skill-package --skill thatopen-impl-highlightingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 17 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/thatopen-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
ThatOpen Highlighting & Selection
Overview
This skill covers element highlighting, selection, hover effects, outline rendering, and fragment-to-mesh conversion in a ThatOpen BIM viewer. The five components covered are:
| Component | Package | Purpose |
|---|---|---|
| Highlighter | @thatopen/components-front | Click-based element selection with color styles |
| Hoverer | @thatopen/components-front | Fade animation on hover |
| Outliner | @thatopen/components-front | Post-processing outline effects |
| Mesher | @thatopen/components-front | Convert fragment selections to THREE.Mesh |
| FastModelPicker | @thatopen/components | GPU-based element picking |
Version: @thatopen/components-front 3.3.x Prerequisites: thatopen-impl-viewer (world setup, PostproductionRenderer)
MaterialDefinition Type
The core type for highlight styles:
type MaterialDefinition = {
color: THREE.Color;
opacity: number;
};All Highlighter styles are either a MaterialDefinition object or null (meaning transparent/no-color selection tracking only).
Highlighter
Setup
ALWAYS call setup() before using any Highlighter functionality. Without setup(), the Highlighter has no world reference and all operations fail silently or throw.
import * as OBCF from "@thatopen/components-front";
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });HighlighterConfig
| Property | Type | Default | Description |
|---|---|---|---|
world | World | required | World to pick elements from |
selectName | string | "select" | Default selection style name |
selectionColor | THREE.Color | #BCF124 | Default selection color |
autoHighlightOnClick | boolean | true | Auto-highlight clicked elements |
selectEnabled | boolean | true | Enable/disable selection globally |
autoUpdateFragments | boolean | true | Auto-update fragment visuals |
selectMaterialDefinition | MaterialDefinition | yellow | Default style material |
highlighter.setup({
world,
selectName: "select",
selectionColor: new THREE.Color("#BCF124"),
autoHighlightOnClick: true,
});Styles
Styles define how highlighted elements appear. Each style has a name and a MaterialDefinition (or null for invisible tracking).
// Add a custom highlight style
highlighter.styles.set("warning", {
color: new THREE.Color("#FF0000"),
opacity: 0.5,
});
// Null style — tracks selection without visual change
highlighter.styles.set("invisible", null);- ALWAYS define styles AFTER calling
setup(). - The default
"select"style is created automatically bysetup(). - Style names are arbitrary strings. Use descriptive names.
Highlight (Click Selection)
// Highlight whatever the mouse is pointing at with the "select" style
await highlighter.highlight("select");
// Highlight without removing previous selection
await highlighter.highlight("select", false);
// Highlight and zoom to selection
await highlighter.highlight("select", true, true);
// Highlight with exclusion list
const exclude: ModelIdMap = new Map();
await highlighter.highlight("select", true, false, exclude);Method signature:
highlight(
name: string,
removePrevious?: boolean, // default: true
zoomToSelection?: boolean, // default: false
exclude?: ModelIdMap // elements to skip
): Promise<void>;HighlightByID (Programmatic Selection)
Select specific elements by their model/element IDs without requiring a mouse event:
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId1, elementId2]));
await highlighter.highlightByID("select", items);
// Keep previous selection
await highlighter.highlightByID("select", items, false);
// Highlight and zoom
await highlighter.highlightByID("select", items, true, true);Method signature:
highlightByID(
name: string,
modelIdMap: ModelIdMap,
removePrevious?: boolean, // default: true
zoomToSelection?: boolean, // default: false
exclude?: ModelIdMap
): Promise<void>;Reading the Selection
The selection property holds the current state for every style:
// Get current "select" selection
const selected: ModelIdMap = highlighter.selection["select"];
// Iterate selected elements
for (const [modelId, elementIds] of selected) {
console.log(`Model ${modelId}: ${elementIds.size} elements selected`);
}Clear Selection
// Clear a specific style
await highlighter.clear("select");
// Clear all styles
await highlighter.clear();
// Clear with a filter (keep specific elements)
await highlighter.clear("select", filterModelIdMap);Multi-Select
The multiple property controls multi-selection behavior:
// No multi-select (default) — each click replaces previous selection
highlighter.multiple = "none";
// Multi-select with Shift key
highlighter.multiple = "shiftKey";
// Multi-select with Ctrl key
highlighter.multiple = "ctrlKey";- When
multipleis"shiftKey"or"ctrlKey", holding the specified
modifier key while clicking adds to the selection instead of replacing it.
- ALWAYS set
multipleAFTER callingsetup().
Zoom to Selection
// Enable zoom-to-selection globally
highlighter.zoomToSelection = true;
// Or per-call
await highlighter.highlight("select", true, true);When zoomToSelection is true, the camera frames the highlighted elements after each highlight operation.
AutoToggle
Styles listed in autoToggle automatically deselect when the same element is clicked again (toggle behavior):
highlighter.autoToggle.add("select");
// First click on element A → selects A
// Second click on element A → deselects A- ALWAYS add style names to
autoToggleAFTER callingsetup(). - AutoToggle works per-element, not per-click. Clicking a different element
still replaces the selection (unless multiple is also set).
Selectable (Per-Style Filtering)
Restrict which elements can be highlighted for a given style:
const allowedElements: ModelIdMap = new Map();
allowedElements.set(modelId, new Set([101, 102, 103]));
highlighter.selectable["select"] = allowedElements;- When
selectableis set for a style, ONLY elements in that ModelIdMap
can be highlighted with that style. All other elements are ignored.
- If
selectableis not set for a style, all elements are selectable.
Update Colors
After changing a style's MaterialDefinition, call updateColors() to apply the visual change immediately:
highlighter.styles.set("select", {
color: new THREE.Color("#0000FF"),
opacity: 0.8,
});
await highlighter.updateColors();Hoverer
Hoverer provides a fade animation effect when the mouse hovers over elements. It is a separate component from Highlighter.
const hoverer = components.get(OBCF.Hoverer);Properties
| Property | Type | Default | Description |
|---|---|---|---|
duration | number | — | Fade animation duration (ms) |
delay | number | — | Delay before hover triggers (ms) |
animation | string | — | Animation type |
Events
hoverer.onHoverStarted.add((data) => {
console.log("Hover started:", data);
});
hoverer.onHoverEnded.add(() => {
console.log("Hover ended");
});- Hoverer works independently of Highlighter. You can use both
simultaneously: Hoverer for visual feedback, Highlighter for selection.
- NEVER rely on Hoverer for selection state. Use Highlighter for that.
Outliner
Outliner renders post-processing outlines around highlighted elements. It requires PostproductionRenderer (not SimpleRenderer).
Setup
import * as OBCF from "@thatopen/components-front";
// MUST use PostproductionRenderer — Outliner has no effect with SimpleRenderer
const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.PostproductionRenderer
>();
// ... world setup ...
world.renderer.postproduction.enabled = true;
const outliner = components.get(OBCF.Outliner);Styles
Outliner styles reference Highlighter style names. When elements are highlighted with a style that the Outliner tracks, outlines appear.
// Outliner tracks Highlighter style names
outliner.styles.add("select");Outline Appearance
| Property | Type | Default | Description |
|---|---|---|---|
color | THREE.Color | — | Outline stroke color |
thickness | number | — | Outline stroke width (px) |
fillColor | THREE.Color | — | Interior fill color |
fillOpacity | number | — | Interior fill opacity (0-1) |
outliner.color = new THREE.Color("#FF6600");
outliner.thickness = 2;
outliner.fillColor = new THREE.Color("#FF6600");
outliner.fillOpacity = 0.1;Manual Item Management
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId]));
outliner.addItems(items);
outliner.removeItems(items);
outliner.clean(); // Remove all outlined itemsMesher
Mesher converts fragment selections into standard THREE.Mesh objects. This is useful for custom rendering, export, or manipulation outside the fragment system.
const mesher = components.get(OBCF.Mesher);
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId1, elementId2]));
const meshes: THREE.Mesh[] = mesher.get(items);Method Signature
get(modelIdMap: ModelIdMap, config?: MesherConfig): THREE.Mesh[];- Returns standard
THREE.Meshobjects with geometry and material. - The returned meshes are NOT managed by the fragment system. You MUST
manually add them to a scene and dispose them when done.
- Useful for: custom shaders, physics engines, export to glTF, snapshots.
FastModelPicker
FastModelPicker provides GPU-based element picking as an alternative to raycasting. It renders element IDs to an off-screen buffer and reads back the pixel under the cursor. This is significantly faster than raycasting for large models.
const picker = components.get(OBC.FastModelPicker);- FastModelPicker is in
@thatopen/components(not components-front). - It is new in v3 and replaces manual raycasting for performance-critical
selection scenarios.
- For most use cases, the built-in raycasting via Highlighter is sufficient.
Use FastModelPicker only when raycasting becomes a bottleneck with very large models (100k+ elements).
Complete Click-to-Select Workflow
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
// 1. Viewer setup (see thatopen-impl-viewer)
const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.PostproductionRenderer
>();
world.scene = new OBC.SimpleScene(components);
world.scene.setup();
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);
world.renderer.postproduction.enabled = true;
// 2. Setup Highlighter — MUST call setup() before any other operation
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// 3. Configure multi-select
highlighter.multiple = "shiftKey";
// 4. Enable zoom-to-selection
highlighter.zoomToSelection = true;
// 5. Add custom styles
highlighter.styles.set("warning", {
color: new THREE.Color("#FF0000"),
opacity: 0.5,
});
// 6. Setup Outliner for post-processing outlines
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");
outliner.color = new THREE.Color("#BCF124");
outliner.thickness = 2;
// 7. Setup Hoverer for hover feedback
const hoverer = components.get(OBCF.Hoverer);
// 8. Start render loop
components.init();
// 9. Load a model (see thatopen-syntax-ifc-loading)
// ... model loading code ...
// 10. Programmatic selection
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId]));
await highlighter.highlightByID("select", items);
// 11. Clear selection
await highlighter.clear("select");Critical Rules
1. ALWAYS call highlighter.setup({ world }) before using any Highlighter method. Without it, highlight operations fail. 2. ALWAYS use PostproductionRenderer when using Outliner. Outlines are a post-processing effect and have no effect with SimpleRenderer. 3. ALWAYS enable postproduction (world.renderer.postproduction.enabled = true) before Outliner will render. 4. ALWAYS dispose meshes returned by Mesher.get() manually. They are not tracked by the fragment system. 5. NEVER set multiple, autoToggle, or selectable before calling setup(). The Highlighter is not initialized until setup() completes. 6. NEVER assume Hoverer and Highlighter share state. They are independent components with separate tracking. 7. NEVER use FastModelPicker without measuring whether raycasting is actually a bottleneck. The built-in raycasting is sufficient for most models.
Reference Files
- references/methods.md — Highlighter, Hoverer,
Outliner, Mesher, FastModelPicker API signatures
- references/examples.md — Click select, hover,
outline, multi-select, custom styles
- references/anti-patterns.md — Missing
setup, style conflicts, performance issues
Source Verification
All API signatures verified against:
- GitHub:
ThatOpen/engine_componentsmain branch - npm:
@thatopen/components-front@3.3.3 - Research:
docs/research/vooronderzoek-thatopen.md(Sections 4.2-4.5) - Skill:
thatopen-impl-viewer(PostproductionRenderer context)
Highlighting Anti-Patterns
Missing setup() Call
WRONG — Using Highlighter without calling setup():
const highlighter = components.get(OBCF.Highlighter);
// Missing: highlighter.setup({ world });
await highlighter.highlight("select"); // Fails — no world referenceCORRECT — ALWAYS call setup() first:
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
await highlighter.highlight("select");setup() initializes the world reference, creates the default style, and binds click event listeners. Without it, all operations fail silently or throw.
---
Configuring Before setup()
WRONG — Setting properties before initialization:
const highlighter = components.get(OBCF.Highlighter);
highlighter.multiple = "shiftKey"; // Too early
highlighter.autoToggle.add("select"); // Too early
highlighter.setup({ world });CORRECT — Configure AFTER setup():
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
highlighter.multiple = "shiftKey";
highlighter.autoToggle.add("select");---
Outliner Without PostproductionRenderer
WRONG — Using Outliner with SimpleRenderer:
world.renderer = new OBC.SimpleRenderer(components, container);
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");
// Outlines never render — SimpleRenderer has no post-processing pipelineCORRECT — ALWAYS use PostproductionRenderer with Outliner:
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.renderer.postproduction.enabled = true;
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");---
Outliner Without Enabling Postproduction
WRONG — PostproductionRenderer but postproduction not enabled:
world.renderer = new OBCF.PostproductionRenderer(components, container);
// Missing: world.renderer.postproduction.enabled = true;
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");
// Outlines do not render — postproduction pipeline is disabledCORRECT:
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.renderer.postproduction.enabled = true;
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");---
Not Disposing Mesher Output
WRONG — Leaking meshes from Mesher:
const meshes = mesher.get(items);
world.scene.three.add(...meshes);
// Later: meshes are never disposed → memory leakCORRECT — ALWAYS dispose Mesher output manually:
const meshes = mesher.get(items);
world.scene.three.add(...meshes);
// When done:
for (const mesh of meshes) {
mesh.geometry.dispose();
if (Array.isArray(mesh.material)) {
mesh.material.forEach((m) => m.dispose());
} else {
mesh.material.dispose();
}
world.scene.three.remove(mesh);
}Mesher returns standard THREE.Mesh objects that are NOT tracked by the fragment system. The caller owns their lifecycle.
---
Style Name Mismatch Between Highlighter and Outliner
WRONG — Outliner tracks a style name that does not exist in Highlighter:
highlighter.styles.set("selected", { color, opacity: 0.5 });
outliner.styles.add("select"); // "select" ≠ "selected"
// Outlines never appear because the style name does not matchCORRECT — Use the exact same style name:
highlighter.styles.set("select", { color, opacity: 0.5 });
outliner.styles.add("select");---
Confusing Hoverer and Highlighter
WRONG — Using Hoverer for selection state:
hoverer.onHoverStarted.add((data) => {
// Treating hover as selection — wrong
selectedElements = data;
updatePropertiesPanel(selectedElements);
});CORRECT — Use Highlighter for selection, Hoverer for visual feedback:
// Hoverer: visual feedback only
hoverer.onHoverStarted.add(() => {
container.style.cursor = "pointer";
});
// Highlighter: actual selection state
await highlighter.highlight("select");
const selected = highlighter.selection["select"];
updatePropertiesPanel(selected);---
Using FastModelPicker Unnecessarily
WRONG — Using FastModelPicker for a small model:
// Model has 500 elements — raycasting is fast enough
const picker = components.get(OBC.FastModelPicker);
// Unnecessary complexity and GPU overheadCORRECT — Use the default Highlighter raycasting for most models:
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Built-in raycasting handles models up to ~100k elements efficientlyOnly switch to FastModelPicker when profiling shows raycasting is a bottleneck with very large models (100k+ elements).
---
Performance: Too Many Highlight Styles
WRONG — Creating hundreds of individual styles:
// One style per element — extremely inefficient
for (const elementId of allElements) {
highlighter.styles.set(`style-${elementId}`, {
color: getColorForElement(elementId),
opacity: 0.5,
});
}CORRECT — Group elements by color into shared styles:
// Group by color — efficient
const colorGroups = groupElementsByColor(allElements);
for (const [color, elements] of colorGroups) {
const styleName = `group-${color}`;
highlighter.styles.set(styleName, {
color: new THREE.Color(color),
opacity: 0.5,
});
await highlighter.highlightByID(styleName, elements, false);
}Each style triggers a separate render pass. Minimize the number of active styles for better performance.
---
Forgetting to Clear Previous Highlights
WRONG — Accumulating highlights without clearing:
// Every click adds more highlights, never clearing
container.addEventListener("click", async () => {
await highlighter.highlight("select", false); // removePrevious = false
});
// Selection grows unbounded, eventually consuming significant memoryCORRECT — Clear when appropriate:
// Default behavior: removePrevious = true (clears before highlighting)
container.addEventListener("click", async () => {
await highlighter.highlight("select"); // removePrevious defaults to true
});
// Or explicitly manage clearing:
async function resetAndHighlight(items: ModelIdMap) {
await highlighter.clear("select");
await highlighter.highlightByID("select", items);
}Highlighting Examples
Basic Click-to-Select
Minimal setup for click selection with default yellow highlight:
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
// Assumes world is already set up (see thatopen-impl-viewer)
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// That's it — clicking elements now highlights them in yellow (#BCF124)Custom Selection Color
import * as THREE from "three";
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({
world,
selectionColor: new THREE.Color("#3498db"), // Blue selection
});Multi-Select with Shift Key
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Hold Shift to add to selection
highlighter.multiple = "shiftKey";
// Or use Ctrl
// highlighter.multiple = "ctrlKey";Toggle Selection (Click to Select/Deselect)
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Clicking the same element again deselects it
highlighter.autoToggle.add("select");Multiple Highlight Styles
import * as THREE from "three";
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Warning style — semi-transparent red
highlighter.styles.set("warning", {
color: new THREE.Color("#FF0000"),
opacity: 0.5,
});
// Info style — semi-transparent blue
highlighter.styles.set("info", {
color: new THREE.Color("#2196F3"),
opacity: 0.3,
});
// Invisible tracking style — no visual change
highlighter.styles.set("tracked", null);
// Apply styles programmatically
const warningItems: ModelIdMap = new Map();
warningItems.set(modelId, new Set([101, 102]));
await highlighter.highlightByID("warning", warningItems);
const infoItems: ModelIdMap = new Map();
infoItems.set(modelId, new Set([201, 202, 203]));
await highlighter.highlightByID("info", infoItems);Zoom to Selection
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Global: every highlight zooms the camera
highlighter.zoomToSelection = true;
// Or per-call:
await highlighter.highlight("select", true, true);Programmatic Selection by Element IDs
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Build a ModelIdMap
const items: ModelIdMap = new Map();
items.set("model-uuid-abc123", new Set([5001, 5002, 5003]));
// Highlight without removing previous selection
await highlighter.highlightByID("select", items, false);
// Read the current selection
const selected = highlighter.selection["select"];
for (const [modelId, elementIds] of selected) {
console.log(`Model ${modelId}: elements`, [...elementIds]);
}Restrict Selectable Elements
Only allow specific elements to be selected:
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Only walls and slabs can be selected
const selectableItems: ModelIdMap = new Map();
selectableItems.set(modelId, new Set([wallId1, wallId2, slabId1]));
highlighter.selectable["select"] = selectableItems;
// Clicking on doors, windows, etc. will have no effectHover Effect with Hoverer
const hoverer = components.get(OBCF.Hoverer);
hoverer.onHoverStarted.add((data) => {
// Update UI with hovered element info
statusBar.textContent = `Hovering: ${data}`;
});
hoverer.onHoverEnded.add(() => {
statusBar.textContent = "";
});Post-Processing Outlines with Outliner
Requires PostproductionRenderer:
import * as THREE from "three";
import * as OBCF from "@thatopen/components-front";
// World MUST use PostproductionRenderer
world.renderer.postproduction.enabled = true;
// Setup Highlighter first
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
// Setup Outliner to track the "select" style
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");
// Configure outline appearance
outliner.color = new THREE.Color("#FF6600");
outliner.thickness = 3;
outliner.fillColor = new THREE.Color("#FF6600");
outliner.fillOpacity = 0.05;
// Now when elements are highlighted with "select" style,
// they also get a post-processing outlineManual Outline Management
const outliner = components.get(OBCF.Outliner);
// Add outlines to specific elements
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId]));
outliner.addItems(items);
// Remove outlines from specific elements
outliner.removeItems(items);
// Remove all outlines
outliner.clean();Convert Selection to THREE.Mesh (Mesher)
const mesher = components.get(OBCF.Mesher);
// Get selected elements as standard meshes
const items: ModelIdMap = new Map();
items.set(modelId, new Set([elementId1, elementId2]));
const meshes = mesher.get(items);
// Add to scene for custom rendering
for (const mesh of meshes) {
world.scene.three.add(mesh);
}
// Clean up when done — YOU must dispose manually
for (const mesh of meshes) {
mesh.geometry.dispose();
if (Array.isArray(mesh.material)) {
mesh.material.forEach((m) => m.dispose());
} else {
mesh.material.dispose();
}
world.scene.three.remove(mesh);
}Full Workflow: Highlight + Outline + Hover
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
// Assumes PostproductionRenderer world is set up
world.renderer.postproduction.enabled = true;
// 1. Highlighter with multi-select
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
highlighter.multiple = "shiftKey";
highlighter.zoomToSelection = true;
highlighter.autoToggle.add("select");
// 2. Custom warning style
highlighter.styles.set("clash", {
color: new THREE.Color("#FF0000"),
opacity: 0.7,
});
// 3. Outliner for both styles
const outliner = components.get(OBCF.Outliner);
outliner.styles.add("select");
outliner.styles.add("clash");
outliner.color = new THREE.Color("#FFFFFF");
outliner.thickness = 2;
// 4. Hoverer for feedback
const hoverer = components.get(OBCF.Hoverer);
hoverer.onHoverStarted.add(() => {
container.style.cursor = "pointer";
});
hoverer.onHoverEnded.add(() => {
container.style.cursor = "default";
});
// Now: Shift+click to multi-select with outlines,
// hover shows cursor change, clash style available for
// programmatic highlighting of clash resultsHighlighting API Reference
Types
MaterialDefinition
type MaterialDefinition = {
color: THREE.Color;
opacity: number;
};ModelIdMap
type ModelIdMap = Map<string, Set<number>>;
// Maps model UUID → set of local element IDsHighlighterConfig
interface HighlighterConfig {
world: World;
selectName: string; // default: "select"
selectionColor: THREE.Color; // default: #BCF124
autoHighlightOnClick: boolean; // default: true
selectEnabled: boolean; // default: true
autoUpdateFragments: boolean; // default: true
selectMaterialDefinition: MaterialDefinition; // default: yellow
}---
Highlighter
Package: @thatopen/components-front Implements: Component, Disposable, Eventable
Properties
| Property | Type | Description |
|---|---|---|
multiple | `"none" \ | "shiftKey" \ |
zoomToSelection | boolean | Auto-zoom camera to selection |
selection | { [styleName: string]: ModelIdMap } | Current selections per style |
styles | `DataMap<string, MaterialDefinition \ | null>` |
autoToggle | Set<string> | Style names that toggle on re-click |
selectable | { [name: string]: ModelIdMap } | Per-style selectable element filter |
Methods
setup(config?)
Initializes the Highlighter for a specific world. MUST be called before any other method.
setup(config?: Partial<HighlighterConfig>): void- Creates the default
"select"style fromselectionColor/
selectMaterialDefinition.
- Binds click event listener to the world's renderer container.
- Sets
autoHighlightOnClickbehavior.
highlight(name, removePrevious?, zoomToSelection?, exclude?)
Highlights the element currently under the cursor using the specified style.
highlight(
name: string,
removePrevious?: boolean, // default: true
zoomToSelection?: boolean, // default: false
exclude?: ModelIdMap
): Promise<void>- Uses raycasting from the world's camera to determine which element is
under the cursor.
- When
removePreviousistrue, clears the style's current selection
before highlighting.
- When
zoomToSelectionistrue, frames the camera on the selected
elements.
excludeprevents specific elements from being highlighted.
highlightByID(name, modelIdMap, removePrevious?, zoomToSelection?, exclude?)
Highlights specific elements by their IDs without requiring a mouse event.
highlightByID(
name: string,
modelIdMap: ModelIdMap,
removePrevious?: boolean, // default: true
zoomToSelection?: boolean, // default: false
exclude?: ModelIdMap
): Promise<void>clear(name?, filter?)
Removes highlighting from elements.
clear(name?: string, filter?: ModelIdMap): Promise<void>- Without arguments: clears ALL styles.
- With
name: clears only the specified style. - With
filter: removes only the specified elements from the style.
updateColors()
Refreshes the visual appearance of all highlighted elements. Call after modifying a style's MaterialDefinition.
updateColors(): Promise<void>---
Hoverer
Package: @thatopen/components-front
Properties
| Property | Type | Description |
|---|---|---|
duration | number | Fade animation duration in milliseconds |
delay | number | Delay before hover effect triggers (ms) |
animation | string | Animation type identifier |
Events
| Event | Payload | Description |
|---|---|---|
onHoverStarted | hover data | Fires when cursor enters an element |
onHoverEnded | void | Fires when cursor leaves an element |
---
Outliner
Package: @thatopen/components-front Requires: PostproductionRenderer with postproduction.enabled = true
Properties
| Property | Type | Description |
|---|---|---|
styles | DataSet<string> | Highlighter style names to outline |
color | THREE.Color | Outline stroke color |
thickness | number | Outline stroke width in pixels |
fillColor | THREE.Color | Interior fill color |
fillOpacity | number | Interior fill opacity (0-1) |
Methods
addItems(modelIdMap)
Manually add elements to outline rendering.
addItems(modelIdMap: ModelIdMap): voidremoveItems(modelIdMap)
Remove specific elements from outline rendering.
removeItems(modelIdMap: ModelIdMap): voidclean()
Remove all items from outline rendering.
clean(): void---
Mesher
Package: @thatopen/components-front
Methods
get(modelIdMap, config?)
Converts fragment elements into standard THREE.Mesh objects.
get(modelIdMap: ModelIdMap, config?: MesherConfig): THREE.Mesh[]- Returns an array of
THREE.Meshobjects with geometry and material. - Returned meshes are NOT managed by the fragment system.
- Caller is responsible for adding meshes to a scene and disposing them.
---
FastModelPicker
Package: @thatopen/components
GPU-based element picking alternative to raycasting. Renders element IDs to an off-screen buffer and reads the pixel under the cursor.
- New in v3.
- Significantly faster than raycasting for models with 100k+ elements.
- For standard use cases, the Highlighter's built-in raycasting is
sufficient and simpler.