39 lines
3.2 KiB
JavaScript
39 lines
3.2 KiB
JavaScript
// Framework-independent data adapter. Does not parse or modify the project's INP.
|
|
export const CAD_ORIGIN=Object.freeze([513800,2344450]);
|
|
export function cadToGltf(point,displayZ=0){
|
|
if(!Array.isArray(point)||point.length<2||!point.slice(0,2).every(Number.isFinite)||!Number.isFinite(displayZ))throw Error('Expected finite CAD XY and an explicit display height.');
|
|
return [point[0]-CAD_ORIGIN[0],displayZ,-(point[1]-CAD_ORIGIN[1])];
|
|
}
|
|
// Supply longitude/latitude OF CAD_ORIGIN, not the old OSM anchor. This function
|
|
// deliberately cannot guess CRS, accept the disabled legacy candidate, or add 23.9deg.
|
|
export function mapModelMatrixElements(mercatorOrigin,meterScale,{rotationDeg=0,horizontalScale=1}={}){
|
|
if(![mercatorOrigin?.x,mercatorOrigin?.y,mercatorOrigin?.z,meterScale,rotationDeg,horizontalScale].every(Number.isFinite)||meterScale<=0||horizontalScale<=0)throw Error('Confirmed Mercator origin and positive scales required.');
|
|
const a=rotationDeg*Math.PI/180,c=Math.cos(a)*meterScale*horizontalScale,s=Math.sin(a)*meterScale*horizontalScale;
|
|
// Column-major THREE.Matrix4; glTF +Y up, -Z CAD north; Mercator +Y south.
|
|
return [c,-s,0,0, 0,0,meterScale,0, s,c,0,0, mercatorOrigin.x,mercatorOrigin.y,mercatorOrigin.z,1];
|
|
}
|
|
export function resolveMeterPlacements(bindingFile,{inpSha256,links,displayHeight=0.35}){
|
|
if(inpSha256!==bindingFile.compatibleInpSha256)throw Error('INP version mismatch: reconcile the equipment mapping before placement.');
|
|
const byId=new Map(links.map(link=>[String(link.id),link]));if(byId.size!==links.length)throw Error('Duplicate INP link IDs.');
|
|
const placements=[],issues=[];
|
|
for(const meter of bindingFile.meters){
|
|
const link=byId.get(meter.inpLinkId);
|
|
if(!link){issues.push({assetId:meter.assetId,reason:'missing_host_link',id:meter.inpLinkId});continue;}
|
|
const pts=link.coordinates;
|
|
if(!Array.isArray(pts)||pts.length<2||pts.some(p=>!Array.isArray(p)||p.length<2||!p.slice(0,2).every(Number.isFinite))||!Number.isFinite(link.diameterMm)||link.diameterMm<=0){issues.push({assetId:meter.assetId,reason:'invalid_host_geometry_or_diameter'});continue;}
|
|
let best;
|
|
for(let i=1;i<pts.length;i++){
|
|
const a=pts[i-1],b=pts[i],dx=b[0]-a[0],dy=b[1]-a[1],length=Math.hypot(dx,dy);if(length<1e-8)continue;
|
|
const t=Math.max(0,Math.min(1,((meter.cadXY[0]-a[0])*dx+(meter.cadXY[1]-a[1])*dy)/(length*length)));
|
|
const distance=Math.hypot(a[0]+t*dx-meter.cadXY[0],a[1]+t*dy-meter.cadXY[1]);
|
|
if(!best||distance<best.distance)best={a,b,dx,dy,length,t,distance};
|
|
}
|
|
if(!best){issues.push({assetId:meter.assetId,reason:'zero_length_host'});continue;}
|
|
if(best.distance>.25){issues.push({assetId:meter.assetId,reason:'CAD_host_registration_exceeds_0.25m',distanceM:best.distance});continue;}
|
|
const half=Math.min(.33,best.length*.2),t=Math.max(half/best.length,Math.min(1-half/best.length,best.t));
|
|
const center=[best.a[0]+best.dx*t,best.a[1]+best.dy*t];
|
|
placements.push({assetId:meter.assetId,prototype:meter.prototype,hostLinkId:meter.inpLinkId,position:cadToGltf(center,displayHeight),direction:[best.dx/best.length,0,-best.dy/best.length],scale:[half/.33,link.diameterMm/150,link.diameterMm/150],portDistanceM:2*half,createsHydraulicLink:false,scadaId:null});
|
|
}
|
|
return {placements,issues};
|
|
}
|