The scene graph system in OpenGeometry provides a hierarchical structure for managing collections of geometric entities. It enables organizing, manipulating, and rendering multiple BREP bodies as a cohesive scene.
Overview
The scene graph consists of two primary structures defined in src/scenegraph.rs:
- OGScene - A named collection of geometric entities
- OGSceneManager - Manages multiple scenes and provides WASM-exposed API
OGScene
A scene is a container for geometric entities:
pub struct OGScene {
pub id: String, // UUID identifier
pub name: String, // Human-readable name
pub entities: Vec<SceneEntity>,
}
SceneEntity
Each entity in a scene wraps a BREP body:
pub struct SceneEntity {
pub id: String, // Unique entity ID
pub kind: String, // Type: "OGLine", "OGCuboid", etc.
pub brep: Brep, // Underlying boundary representation
}
The kind field identifies the primitive or shape type that generated the BREP, enabling type-aware operations.
Creating Scenes
Basic Scene Creation
use opengeometry::scenegraph::OGScene;
let scene = OGScene::new("My Building");
println!("Scene ID: {}", scene.id);
Scenes are automatically assigned a UUID and start with an empty entity list.
Adding Entities
Entities are added using the upsert_entity method:
use opengeometry::scenegraph::SceneEntity;
use opengeometry::brep::Brep;
use uuid::Uuid;
let mut scene = OGScene::new("Test Scene");
let entity = SceneEntity {
id: Uuid::new_v4().to_string(),
kind: "OGCuboid".to_string(),
brep: cuboid.brep().clone(),
};
scene.upsert_entity(entity);
Upsert behavior:
- If entity ID exists → replaces existing entity
- If entity ID is new → appends to entity list
Removing Entities
let removed = scene.remove_entity("entity-uuid");
if removed {
println!("Entity removed");
}
Returns true if entity was found and removed, false otherwise.
OGSceneManager
The scene manager provides a stateful API for working with multiple scenes:
pub struct OGSceneManager {
scenes: HashMap<String, OGScene>,
current_scene_id: Option<String>,
}
Creating Scenes
let mut manager = OGSceneManager::new();
let scene_id = manager.create_scene_internal("Floor Plan");
The newly created scene automatically becomes the current scene.
Managing Active Scene
// Set current scene
manager.set_current_scene(scene_id)?;
// Get current scene ID
if let Some(id) = manager.get_current_scene_id() {
println!("Current scene: {}", id);
}
Removing Scenes
let removed = manager.remove_scene(scene_id);
If the removed scene was current, the manager automatically selects another scene as current.
Adding Entities to Scenes
The scene manager provides convenience methods for adding primitives:
From BREP
manager.add_brep_entity_to_scene_internal(
&scene_id,
"wall-1",
"CustomShape",
&brep
)?;
From Primitives
use opengeometry::primitives::cuboid::OGCuboid;
let cuboid = OGCuboid::new(1.0, 2.0, 3.0);
manager.add_cuboid_to_scene_internal(&scene_id, "box-1", &cuboid)?;
Supported primitives:
add_line_to_scene_internal
add_polyline_to_scene_internal
add_arc_to_scene_internal
add_rectangle_to_scene_internal
add_polygon_to_scene_internal
add_cuboid_to_scene_internal
add_cylinder_to_scene_internal
add_sphere_to_scene_internal
add_wedge_to_scene_internal
Each method:
- Extracts the BREP from the primitive
- Creates a SceneEntity with appropriate
kind
- Adds to the specified scene
Scene to BREP Relationship
Every entity in a scene contains a complete BREP representation:
OGScene
└─ SceneEntity ("wall-1")
├─ kind: "OGCuboid"
└─ brep: Brep { vertices, edges, faces }
└─ SceneEntity ("window-1")
├─ kind: "OGRectangle"
└─ brep: Brep { vertices, edges, faces }
This design means:
- Each entity is self-contained
- No shared vertex/edge pools between entities
- Easy to add/remove entities without topology updates
- Straightforward serialization
2D Projection
Scenes can be projected to 2D with hidden line removal:
use opengeometry::export::{CameraParameters, HlrOptions};
let camera = CameraParameters::default();
let hlr = HlrOptions::default();
let scene_2d = scene.project_to_2d(&camera, &hlr);
This projects all entities in the scene and combines them into a single 2D representation.
Via Scene Manager
let scene_2d = manager.project_scene_to_2d(
&scene_id,
&camera,
&hlr
)?;
// Or as JSON
let json = manager.project_scene_to_2d_json(&scene_id, &camera, &hlr)?;
WASM API
The OGSceneManager is exposed to JavaScript via wasm-bindgen:
import { OGSceneManager } from 'opengeometry';
const manager = new OGSceneManager();
// Create scene
const sceneId = manager.createScene('My Scene');
// Add entity
manager.addCuboidToScene(sceneId, 'box-1', cuboid);
// Project to 2D
const cameraJson = JSON.stringify({ /* camera params */ });
const projection = manager.projectTo2DCamera(sceneId, cameraJson);
WASM Methods
Scene Management:
createScene(name: string): string
removeScene(sceneId: string): boolean
setCurrentScene(sceneId: string)
getCurrentSceneId(): string | undefined
listScenes(): string (JSON array)
Entity Management:
addBrepEntityToScene(sceneId, entityId, kind, brepJson)
addLineToScene(sceneId, entityId, line)
addCuboidToScene(sceneId, entityId, cuboid)
- … (one method per primitive type)
removeEntityFromScene(sceneId, entityId): boolean
Projection:
projectTo2DCamera(sceneId, cameraJson, hlrJson): string
projectTo2DLines(sceneId, cameraJson, hlrJson): string
Current Scene Shortcuts
Many methods have variants that operate on the current scene:
manager.addCuboidToCurrentScene('box-1', cuboid);
manager.projectCurrentTo2DCamera(cameraJson);
Scene Serialization
Scenes and entities are fully serializable:
// Serialize scene
let json = serde_json::to_string(&scene)?;
// Via manager (WASM)
let json = manager.get_scene_serialized(scene_id)?;
Serialized format includes:
- Scene ID and name
- All entities with their IDs, kinds, and complete BREP data
Example: Building a Multi-Entity Scene
use opengeometry::scenegraph::OGSceneManager;
use opengeometry::primitives::cuboid::OGCuboid;
use opengeometry::primitives::cylinder::OGCylinder;
let mut manager = OGSceneManager::new();
let scene_id = manager.create_scene_internal("Building");
// Add foundation
let foundation = OGCuboid::new(10.0, 10.0, 0.5);
manager.add_cuboid_to_scene_internal(&scene_id, "foundation", &foundation)?;
// Add columns
for i in 0..4 {
let column = OGCylinder::new(0.3, 3.0, 32);
let id = format!("column-{}", i);
manager.add_cylinder_to_scene_internal(&scene_id, &id, &column)?;
}
println!("Scene has {} entities", manager.scenes[&scene_id].entities.len());
- Entity lookup: O(n) search by ID (consider HashMap for large scenes)
- BREP cloning: Each add operation clones the BREP
- Projection: All entities processed together, efficient for batch operations
- Serialization: Full scene serialization can be expensive for large models
Next Steps
Last modified on March 7, 2026