Warning, /firebird/firebird-ng/src/app/data-pipelines/three-geometry.processor.ts is written in an unsupported language. File is not indexed.
0001 import * as THREE from "three";
0002 import {wildCardCheck} from "../utils/wildcard";
0003 import {editThreeNodeContent, EditThreeNodeRule, clearGeometryEditingFlags} from "../utils/three-geometry-editor";
0004 import {Subdetector} from "../model/subdetector";
0005
0006 /**
0007 * A typed object that associates a name (or multiple names) with an array of edit rules.
0008 * E.g. { name: "DIRC_14", rules: [ { patterns: [...], ... } ] }
0009 */
0010 export interface DetectorThreeRuleSet {
0011 names?: string[];
0012 name?: string;
0013 rules: EditThreeNodeRule[];
0014 }
0015
0016 /**
0017 * Converts a raw JSON/JSONC array into typed DetectorThreeRuleSet objects.
0018 * If an EditThreeNodeRule has "materialJson", we parse it using THREE.MaterialLoader.
0019 */
0020 export function ruleSetsFromObj(obj: any): DetectorThreeRuleSet[] {
0021 // Not an array => return empty
0022 if (!Array.isArray(obj)) {
0023 console.warn('ruleSetsFromObj: top-level object is not an array. Returning empty.');
0024 return [];
0025 }
0026
0027 // Create a single MaterialLoader we can reuse for all materialJson objects
0028 const materialLoader = new THREE.MaterialLoader();
0029
0030 return obj.map((item: any) => {
0031 // Ensure we have a rules array
0032 if (!item.rules || !Array.isArray(item.rules)) {
0033 console.warn('ruleSetsFromObj: missing or invalid "rules" array in item:', item);
0034 return { rules: [] };
0035 }
0036
0037 // Convert each rule
0038 const convertedRules: EditThreeNodeRule[] = item.rules.map((r: any) => {
0039 const rule: EditThreeNodeRule = { ...r };
0040
0041 // 1) Convert a color from string hex "0xabcdef" => number
0042 if (typeof rule.color === 'string') {
0043 rule.color = parseInt(rule.color, 16);
0044 }
0045
0046 // 2) If there's "materialJson", parse it using THREE.MaterialLoader
0047 if (r.materialJson && typeof r.materialJson === 'object') {
0048 try {
0049 // Convert raw JSON to real material
0050 const loadedMaterial = materialLoader.parse(r.materialJson);
0051 rule.material = loadedMaterial;
0052 } catch (err) {
0053 console.error('Failed to parse materialJson:', err, r.materialJson);
0054 }
0055 }
0056
0057 return rule;
0058 });
0059
0060 return {
0061 names: item.names,
0062 name: item.name,
0063 rules: convertedRules,
0064 };
0065 });
0066 }
0067
0068
0069 /**
0070 * Matches which set of rules should be applied to which detectors
0071 *
0072 * - Detectors are matched based on their `sourceGeometryName` against the names specified in the rulesets.
0073 * - Rule lists are matched to detectors in the order they appear in the rulesets
0074 * - Once a rule is applied to a detector, that detector is excluded from further rule matching, ensuring each detector is processed only once.
0075 * - If both `names` and `name` are provided in a ruleset, the function treats it as a user error in JSON rule editing but processes both without raising an exception.
0076 * - If a ruleset contains a wildcard name (`"*"`), it will apply its rules to any detectors not already matched by previous rulesets. So it should be placed in
0077 *
0078 * @param {Subdetector[]} detectors - The list of detectors to which the rules will be applied.
0079 * @param {DetectorThreeRuleSet[]} ruleSets - The set of rules to be applied, processed sequentially.
0080 * @return {Map<Subdetector, EditThreeNodeRule[]>} - A map associating each detector with an array of the rules applied to it.
0081 */
0082 export function matchRulesToDetectors(ruleSets: DetectorThreeRuleSet[], detectors: Subdetector[]): Map<Subdetector, EditThreeNodeRule[]> {
0083 const unassignedDetectors = new Set(detectors);
0084 const detectorRulesMap = new Map<Subdetector, EditThreeNodeRule[]>();
0085
0086 for(let ruleSet of ruleSets) {
0087 const targets = new Set<Subdetector>();
0088 let names = new Set<string>(ruleSet.names || []);
0089
0090 // Handle possible user error where both 'name' and 'names' are provided.
0091 if (ruleSet.name) {
0092 names.add(ruleSet.name);
0093 }
0094
0095 for(let name of names) {
0096 for(let det of unassignedDetectors) {
0097 if (wildCardCheck(det.sourceGeometryName, name)) {
0098 targets.add(det);
0099 detectorRulesMap.set(det, ruleSet.rules || []);
0100 unassignedDetectors.delete(det); // Move deletion here to optimize
0101 }
0102 }
0103 }
0104 }
0105
0106 return detectorRulesMap;
0107 }
0108
0109 export class ThreeGeometryProcessor {
0110
0111
0112 constructor() {
0113
0114 }
0115
0116 public processRuleSets(ruleSets: DetectorThreeRuleSet[], detectors: Subdetector[]) {
0117 console.log(`[processRuleSets] Applying ${ruleSets.length} theme rules...`)
0118 const totalTimePerfMessage = "[processRuleSets] Time applying rules";
0119 console.time(totalTimePerfMessage);
0120 let detRulesMap = matchRulesToDetectors(ruleSets, detectors);
0121 for (let [detector, ruleSet] of detRulesMap) {
0122 // Some performance metrics
0123 const start = performance.now();
0124
0125 // Clear geometry editing flags before processing this detector's rules
0126 // This ensures "the rest" rules work correctly within each detector's ruleset
0127 clearGeometryEditingFlags(detector.geometry);
0128
0129 // Actually apply rules
0130 for(let rule of ruleSet) {
0131 editThreeNodeContent(detector.geometry, rule);
0132 }
0133
0134 // Check the rule didn't take too long
0135 const end = performance.now();
0136 const elapsed = end - start;
0137 if(elapsed > 500) {
0138 console.log(`[processRuleSet] Applying rules took >0.5s: ${elapsed.toFixed(1)} for ${detector.name}`);
0139 }
0140 }
0141 console.timeEnd(totalTimePerfMessage);
0142 }
0143 }