Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/docs/geometry-rules.md is written in an unsupported language. File is not indexed.

0001 # Geometry Editing Rules
0002 
0003 Firebird uses a powerful rule-based system to customize detector geometry appearance.
0004 Rules control colors, materials, merging behavior, and outlines for different detector components.
0005 
0006 ## Overview
0007 
0008 When geometry is loaded from ROOT files, it consists of a hierarchical tree of Three.js `Object3D` nodes
0009 (Groups and Meshes). The geometry editing system allows you to:
0010 
0011 - **Match nodes** by name patterns (glob-style wildcards)
0012 - **Apply styling** (colors, materials, transparency)
0013 - **Merge geometries** for better performance
0014 - **Create outlines** for visual clarity
0015 - **Process hierarchically** ("the rest" rules for unmatched nodes)
0016 
0017 ## Rule Structure
0018 
0019 Rules are organized in **RuleSets**, where each ruleset targets specific detectors:
0020 
0021 ```typescript
0022 {
0023   name: "DIRC*",           // Match detector by name (glob pattern)
0024   // or
0025   names: ["DIRC*", "RICH*"],  // Match multiple detectors
0026 
0027   rules: [
0028     // Array of rules applied in order
0029   ]
0030 }
0031 ```
0032 
0033 ### Basic Rule Properties
0034 
0035 ```typescript
0036 interface EditThreeNodeRule {
0037   // Node selection
0038   patterns?: string[] | string;  // Glob patterns to match nodes
0039 
0040   // Geometry handling
0041   merge?: boolean;               // Merge matched meshes (default: true)
0042   newName?: string;              // Name for merged mesh
0043   deleteOrigins?: boolean;       // Remove original meshes after merge (default: true)
0044   cleanupNodes?: boolean;        // Remove empty groups (default: true)
0045 
0046   // Styling
0047   color?: ColorRepresentation;   // Hex color (e.g., 0xFF0000)
0048   material?: Material;           // Three.js material
0049 
0050   // Outlines
0051   outline?: boolean;             // Create edge outlines (default: true)
0052   outlineThresholdAngle?: number; // Edge detection angle in degrees (default: 40)
0053   outlineColor?: ColorRepresentation;
0054 
0055   // Advanced
0056   applyToDescendants?: boolean;  // Apply to children of matched nodes
0057   simplifyMeshes?: boolean;      // Reduce vertex count
0058   simplifyRatio?: number;        // Simplification ratio (default: 0.7)
0059 }
0060 ```
0061 
0062 ## Pattern Matching
0063 
0064 Patterns use glob-style matching (powered by [outmatch](https://github.com/axtgr/outmatch)):
0065 
0066 | Pattern | Matches |
0067 |---------|---------|
0068 | `*` | Any single path segment |
0069 | `**` | Any number of path segments |
0070 | `**/name*` | Any node whose name starts with "name" |
0071 | `**/v_upstream*` | Nodes like `v_upstream_coating`, `v_upstream_wall` |
0072 | `**/*box*` | Any node containing "box" in its name |
0073 
0074 ### Path Structure
0075 
0076 Node paths are built from the hierarchy:
0077 ```
0078 BeamPipe_assembly/v_upstream_coating/Left
0079                  ↑                  ↑
0080             parent name        child name
0081 ```
0082 
0083 The pattern `**/v_upstream*` matches `v_upstream_coating` but NOT `Left`.
0084 To match descendants, use `applyToDescendants: true` (default when `merge: false`).
0085 
0086 ## Rule Processing Order
0087 
0088 Rules are processed **in order**. Earlier rules take precedence:
0089 
0090 ```typescript
0091 rules: [
0092   // Rule 1: Process specific patterns first
0093   {
0094     patterns: ["**/mirror*"],
0095     color: SILVER,
0096     merge: true
0097   },
0098   // Rule 2: "The rest" - no pattern means all remaining nodes
0099   {
0100     color: GREEN_100,
0101     merge: false,
0102     outline: true
0103   }
0104 ]
0105 ```
0106 
0107 ### Skip Flags and Hierarchical Processing
0108 
0109 When a node is processed, it's marked with a skip flag. Subsequent rules respect this:
0110 
0111 1. **Individual skip**: Processed meshes are skipped by later rules
0112 2. **Hierarchical skip**: If a parent is processed, descendants are skipped by "the rest" rules
0113 3. **Outlines**: Created outlines are automatically marked to prevent "outline of outline"
0114 
0115 ## Working with Nested Nodes
0116 
0117 Understanding how rules interact with hierarchical geometry is crucial for effective styling.
0118 
0119 ### Geometry Tree Structure
0120 
0121 Detector geometry forms a tree of `Object3D` nodes:
0122 
0123 ```
0124 BeamPipe_assembly (Group)
0125 ├── v_upstream_coating (Group)
0126 │   ├── Left (Mesh)         ← has geometry, can be styled
0127 │   └── Right (Mesh)        ← has geometry, can be styled
0128 ├── v_downstream_section (Mesh)
0129 └── center_pipe (Mesh)
0130 ```
0131 
0132 **Key distinction:**
0133 - **Groups** - containers without geometry, used for organization
0134 - **Meshes** - have actual geometry that gets rendered
0135 
0136 ### Pattern Matching Depth
0137 
0138 Patterns match based on the **full path** from root:
0139 
0140 ```typescript
0141 // Given this structure:
0142 // BeamPipe_assembly/v_upstream_coating/Left
0143 
0144 // Pattern "**/v_upstream*" matches:
0145 "BeamPipe_assembly/v_upstream_coating"  ✓  (name starts with v_upstream)
0146 
0147 // But does NOT match:
0148 "BeamPipe_assembly/v_upstream_coating/Left"  ✗  (name is "Left", not "v_upstream*")
0149 ```
0150 
0151 ### The `applyToDescendants` Option
0152 
0153 When a pattern matches a **Group** (not a Mesh), you need to decide what happens to its children:
0154 
0155 ```typescript
0156 {
0157   patterns: ["**/v_upstream_coating"],
0158   color: 0xFF0000,
0159   merge: false,
0160   applyToDescendants: true  // ← Default when merge=false
0161 }
0162 ```
0163 
0164 | `applyToDescendants` | Behavior |
0165 |---------------------|----------|
0166 | `true` (default) | Pattern matches Group → all descendant Meshes get styled |
0167 | `false` | Pattern matches Group → nothing styled (Groups have no geometry) |
0168 
0169 **Example - styling a branch:**
0170 
0171 ```typescript
0172 // Style v_upstream_coating AND all its children (Left, Right)
0173 {
0174   patterns: ["**/v_upstream_coating"],
0175   color: INDIGO_200,
0176   merge: false
0177   // applyToDescendants: true is the default
0178 }
0179 
0180 // Result:
0181 // v_upstream_coating (Group) - marked as processed
0182 // ├── Left (Mesh) - colored INDIGO_200
0183 // └── Right (Mesh) - colored INDIGO_200
0184 ```
0185 
0186 ### "The Rest" Rules and Hierarchical Skip
0187 
0188 A rule **without patterns** is called a "the rest" rule - it processes everything not yet handled:
0189 
0190 ```typescript
0191 rules: [
0192   // Rule 1: Specific pattern
0193   { patterns: ["**/v_upstream*"], color: RED, merge: false },
0194 
0195   // Rule 2: "The rest" - no patterns
0196   { color: GREEN, merge: false }
0197 ]
0198 ```
0199 
0200 **Hierarchical skip** ensures that once a branch is processed, "the rest" rules skip the entire branch:
0201 
0202 ```
0203 Processing flow:
0204 
0205 1. Rule 1 matches "v_upstream_coating"
0206    → Marks v_upstream_coating as processed
0207    → Styles Left and Right (applyToDescendants=true)
0208    → Marks Left and Right as processed
0209 
0210 2. Rule 2 ("the rest") runs
0211    → Checks each mesh: "Is it in a processed branch?"
0212    → Left: parent v_upstream_coating is processed → SKIP
0213    → Right: parent v_upstream_coating is processed → SKIP
0214    → center_pipe: not in processed branch → style GREEN
0215    → v_downstream_section: not in processed branch → style GREEN
0216 ```
0217 
0218 ### Common Pitfalls
0219 
0220 #### Pitfall 1: Pattern doesn't match children
0221 
0222 ```typescript
0223 // WRONG: Only matches v_upstream_coating, not its children
0224 { patterns: ["**/v_upstream_coating"], merge: true }
0225 // Result: Tries to merge just the Group (no geometry!)
0226 
0227 // CORRECT: Match the Group with applyToDescendants
0228 { patterns: ["**/v_upstream_coating"], merge: false, applyToDescendants: true }
0229 // Result: Styles all children of v_upstream_coating
0230 ```
0231 
0232 #### Pitfall 2: Children processed twice
0233 
0234 ```typescript
0235 // PROBLEMATIC:
0236 rules: [
0237   { patterns: ["**/v_upstream*"], color: RED },      // Matches v_upstream_coating
0238   { patterns: ["**/Left", "**/Right"], color: BLUE } // Also matches children!
0239 ]
0240 // Result: Left and Right get BLUE (second rule overwrites)
0241 
0242 // BETTER: Use one pattern and rely on applyToDescendants
0243 rules: [
0244   { patterns: ["**/v_upstream*"], color: RED, merge: false }
0245 ]
0246 // Result: v_upstream_coating AND its children get RED
0247 ```
0248 
0249 #### Pitfall 3: "The rest" processes already-styled nodes
0250 
0251 ```typescript
0252 // WRONG ORDER:
0253 rules: [
0254   { color: GREEN, merge: false },               // "The rest" FIRST - styles everything!
0255   { patterns: ["**/mirror*"], color: SILVER }   // Never runs - everything already processed
0256 ]
0257 
0258 // CORRECT ORDER:
0259 rules: [
0260   { patterns: ["**/mirror*"], color: SILVER },  // Specific patterns FIRST
0261   { color: GREEN, merge: false }                // "The rest" LAST
0262 ]
0263 ```
0264 
0265 #### Pitfall 4: Outline of outline
0266 
0267 When creating outlines, the outline object is added to the scene. Without protection, a subsequent rule might try to outline the outline:
0268 
0269 ```typescript
0270 // This is handled automatically!
0271 // Outlines are marked with geometryEditingSkipRules = true
0272 // They will be skipped by subsequent rules
0273 ```
0274 
0275 ### Advanced: Multiple Nesting Levels
0276 
0277 For deeply nested structures:
0278 
0279 ```
0280 DRICH_assembly (Group)
0281 ├── sector_0 (Group)
0282 │   ├── mirror_panel (Group)
0283 │   │   ├── mirror_surface (Mesh)
0284 │   │   └── mirror_backing (Mesh)
0285 │   └── sensor_array (Group)
0286 │       ├── sensor_0 (Mesh)
0287 │       └── sensor_1 (Mesh)
0288 └── sector_1 (Group)
0289     └── ...
0290 ```
0291 
0292 **Strategy 1: Match deepest level first**
0293 ```typescript
0294 rules: [
0295   // Most specific first
0296   { patterns: ["**/mirror_surface*"], color: SILVER, merge: true, newName: "mirrors" },
0297   { patterns: ["**/sensor_*"], color: TEAL_200, merge: true, newName: "sensors" },
0298   // Everything else
0299   { color: GREEN_100, merge: false }
0300 ]
0301 ```
0302 
0303 **Strategy 2: Match by branch**
0304 ```typescript
0305 rules: [
0306   // Match parent groups, style all descendants
0307   { patterns: ["**/mirror_panel"], color: SILVER, merge: false },
0308   { patterns: ["**/sensor_array"], color: TEAL_200, merge: false },
0309   // Everything else
0310   { color: GREEN_100, merge: false }
0311 ]
0312 ```
0313 
0314 **Strategy 3: Merge entire branches**
0315 ```typescript
0316 rules: [
0317   // Merge all meshes under mirror_panel into one
0318   { patterns: ["**/mirror_panel/**"], merge: true, newName: "mirrors" },
0319   // Merge all sensors
0320   { patterns: ["**/sensor_array/**"], merge: true, newName: "sensors" }
0321 ]
0322 ```
0323 
0324 ### Visualizing the Processing
0325 
0326 Here's a complete example showing the processing flow:
0327 
0328 ```typescript
0329 // Geometry:
0330 // BeamPipe_assembly
0331 // ├── v_upstream_coating
0332 // │   ├── Left
0333 // │   └── Right
0334 // ├── v_downstream_section
0335 // └── center_pipe
0336 
0337 {
0338   name: "BeamPipe_assembly*",
0339   rules: [
0340     // Rule 1: Match v_upstream branch
0341     {
0342       patterns: ["**/v_upstream*"],
0343       color: INDIGO_200,
0344       merge: false,
0345       outline: true
0346     },
0347     // Rule 2: The rest
0348     {
0349       color: INDIGO_80,
0350       merge: false,
0351       outline: true
0352     }
0353   ]
0354 }
0355 
0356 // Processing steps:
0357 //
0358 // 1. Clear all skip flags on BeamPipe_assembly tree
0359 //
0360 // 2. Rule 1 executes:
0361 //    - Pattern "**/v_upstream*" matches "v_upstream_coating"
0362 //    - applyToDescendants=true (default)
0363 //    - Traverse v_upstream_coating:
0364 //      - Left (Mesh): color=INDIGO_200, create outline, mark processed
0365 //      - Right (Mesh): color=INDIGO_200, create outline, mark processed
0366 //    - Mark v_upstream_coating as processed
0367 //
0368 // 3. Rule 2 ("the rest") executes:
0369 //    - Traverse BeamPipe_assembly looking for unprocessed meshes:
0370 //      - v_upstream_coating: skip (processed)
0371 //      - Left: skip (in processed branch - parent is processed)
0372 //      - Right: skip (in processed branch)
0373 //      - Left_outline: skip (marked when created)
0374 //      - Right_outline: skip (marked when created)
0375 //      - v_downstream_section: NOT in processed branch
0376 //        → color=INDIGO_80, create outline, mark processed
0377 //      - center_pipe: NOT in processed branch
0378 //        → color=INDIGO_80, create outline, mark processed
0379 //
0380 // Final result:
0381 // ├── v_upstream_coating (processed)
0382 // │   ├── Left ─────────── INDIGO_200 + outline
0383 // │   ├── Left_outline ─── (auto-created)
0384 // │   ├── Right ────────── INDIGO_200 + outline
0385 // │   └── Right_outline ── (auto-created)
0386 // ├── v_downstream_section ─ INDIGO_80 + outline
0387 // ├── v_downstream_section_outline
0388 // ├── center_pipe ────────── INDIGO_80 + outline
0389 // └── center_pipe_outline
0390 ```
0391 
0392 ## Examples
0393 
0394 ### Simple Color Change
0395 
0396 ```typescript
0397 {
0398   name: "HcalBarrel*",
0399   rules: [
0400     {
0401       color: 0x90CAF9,  // Light blue
0402       merge: true,
0403       outline: true
0404     }
0405   ]
0406 }
0407 ```
0408 
0409 ### Multiple Patterns with Different Styles
0410 
0411 ```typescript
0412 {
0413   name: "DRICH*",
0414   rules: [
0415     // Mirrors get special treatment
0416     {
0417       patterns: ["**/DRICH_mirror*"],
0418       color: SILVER,
0419       merge: true,
0420       outline: false,
0421       newName: "DRICH_mirror"
0422     },
0423     // PDUs in teal
0424     {
0425       patterns: ["**/DRICH*pdu*"],
0426       color: TEAL_200,
0427       merge: true,
0428       newName: "DRICH_pdu"
0429     },
0430     // Everything else in light green
0431     {
0432       color: GREEN_100,
0433       merge: false,
0434       outline: true
0435     }
0436   ]
0437 }
0438 ```
0439 
0440 ### Transparent Material
0441 
0442 ```typescript
0443 {
0444   name: "InnerTrackerSupport_assembly*",
0445   rules: [
0446     {
0447       material: new THREE.MeshStandardMaterial({
0448         color: 0x878F99,      // Titanium
0449         roughness: 0.4,
0450         metalness: 0.2,
0451         transparent: true,
0452         opacity: 0.7,
0453         side: THREE.DoubleSide
0454       }),
0455       outline: true,
0456       outlineColor: 0xDBE4EB,  // Chrome
0457       merge: true
0458     }
0459   ]
0460 }
0461 ```
0462 
0463 ### Glass-like Material (DIRC)
0464 
0465 ```typescript
0466 {
0467   patterns: ["**/*box*", "**/*prism*"],
0468   material: new THREE.MeshPhysicalMaterial({
0469     color: 0xA5D6A7,        // Green
0470     metalness: 0.3,
0471     roughness: 0.2,
0472     envMapIntensity: 0.5,
0473     clearcoat: 0.8,
0474     transparent: true,
0475     opacity: 0.5,
0476     reflectivity: 0.2,
0477     ior: 0.9,
0478     side: THREE.DoubleSide
0479   }),
0480   newName: "DIRC_barAndPrisms"
0481 }
0482 ```
0483 
0484 ### Hierarchical Processing (BeamPipe Example)
0485 
0486 ```typescript
0487 {
0488   name: "BeamPipe_assembly*",
0489   rules: [
0490     // Match v_upstream nodes AND all their children
0491     {
0492       patterns: ["**/v_upstream*"],
0493       color: 0x9FA8DA,     // Indigo 200
0494       merge: false,        // Keep individual meshes
0495       outline: true
0496       // applyToDescendants defaults to true
0497     },
0498     // "The rest" - skips v_upstream branch entirely
0499     {
0500       color: 0xD1D9F0,     // Indigo 80
0501       merge: false,
0502       outline: true
0503     }
0504   ]
0505 }
0506 ```
0507 
0508 ## Built-in Color Palette
0509 
0510 Firebird provides a curated color palette in `cool3-geometry-ruleset.ts`:
0511 
0512 ### Cool Colors (Light/Pastel)
0513 | Constant | Hex | Use Case |
0514 |----------|-----|----------|
0515 | `BLUE_50` | `0xE3F2FD` | Very light backgrounds |
0516 | `CYAN_50` | `0xE0F7FA` | Light cyan accents |
0517 | `INDIGO_80` | `0xD1D9F0` | Light indigo |
0518 | `INDIGO_150` | `0xB2BBE0` | Medium-light indigo |
0519 | `GREEN_100` | `0xC8E6C9` | Light green (PID) |
0520 | `YELLOW_100` | `0xFFF9C4` | Light yellow (tracking) |
0521 
0522 ### Medium Colors
0523 | Constant | Hex | Use Case |
0524 |----------|-----|----------|
0525 | `AMBER_200` | `0xFFE082` | Tracking detectors |
0526 | `BLUE_200` | `0x90CAF9` | HCAL |
0527 | `TEAL_200` | `0x80CBC4` | TOF components |
0528 | `INDIGO_200` | `0x9FA8DA` | ECAL |
0529 | `ORANGE_200` | `0xFFCC80` | Roman pots |
0530 
0531 ### Metallic Colors
0532 | Constant | Hex | Use Case |
0533 |----------|-----|----------|
0534 | `SILVER` | `0xC0C0C0` | Mirrors, rails |
0535 | `CHROME` | `0xDBE4EB` | Polished metal |
0536 | `STEEL_BLUE` | `0x4682B4` | Beam pipes |
0537 | `TITANIUM` | `0x878F99` | Supports |
0538 | `COPPER` | `0xB87333` | Conductors |
0539 
0540 ### Color Scheme Philosophy
0541 
0542 The COOL3 theme follows detector-type conventions:
0543 
0544 | Detector Type | Color Family | Examples |
0545 |---------------|--------------|----------|
0546 | **Tracking** | Yellowish-Orange | Vertex, Si Trackers, MPGD |
0547 | **PID** | Greenish | TOF, DIRC, RICH |
0548 | **ECAL** | Pink/Violet | Forward, Barrel, Backward |
0549 | **HCAL** | Bluish | LFHCAL, HcalBarrel |
0550 | **Flux Return** | Grey | FluxBarrel, FluxEndcap |
0551 | **Beam Pipes** | Blue Metallic | Electron pipe |
0552 | **Magnets** | Neutral/Light | Solenoid, Beamline magnets |
0553 
0554 ## Creating Custom Themes
0555 
0556 To create a custom theme:
0557 
0558 1. **Create a new file** in `src/app/theme/`:
0559 
0560 ```typescript
0561 // my-theme-geometry-ruleset.ts
0562 import * as THREE from "three";
0563 
0564 // Define your colors
0565 export const MY_PRIMARY = 0x3498DB;
0566 export const MY_SECONDARY = 0x2ECC71;
0567 
0568 // Export your ruleset
0569 export const myThemeRules = [
0570   {
0571     name: "MyDetector*",
0572     rules: [
0573       { color: MY_PRIMARY, merge: true, outline: true }
0574     ]
0575   },
0576   // ... more rules
0577 ];
0578 ```
0579 
0580 2. **Register the theme** in the geometry processor or configuration.
0581 
0582 ## Performance Considerations
0583 
0584 ### Merge for Performance
0585 
0586 Merging reduces draw calls significantly:
0587 
0588 ```typescript
0589 // Good for performance - single merged mesh
0590 { merge: true, newName: "HCAL_merged" }
0591 
0592 // More flexible but slower - individual meshes
0593 { merge: false }
0594 ```
0595 
0596 ### Outline Threshold
0597 
0598 Higher threshold = fewer edges = better performance:
0599 
0600 ```typescript
0601 {
0602   outline: true,
0603   outlineThresholdAngle: 60  // Only show sharp edges
0604 }
0605 ```
0606 
0607 ### Simplification
0608 
0609 For complex geometries:
0610 
0611 ```typescript
0612 {
0613   simplifyMeshes: true,
0614   simplifyRatio: 0.5  // Keep 50% of vertices
0615 }
0616 ```
0617 
0618 ## Debugging Rules
0619 
0620 ### Check Console Output
0621 
0622 The geometry processor logs timing information:
0623 
0624 ```
0625 [processRuleSets] Applying 25 theme rules...
0626 [processRuleSets] Applying rules took >0.5s: 1234.5 for DRICH_assembly
0627 ```
0628 
0629 ### Verify Patterns
0630 
0631 Use the browser console to test patterns:
0632 
0633 ```javascript
0634 // In browser dev tools
0635 import outmatch from 'outmatch';
0636 const pattern = outmatch('**/v_upstream*');
0637 pattern('BeamPipe_assembly/v_upstream_coating');  // true
0638 pattern('BeamPipe_assembly/v_upstream_coating/Left');  // false
0639 ```
0640 
0641 ## API Reference
0642 
0643 ### Main Functions
0644 
0645 ```typescript
0646 // Clear skip flags before processing
0647 clearGeometryEditingFlags(root: Object3D): void
0648 
0649 // Apply a single rule to a node
0650 editThreeNodeContent(node: Object3D, rule: EditThreeNodeRule): void
0651 
0652 // Check if node was already processed
0653 isAlreadyProcessed(obj: Object3D): boolean
0654 
0655 // Check if node or any ancestor was processed
0656 isInProcessedBranch(obj: Object3D): boolean
0657 ```
0658 
0659 ### ThreeGeometryProcessor
0660 
0661 ```typescript
0662 class ThreeGeometryProcessor {
0663   // Apply rulesets to detectors
0664   processRuleSets(
0665     ruleSets: DetectorThreeRuleSet[],
0666     detectors: Subdetector[]
0667   ): void
0668 }
0669 ```
0670 
0671 ### Loading Rules from JSON
0672 
0673 Rules can be loaded from JSONC files:
0674 
0675 ```typescript
0676 import { ruleSetsFromObj } from './three-geometry.processor';
0677 
0678 // Parse JSON with material support
0679 const ruleSets = ruleSetsFromObj(jsonData);
0680 ```
0681 
0682 JSON format supports `materialJson` for Three.js materials:
0683 
0684 ```json
0685 {
0686   "name": "MyDetector*",
0687   "rules": [
0688     {
0689       "patterns": ["**/*box*"],
0690       "color": "0xA5D6A7",
0691       "materialJson": {
0692         "type": "MeshStandardMaterial",
0693         "color": 10809767,
0694         "roughness": 0.5,
0695         "metalness": 0.3
0696       }
0697     }
0698   ]
0699 }
0700 ```