@equinor/videx-map
Version:
Component for Pixi-overlay in Leaflet.
1 lines • 92.1 kB
JavaScript
import*as PIXI from"pixi.js";import Vector2 from"@equinor/videx-vector2";import{inverseLerp,lerp,clamp}from"@equinor/videx-math";import{flatten,mix}from"@equinor/videx-linear-algebra";import earcut from"earcut";import{color}from"d3-color";import{v4}from"uuid";class ModuleInterface{pixiOverlay;root;visibility=!0;constructor(){this.root=new PIXI.Container,this.root.sortableChildren=!0}destroy(){this.root.destroy({children:!0,texture:!0,baseTexture:!0}),this.root=null}toggle(){this.root.visible=!this.root.visible}setVisibility(visible){return visible!==this.visibility&&(this.root.visible=visible,this.visibility=visible,!0)}onAdd(_map){}onRemove(_map){}resize(_zoom){}}function log(text){const out=`%cVIDEX-MAP%c ${text}`;console.log(`${out} (${function(){const date=new Date;return`${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`}()})`,"\n background: #555;\n color: #eee;\n padding: 0 6px 0 6px;\n border-radius: 2px;\n ",null)}class FaultlineModule extends ModuleInterface{spawned=[];pool=[];config={color:7503240,alpha:1,outlineWidth:.125};constructor(config){super(),config&&(isNaN(config.color)||(this.config.color=config.color),isNaN(config.alpha)||(this.config.alpha=config.alpha),isNaN(config.outlineWidth)||(this.config.outlineWidth=config.outlineWidth))}destroy(){super.destroy(),this.pool.forEach((g=>g.destroy({children:!0,texture:!0,baseTexture:!0}))),this.pool=null,this.spawned=null}set(data,redraw=!1){this.clear();const project=this.pixiOverlay.utils.latLngToLayerPoint;let lineCount=0;data.forEach((d=>{let faultline;faultline=this.pool.length>0?this.pool.pop():new PIXI.Graphics,this.root.addChild(faultline),this.spawned.push(faultline),faultline.alpha=this.config.alpha;const projected=d.coordinates.map((p=>{const coord=project(p);return new PIXI.Point(coord.x,coord.y)})),first=projected[0],last=projected[projected.length-1];if(Vector2.equals([first.x,first.y],[last.x,last.y],1e-6))faultline.beginFill(this.config.color),faultline.lineStyle(this.config.outlineWidth,this.config.color),faultline.drawPolygon(projected),faultline.endFill();else{lineCount++,faultline.lineStyle(this.config.outlineWidth,this.config.color).moveTo(first.x,first.y);for(let i=1;i<projected.length;i++)faultline.lineTo(projected[i].x,projected[i].y)}})),lineCount>0&&log(`Drawing ${lineCount} faultline polygons as lines.`),redraw&&this.pixiOverlay.redraw()}clear(){for(;this.spawned.length>0;){const temp=this.spawned.pop();this.root.removeChild(temp),temp.clear(),this.pool.push(temp)}}resize(_zoom){}}function Intersection(p1,d1,p2,d2){const c=[p1[0]-p2[0],p1[1]-p2[1]],len=(c[0]*d2[1]-c[1]*d2[0])/(d1[1]*d2[0]-d1[0]*d2[1]);return c[0]=d1[0]*len+p1[0],c[1]=d1[1]*len+p1[1],c}class Mesh{static WellboreSegment(points,thickness=1,type){const vertices=[],triangles=[],vertexData=[],extraData=[],_thickness=.5*thickness,point0=points[0],first=point0.position,from0=Vector2.sub(points[1].position,first).rescale(_thickness);vertices.push(-from0[1]+first[0],from0[0]+first[1],from0[1]+first[0],-from0[0]+first[1]),vertexData.push(point0.distance,1,-point0.direction[1],point0.direction[0],point0.distance,0,point0.direction[1],-point0.direction[0]),extraData.push(type,type);for(let i=1;i<points.length-1;i++){const point=points[i],prev=points[i-1].position,cur=point.position,next=points[i+1].position,to=Vector2.sub(cur,prev),from=Vector2.sub(next,cur);let upper=null,inner=null;if(Vector2.angleDeg(to,from)<90){const toU=to.rotate90().mutable.rescale(_thickness).add(prev).immutable,fromU=from.rotate90().mutable.rescale(_thickness).add(next).immutable,toI=to.rotate270().mutable.rescale(_thickness).add(prev).immutable,fromI=from.rotate270().mutable.rescale(_thickness).add(next).immutable;upper=Intersection(toU,to,fromU,from),inner=Intersection(toI,to,fromI,from)}else upper=[-point.direction[1]*_thickness+cur[0],point.direction[0]*_thickness+cur[1]],inner=[point.direction[1]*_thickness+cur[0],-point.direction[0]*_thickness+cur[1]];if(vertices.push(upper[0],upper[1],inner[0],inner[1]),vertexData.push(point.distance,1,-point.direction[1],point.direction[0],point.distance,0,point.direction[1],-point.direction[0]),extraData.push(type,type),0!==i){const n=2*i;triangles.push(n-1,n-2,n,n-1,n,n+1)}}const pointN=points[points.length-1],last=pointN.position,toN=Vector2.sub(last,points[points.length-2].position).rescale(_thickness);vertices.push(last[0]-toN[1],last[1]+toN[0],last[0]+toN[1],last[1]-toN[0]),vertexData.push(pointN.distance,1,-pointN.direction[1],pointN.direction[0],pointN.distance,0,pointN.direction[1],-pointN.direction[0]),extraData.push(type,type);const n=2*points.length-2;return triangles.push(n-1,n-2,n,n-1,n,n+1),{vertices:vertices,triangles:triangles,vertexData:vertexData,extraData:extraData}}static SimpleLine=(points,thickness=1)=>{const linethickness=.5*thickness;function GetNormal(index){if(0===index)return Vector2.sub(points[1],points[0]).mutable.rotate90().rescale(1);if(index===points.length-1)return Vector2.sub(points[points.length-1],points[points.length-2]).mutable.rotate90().rescale(1);const prev=points[index-1],cur=points[index],next=points[index+1];return Vector2.lerpRot(Vector2.sub(cur,prev),Vector2.sub(next,cur),.5).mutable.rotate90().rescale(1)}const vertices=[],triangles=[],normals=[];let prevUpperRight,baseTris=0;for(let i=0;i<points.length-1;i++){const cur=points[i],next=points[i+1],dirN=Vector2.sub(next,cur).rotate90().mutable.rescale(linethickness).immutable,leftNormal=GetNormal(i),rightNormal=GetNormal(i+1),lowerLeft=Vector2.sub(cur,dirN),upperLeft=Vector2.add(cur,dirN),lowerRight=Vector2.sub(next,dirN),upperRight=Vector2.add(next,dirN);if(vertices.push(lowerLeft[0],lowerLeft[1],upperLeft[0],upperLeft[1],lowerRight[0],lowerRight[1],upperRight[0],upperRight[1]),normals.push(-leftNormal[0],-leftNormal[1],leftNormal[0],leftNormal[1],-rightNormal[0],-rightNormal[1],rightNormal[0],rightNormal[1]),triangles.push(baseTris,baseTris+1,baseTris+3,baseTris,baseTris+3,baseTris+2),0!==i){const toPrevUpper=Vector2.sub(prevUpperRight,upperLeft);Vector2.signedAngle(dirN,toPrevUpper)<0?triangles.push(baseTris,baseTris-2,baseTris+1):triangles.push(baseTris,baseTris-1,baseTris+1)}prevUpperRight=upperRight,baseTris+=4}return{vertices:vertices,triangles:triangles,normals:normals}};static Polygon=points=>{const vertices=flatten(points);return{vertices:vertices,triangles:earcut(vertices)}};static PolygonOutline=(points,thickness=1)=>{const linethickness=.5*thickness;function GetIndex(index){let r=index%points.length;return r<0&&(r+=points.length),r}const vertices=[],triangles=[],normals=[];let prevUpperRight,firstUpperLeft,firstDirN,baseTris=0;for(let i=0;i<points.length;i++){const prev=points[GetIndex(i-1)],cur=points[GetIndex(i)],next=points[GetIndex(i+1)],next2=points[GetIndex(i+2)],dirN=Vector2.sub(next,cur).rotate90().mutable.rescale(linethickness).immutable,leftNormal=Vector2.lerpRot(Vector2.sub(cur,prev),Vector2.sub(next,cur),.5).mutable.rotate90().rescale(1),rightNormal=Vector2.lerpRot(Vector2.sub(next,cur),Vector2.sub(next2,next),.5).mutable.rotate90().rescale(1),lowerLeft=Vector2.sub(cur,dirN),upperLeft=Vector2.add(cur,dirN),lowerRight=Vector2.sub(next,dirN),upperRight=Vector2.add(next,dirN);if(vertices.push(lowerLeft[0],lowerLeft[1],upperLeft[0],upperLeft[1],lowerRight[0],lowerRight[1],upperRight[0],upperRight[1]),normals.push(-leftNormal[0],-leftNormal[1],leftNormal[0],leftNormal[1],-rightNormal[0],-rightNormal[1],rightNormal[0],rightNormal[1]),triangles.push(baseTris,baseTris+1,baseTris+3,baseTris,baseTris+3,baseTris+2),0!==i){const toPrevUpper=Vector2.sub(prevUpperRight,upperLeft);Vector2.signedAngle(dirN,toPrevUpper)<0?triangles.push(baseTris,baseTris-2,baseTris+1):triangles.push(baseTris,baseTris-1,baseTris+1)}else firstUpperLeft=upperLeft,firstDirN=dirN;if(i===points.length-1){const toLastUpper=Vector2.sub(upperRight,firstUpperLeft);Vector2.signedAngle(firstDirN,toLastUpper)<0?triangles.push(0,baseTris-2,1):triangles.push(0,baseTris-1,1)}prevUpperRight=upperRight,baseTris+=4}return{vertices:vertices,triangles:triangles,normals:normals}};static from(vertices,triangles,vertexShader,fragmentShader,uniforms,normals){const geometry=new PIXI.Geometry;geometry.addAttribute("inputVerts",vertices,2),normals&&geometry.addAttribute("inputNormals",normals,2),geometry.addIndex(triangles);const shader=PIXI.Shader.from(vertexShader,fragmentShader,uniforms);return new PIXI.Mesh(geometry,shader)}}class OutlineModule extends ModuleInterface{outlineDict={};spawned=[];static vertexShader;static fragmentShader;config={baseWidth:.1,minZoom:0,maxZoom:18,minExtraWidth:.1,maxExtraWidth:10};state={extraWidth:1};scaling;constructor(config){super(),config&&(isNaN(config.minZoom)||(this.config.minZoom=config.minZoom),isNaN(config.maxZoom)||(this.config.maxZoom=config.maxZoom),isNaN(config.minExtraWidth)||(this.config.minExtraWidth=config.minExtraWidth),isNaN(config.maxExtraWidth)||(this.config.maxExtraWidth=config.maxExtraWidth))}set(data){const project=this.pixiOverlay.utils.latLngToLayerPoint;this.clear(),data.forEach((outlineCollection=>{const uniforms={color:outlineCollection.meta.stroke,width:this.state.extraWidth,visible:!0};this.outlineDict[outlineCollection.meta.name]=uniforms;const coordinates=outlineCollection.coordinates;for(let n=0;n<coordinates.length;n++){const polygon=coordinates[n],projected=[];for(let i=0;i<polygon.length;i++){const p=polygon[i],pos=project(p);projected.push([pos.x,pos.y])}let outlineData;if(Vector2.equals(projected[0],projected[projected.length-1],1e-6)){if(projected.pop(),projected.length<=2){log(`Skipping outline (Polygon) with ${projected.length} points.`);continue}outlineData=Mesh.PolygonOutline(projected,this.config.baseWidth)}else{if(projected.length<=1){log(`Skipping outline (Line) with ${projected.length} points.`);continue}outlineData=Mesh.SimpleLine(projected,this.config.baseWidth)}const outline=Mesh.from(outlineData.vertices,outlineData.triangles,OutlineModule.vertexShader,OutlineModule.fragmentShader,uniforms,outlineData.normals);this.root.addChild(outline),this.spawned.push(outline)}}))}setVisibleLayers(names){Object.keys(this.outlineDict).forEach((key=>this.outlineDict[key].visible=!1)),names.forEach((name=>{const uniforms=this.outlineDict[name];uniforms&&(uniforms.visible=!0)}))}clear(){for(;this.spawned.length>0;){const temp=this.spawned.pop();this.root.removeChild(temp),temp.destroy()}this.outlineDict={}}resize(zoom){const t=inverseLerp(this.config.minZoom,this.config.maxZoom,zoom),width=lerp(this.config.maxExtraWidth,this.config.minExtraWidth,t);this.state.extraWidth=width,Object.keys(this.outlineDict).forEach((key=>{this.outlineDict[key].width=width}))}}function toShader(n){return n===Math.floor(n)?`${n.toString()}.0`:n.toString()}OutlineModule.vertexShader="\n attribute vec2 inputVerts;\n attribute vec2 inputNormals;\n\n uniform mat3 translationMatrix;\n uniform mat3 projectionMatrix;\n\n uniform float width;\n\n void main() {\n vec2 pos = inputVerts + inputNormals * width;\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(pos, 1.0)).xy, 0.0, 1.0);\n }\n",OutlineModule.fragmentShader="\n precision mediump float;\n\n uniform vec3 color;\n uniform bool visible;\n\n void main() {\n if (!visible) discard;\n gl_FragColor = vec4(color, 1.0);\n }\n";class WellboreShader{static program=null;static get(color,completionVisible,wellboreVisible){return new PIXI.Shader(WellboreShader.program,{wellboreColor1:color.col1,wellboreColor2:color.col2,completionVisible:completionVisible,wellboreVisible:wellboreVisible,status:0})}static build(maxScale,wellboreDash){const vertex=`\n attribute vec2 verts;\n attribute vec4 vertCol;\n attribute float typeData;\n\n uniform mat3 translationMatrix;\n uniform mat3 projectionMatrix;\n uniform float wellboreRadius;\n\n varying vec4 vCol;\n varying float type;\n\n void main() {\n vCol = vertCol;\n type = typeData;\n\n vec2 normal = vertCol.zw;\n\n float extraRadius = wellboreRadius - ${toShader(maxScale)};\n\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(verts + normal * extraRadius, 1.0)).xy, 0.0, 1.0);\n }\n `,dash=toShader(wellboreDash),doubleDash=toShader(2*wellboreDash),fragment=`\n precision mediump float;\n\n varying vec4 vCol;\n varying float type;\n\n uniform vec3 wellboreColor1;\n uniform vec3 wellboreColor2;\n uniform bool completionVisible;\n uniform bool wellboreVisible;\n uniform int status;\n\n const vec3 sunDir = vec3(0.6247, -0.6247, 0.4685);\n\n void main() {\n vec3 col = vec3(0.0);\n float alpha = 1.0;\n\n if (status == 0) {\n if(type == 0.0) {\n if (!wellboreVisible) {\n alpha = 0.03;\n }\n } else if (type == 1.0) {\n if(completionVisible){\n if(mod(vCol.x, ${doubleDash}) > ${dash}) discard;\n } else if(!wellboreVisible){\n alpha = 0.03;\n }\n }\n if (!completionVisible && type == 2.0) discard;\n\n float dist = clamp(vCol.z * vCol.z + vCol.w * vCol.w, 0.0, 1.0);\n\n vec3 dir3D = vec3(vCol.zw, sqrt(1.0 - dist * dist));\n\n float light = 0.4 + dot(dir3D, sunDir) * 0.6;\n light = clamp(light, 0.0, 1.0);\n\n col = mix(wellboreColor2, wellboreColor1, light);\n }\n\n else if (status == 1) {\n if (type == 2.0) discard;\n if(mod(vCol.x + vCol.y * 0.2, ${toShader(4*wellboreDash)}) > ${doubleDash}) discard;\n vec3 c = wellboreColor2 + wellboreColor1 * 0.5;\n vec3 gray = vec3(0.9);\n col = mix(gray, c, 0.3);\n }\n\n else if (status == 2) {\n if (type == 2.0) discard;\n alpha = 0.03;\n }\n\n col *= alpha;\n gl_FragColor = vec4(col, alpha);\n }\n `;WellboreShader.program=new PIXI.Program(vertex,fragment)}}class RootShader{static program=null;static get(){return new PIXI.Shader(RootShader.program,{active:!0,circleColor1:[0,0,0],circleColor2:[0,0,0]})}static build(maxScale){const vertex=`\n attribute vec2 verts;\n attribute vec2 inputUVs;\n\n uniform mat3 translationMatrix;\n uniform mat3 projectionMatrix;\n uniform float rootRadius;\n\n varying vec2 UVs;\n\n void main() {\n UVs = inputUVs;\n\n vec2 dir = 2.0 * inputUVs - 1.0;\n\n float extraRadius = rootRadius - ${toShader(maxScale)};\n\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(verts + dir * extraRadius, 1.0)).xy, 0.0, 1.0);\n }\n `;RootShader.program=new PIXI.Program(vertex,"\n precision mediump float;\n\n varying vec2 UVs;\n\n uniform vec3 circleColor1;\n uniform vec3 circleColor2;\n uniform bool active;\n\n const vec3 sunDir = vec3(0.6247, -0.6247, 0.4685);\n\n void main() {\n if (!active) {\n discard;\n return;\n }\n vec2 dir = 2.0 * UVs - 1.0;\n float dist = dir.x * dir.x + dir.y * dir.y;\n if (dist > 1.0) discard;\n\n vec3 dir3D = vec3(dir, sqrt(1.0 - dist * dist));\n\n float light = dot(dir3D, sunDir);\n light = 0.4 + light * 0.6;\n\n vec3 col = mix(circleColor2, circleColor1, clamp(light, 0.0, 1.0));\n\n gl_FragColor = vec4(col, 1.0);\n }\n ")}}function generateCircle(center,radius,shader){const geometry=new PIXI.Geometry;geometry.addAttribute("verts",[center[0]-radius,center[1]-radius,center[0]+radius,center[1]-radius,center[0]-radius,center[1]+radius,center[0]+radius,center[1]+radius],2),geometry.addAttribute("inputUVs",[0,0,1,0,0,1,1,1],2),geometry.addIndex([0,2,3,0,3,1]);return new PIXI.Mesh(geometry,shader)}class Label{static state={zoom:1,scale:1,visible:!0,rootDisplacement:1};static style;static config;static height;container;text;background;metrics;_attachToRoot=!1;static setStyle(fontSize){Label.style=new PIXI.TextStyle({fontFamily:"Arial",fontSize:fontSize,fill:16777215,align:"center"}),Label.height=PIXI.TextMetrics.measureText(" ",Label.style).height}static setCommon(config){Label.config=config}constructor(label,fontColor,bgColor){const metrics=PIXI.TextMetrics.measureText(label,Label.style);this.metrics=metrics;const container=new PIXI.Container;container.visible=Label.state.visible,container.zIndex=0,this.container=container;const background=new PIXI.Graphics;background.beginFill(16777215),background.drawRect(.55*-metrics.width,.525*-Label.height,1.1*metrics.width,1.05*Label.height),background.endFill(),background.alpha=Label.config.backgroundOpacity,background.tint=bgColor,this.background=background;const text=new PIXI.Text(label,Label.style);text.resolution=window.devicePixelRatio,text.tint=fontColor,text.anchor.set(.5),this.text=text,container.addChild(background,text)}get visible(){return this.container.visible}set visible(flag){this.container.visible=flag&&Label.state.visible}set fontColor(color){this.text.tint=color}get attachToRoot(){return this._attachToRoot}set attachToRoot(val){val!==this._attachToRoot&&(this._attachToRoot=val)}getBoundingBox(){const{y:y,width:width,height:height}=this.container,x=this.container.x-width/2;return new PIXI.Rectangle(x,y,width,height)}}class RootData{static state={rootRadius:1,maxScale:1};mesh;wellbores=[];position;labelIndex=0;rootLabelsBBox=null;target=null;constructor(position){this.position=position;const shader=RootShader.get();this.mesh=generateCircle(position,RootData.state.maxScale,shader)}get active(){return this.target&&this.target.active}updateLabelsBBox(label){const bbox=label.getBoundingBox();this.rootLabelsBBox?(this.rootLabelsBBox.height=bbox.y+bbox.height-this.rootLabelsBBox.y,bbox.width>this.rootLabelsBBox.width&&(this.rootLabelsBBox.x=bbox.x,this.rootLabelsBBox.width=bbox.width)):this.rootLabelsBBox=bbox}positionLabel(wellbore){if(wellbore.label.attachToRoot){!function(wellbore,position){wellbore.label.attachToRoot=!0;const{container:container}=wellbore.label,{scale:scale,rootDisplacement:rootDisplacement}=Label.state;container.rotation=0,container.pivot.set(0,.5*-Label.height);const yPos=rootDisplacement+5*scale+position*(Label.height+5)*scale+wellbore.root.position[1];container.position.set(wellbore.root.position[0],yPos),container.scale.set(scale)}(wellbore,this.labelIndex++),this.updateLabelsBBox(wellbore.label)}else!function(wellbore){wellbore.label.attachToRoot=!1;const{container:container,metrics:metrics}=wellbore.label,end=wellbore.interpolator.GetPoint(1).position,width=metrics.width*Label.state.scale,start=wellbore.interpolator.GetPointFromEnd(width),dir=Vector2.sub(end,start.position).mutable;let pivotX,pivotY,angle,pos;const mirror=!!wellbore.group?.mirrorLabels;dir.x<0?mirror?(pivotX=.5*-metrics.width,pivotY=.5*metrics.height,angle=Vector2.signedAngle(Vector2.left,dir),pos=dir.rotate90().rescale(.5*wellbore.wellboreWidth+.075).add(end)):(pivotX=.5*-metrics.width,pivotY=.5*-metrics.height,angle=Vector2.signedAngle(Vector2.left,dir),pos=dir.rotate270().rescale(.5*wellbore.wellboreWidth+.075).add(end)):mirror?(pivotX=.5*metrics.width,pivotY=.5*metrics.height,angle=Vector2.signedAngle(Vector2.right,dir),pos=dir.rotate270().rescale(.5*wellbore.wellboreWidth+.075).add(end)):(pivotX=.5*metrics.width,pivotY=.5*-metrics.height,angle=Vector2.signedAngle(Vector2.right,dir),pos=dir.rotate90().rescale(.5*wellbore.wellboreWidth+.075).add(end)),container.position.set(pos[0],pos[1]),container.pivot.set(pivotX,pivotY),container.rotation=angle,container.scale.set(Label.state.scale)}(wellbore)}append(wellbore){this.wellbores.push(wellbore),wellbore.active&&(this.target?wellbore.order<this.target.order&&wellbore.status>this.target.status?this.recalculate(!0):Label.state.visible&&this.positionLabel(wellbore):this.recalculate(!0))}recalculate(labelUpdate=!1){this.updateTarget(),this.updateUniforms(),labelUpdate&&this.updateLabels()}updateTarget(){let target,smallest=Number.MAX_VALUE;for(let i=0;i<this.wellbores.length;i++){const wellbore=this.wellbores[i];if(!wellbore.active)continue;if(wellbore.selected){target=wellbore;break}const weighted=wellbore.order-1e6*wellbore.status;weighted<smallest&&(smallest=weighted,target=wellbore)}this.target=target}updateUniforms(){const uniform=this.mesh.shader.uniforms;if(uniform.active=this.active,this.target){const color=this.target.color;uniform.circleColor1=color.col1,uniform.circleColor2=color.col2,this.mesh.zIndex=this.target.status}uniform.rootRadius=RootData.state.rootRadius}updateLabels(){this.labelIndex=0,this.rootLabelsBBox=null,Label.state.visible&&this.wellbores.forEach((wellbore=>{wellbore.active&&this.positionLabel(wellbore)}))}setLabelVisibility(visible){visible?this.updateLabels():this.rootLabelsBBox=null,this.wellbores.forEach((wellbore=>{wellbore.active&&(wellbore.label.visible=visible)}))}}class LineInterpolator{amount;length;singlePoint=!0;path;constructor(points,radius){const amount=points.length,path=new Array(amount),root=points[0];let initDir;initDir=points.length>=2?Vector2.sub(points[1],points[0]).normalize():Vector2.right,path[0]={point:root,direction:initDir,distance:0,relative:0};let length=0;for(let i=1;i<amount;i++){const point=points[i];length+=Vector2.distance(point,path[i-1].point),path[i]={point:point,direction:this.GetDirection(points,i),distance:length,relative:0},Vector2.distance(point,root)>radius&&(this.singlePoint=!1)}for(let i=1;i<amount;i++){const p=path[i];p.relative=0===length?0:p.distance/length}this.amount=amount,this.length=length,this.path=path}GetPoint(relative){if(this.singlePoint)return{position:this.path[0].point,direction:Vector2.up,distance:0};if(relative<0){const first=this.path[0];return{position:first.point,direction:first.direction,distance:0}}if(relative>=1){const last=this.path[this.amount-1];return{position:last.point,direction:last.direction,distance:this.length}}const base=this.GetClosestPointBelow(relative),prev=this.path[base],cur=this.path[base+1],dist=cur.relative-prev.relative,frac=(relative-prev.relative)/dist;return{position:mix(prev.point,cur.point,frac,Vector2.zero),direction:Vector2.lerpRot(prev.direction,cur.direction,frac).normalize(),distance:prev.distance*(1-frac)+cur.distance*frac}}GetSection(relativeStart,relativeEnd){if(this.singlePoint)return[{position:this.path[0].point,direction:Vector2.up,distance:0},{position:this.path[0].point,direction:Vector2.up,distance:0}];if(relativeStart>=1){const last=this.path[this.path.length-1];return[{position:last.point,direction:last.direction,distance:this.length},{position:last.point,direction:last.direction,distance:this.length}]}const base=this.GetClosestPointBelow(relativeStart),points=[],prev=this.path[base],cur=this.path[base+1],dist=cur.relative-prev.relative,frac=(relativeStart-prev.relative)/dist;points.push({position:mix(prev.point,cur.point,frac,Vector2.zero),direction:Vector2.lerpRot(prev.direction,cur.direction,frac).normalize(),distance:prev.distance*(1-frac)+cur.distance*frac});for(let i=base+1;i<this.amount;i++){const cur=this.path[i];if(cur.relative>=relativeEnd){const cur=this.path[i],prev=this.path[i-1],dist=cur.relative-prev.relative,frac=(relativeEnd-prev.relative)/dist;points.push({position:mix(prev.point,cur.point,frac,Vector2.zero),direction:Vector2.lerpRot(prev.direction,cur.direction,frac).normalize(),distance:prev.distance*(1-frac)+cur.distance*frac});break}points.push({position:cur.point,direction:this.path[i].direction,distance:cur.distance})}return points}GetClosestPointBelow(relative){let base=0,range=this.amount,idx=Math.floor(.5*range);for(;range>1;)relative<this.path[idx].relative?(range=Math.floor(.5*range),idx=base+Math.floor(.5*range)):(base+=Math.floor(.5*range),range=Math.ceil(.5*range),idx=base+Math.floor(.5*range));return base}GetPointFromStart(distance){const relative=distance/this.length;return this.GetPoint(relative)}GetPointFromEnd(distance){const relative=1-distance/this.length;return this.GetPoint(relative)}GetRangeFromStart(relative,width,resolution=10){const relativeDisp=(relative+width/this.length-relative)/resolution,points=[];for(let i=0;i<=resolution;i++)points.push(this.GetPoint(relative+relativeDisp*i));return points}GetDirection(points,idx){const end=points.length-1;if(0===idx)return Vector2.sub(points[1],points[0]).normalize();if(idx===end)return Vector2.sub(points[end],points[end-1]).normalize();{const cur=points[idx],to=Vector2.sub(cur,points[idx-1]),from=Vector2.sub(points[idx+1],cur);return Vector2.lerpRot(to,from,.5).normalize()}}}class WellboreMesh{interp;thickness;baseTris;tick;constructor(interp,thickness,tick){this.interp=interp,this.thickness=thickness,this.baseTris=0,this.tick=tick}generate(intervals=[]){const vertices=[],triangles=[],vertexData=[],extraData=[];if(intervals.length<=0){const path=this.interp.GetSection(0,1);this.appendSegment(path,0,vertices,triangles,vertexData,extraData)}else if(intervals.length>0){let p=0;intervals.forEach((i=>{const path1=this.interp.GetSection(p,i[0]);this.appendSegment(path1,0,vertices,triangles,vertexData,extraData);const path2=this.interp.GetSection(i[0],i[1]);this.appendSegment(path2,1,vertices,triangles,vertexData,extraData),p=i[1]}));const end=intervals[intervals.length-1][1];if(end<1){const lastPath=this.interp.GetSection(end,1);this.appendSegment(lastPath,0,vertices,triangles,vertexData,extraData)}}return intervals.forEach((i=>{const p1=this.interp.GetPoint(i[0]);if(this.generateCrossline(p1,vertices,triangles,vertexData,extraData),Math.abs(i[0]-i[1])<.001)return;const p2=this.interp.GetPoint(i[1]);this.generateCrossline(p2,vertices,triangles,vertexData,extraData)})),{vertices:vertices,triangles:triangles,vertexData:vertexData,extraData:extraData}}appendSegment(section,type,vertices,triangles,vertexData,extraData){const mesh=Mesh.WellboreSegment(section,this.thickness,type);vertices.push(...mesh.vertices),mesh.triangles.forEach((d=>triangles.push(d+this.baseTris))),vertexData.push(...mesh.vertexData),extraData.push(...mesh.extraData),this.baseTris+=mesh.vertices.length/2}generateCrossline(p,vertices,triangles,vertexData,extraData){const px=p.position[0],py=p.position[1],crosslinesWidth=this.tick.width,dirX=p.direction[0]*crosslinesWidth,dirY=p.direction[1]*crosslinesWidth,crosslinesHeight=this.tick.height,normX=-p.direction[1]*crosslinesHeight,normY=p.direction[0]*crosslinesHeight;vertices.push(px-dirX-normX,py-dirY-normY,px+dirX-normX,py+dirY-normY,px-dirX+normX,py-dirY+normY,px+dirX+normX,py+dirY+normY),triangles.push(this.baseTris,this.baseTris+2,this.baseTris+3,this.baseTris,this.baseTris+3,this.baseTris+1),extraData.push(2,2,2,2);const normalizedNormal=new Vector2(normX,normY).normalized(),nnx=normalizedNormal.x,nny=normalizedNormal.y;vertexData.push(p.distance,0,-nnx,-nny,p.distance,0,-nnx,-nny,p.distance,1,nnx,nny,p.distance,1,nnx,nny),this.baseTris+=4}}var WellboreStatus,FilterStatus,ColorType;!function(WellboreStatus){WellboreStatus[WellboreStatus.normal=0]="normal",WellboreStatus[WellboreStatus.highlighted=1]="highlighted",WellboreStatus[WellboreStatus.multiHighlighted=2]="multiHighlighted",WellboreStatus[WellboreStatus.selected=3]="selected"}(WellboreStatus||(WellboreStatus={})),function(FilterStatus){FilterStatus[FilterStatus.none=0]="none",FilterStatus[FilterStatus.soft=1]="soft",FilterStatus[FilterStatus.hard=2]="hard"}(FilterStatus||(FilterStatus={}));class WellboreData{static state={wellboreRadius:1,rootRadius:1};data;group;wellboreWidth;interpolator;container;label;_zIndex=0;details;detailsDict={};mesh;root;status=WellboreStatus.normal;filter=FilterStatus.none;constructor(input){if(this.data=input.data,this.group=input.group,this.root=input.root,this.wellboreWidth=input.wellboreWidth,this.interpolator=new LineInterpolator(input.coords,input.pointThreshold),this.label=new Label(input.data.labelShort,this.colors.fontColor,this.colors.default.labelBg),this.interpolator.singlePoint)this.label.attachToRoot=!0;else{this.container=new PIXI.Container;const intervals=function(intervals){let output=intervals.map((i=>[i.l1,i.l2])).sort(((a,b)=>a[0]<b[0]?-1:a[0]>b[0]?1:0));return output.length>0&&(output=function(intervals){const output=[];let prev=intervals[0].slice(0);for(let i=1;i<intervals.length;i++){const cur=intervals[i].slice(0);cur[0]<prev[1]?cur[1]>prev[1]&&(prev[1]=cur[1]):(output.push(prev),prev=cur)}return output.push(prev),output}(output)),output}(input.data.intervals);this.details=new PIXI.Container,this.mesh=this.createWellboreMesh(intervals,input.tick),this.container.addChild(this.details,this.mesh)}this.update()}set zIndex(val){this._zIndex=val,this.container&&(this.container.zIndex=this._zIndex)}get colors(){return this.group.colors}get color(){const{colors:colors}=this.group;switch(this.status){case WellboreStatus.normal:return colors.default;case WellboreStatus.highlighted:return colors.highlight;case WellboreStatus.multiHighlighted:return colors.multiHighlight;case WellboreStatus.selected:return colors.selected}}get active(){const activeUniform=this.mesh&&0===this.mesh.shader.uniforms.status;return this.group.active&&(activeUniform||this.filter===FilterStatus.none)}tryDrawDetail(key,detail){if(!this.container)return;const relative=detail.getRelative(this.data);if(!Array.isArray(relative)||0===relative.length)return;const container=new PIXI.Container;container.visible=detail.visible,relative.forEach((p=>{const graphics=detail.getGraphics(p,this.interpolator);container.addChild(graphics)})),this.details.addChild(container),this.detailsDict[key]=container}setFilter(filter){this.filter!==filter&&(this.filter=filter,this.update())}get selected(){return this.status===WellboreStatus.selected}get highlighted(){return this.status===WellboreStatus.highlighted||this.status===WellboreStatus.multiHighlighted}get order(){return this.group.order}get uniforms(){return this.mesh.shader.uniforms}createWellboreMesh(intervals,tick){const line=new WellboreMesh(this.interpolator,this.wellboreWidth,tick),{vertices:vertices,triangles:triangles,vertexData:vertexData,extraData:extraData}=line.generate(intervals),geometry=new PIXI.Geometry;geometry.addAttribute("verts",vertices,2),geometry.addAttribute("vertCol",vertexData,4),geometry.addAttribute("typeData",extraData,1),geometry.addIndex(triangles);const shader=WellboreShader.get(this.colors.default,this.group.state.completionVisible,this.group.state.wellboreVisible);return new PIXI.Mesh(geometry,shader)}setCompletionVisibility(visible){this.mesh&&(this.uniforms.completionVisible=visible)}setWellboreVisibility(visible){this.mesh&&(this.uniforms.wellboreVisible=visible)}setDetailsVisibility(key,visible){key in this.detailsDict&&(this.detailsDict[key].visible=visible)}setHighlight(isHighlighted,multiple=!1){if(this.status!==WellboreStatus.selected)if(this.status=isHighlighted?multiple?WellboreStatus.multiHighlighted:WellboreStatus.highlighted:WellboreStatus.normal,isHighlighted){const color=multiple?this.colors.multiHighlight:this.colors.highlight;this.mesh&&(this.mesh.shader.uniforms.wellboreColor1=color.col1,this.mesh.shader.uniforms.wellboreColor2=color.col2,this.container.zIndex=this._zIndex+1e5),this.label.container.zIndex=1,this.label.background.tint=color.labelBg,this.label.background.alpha=.75,this.label.fontColor=this.colors.interactFontColor}else this.mesh&&(this.mesh.shader.uniforms.wellboreColor1=this.colors.default.col1,this.mesh.shader.uniforms.wellboreColor2=this.colors.default.col2,this.container.zIndex=this._zIndex),this.label.container.zIndex=0,this.label.background.tint=this.colors.default.labelBg,this.label.background.alpha=Label.config.backgroundOpacity,this.label.fontColor=this.colors.fontColor}setSelected(isSelected){this.status=isSelected?WellboreStatus.selected:WellboreStatus.normal,isSelected?(this.mesh&&(this.mesh.shader.uniforms.wellboreColor1=this.colors.selected.col1,this.mesh.shader.uniforms.wellboreColor2=this.colors.selected.col2,this.container.zIndex=this._zIndex+1e6),this.label.container.zIndex=1,this.label.background.tint=this.colors.selected.labelBg,this.label.background.alpha=.75):(this.mesh&&(this.mesh.shader.uniforms.wellboreColor1=this.colors.default.col1,this.mesh.shader.uniforms.wellboreColor2=this.colors.default.col2,this.container.zIndex=this._zIndex),this.label.container.zIndex=0,this.label.background.tint=this.colors.default.labelBg,this.label.background.alpha=Label.config.backgroundOpacity),this.label.fontColor=this.colors.fontColor,this.root.recalculate()}update(){if(this.group.active){const noFilter=this.filter===FilterStatus.none;this.container&&(this.container.visible=!0,this.mesh.shader.uniforms.status=this.filter,this.mesh.shader.uniforms.wellboreRadius=WellboreData.state.wellboreRadius,this.mesh.shader.uniforms.rootRadius=WellboreData.state.rootRadius,this.details.visible=noFilter),this.label.visible=noFilter}else this.container&&(this.container.visible=!1,this.details.visible=!1),this.label.visible=!1}}function getDefaultColors(input){const output={fontColor:0,interactFontColor:16777215,default:{col1:[.3,.3,.3],col2:[.05,.05,.05],labelBg:16777215},highlight:{col1:[.8,.2,.9],col2:[.5,.05,.6],labelBg:10685091},multiHighlight:{col1:[.55,.55,.55],col2:[.3,.3,.3],labelBg:6710886},selected:{col1:[1,0,0],col2:[.5,0,0],labelBg:16777215}};if(!input)return output;function transfer(key){isNaN(input[key])||(output[key]=input[key])}function transferColor(color){const inputCol1=input[`${color}Color1`],inputCol2=input[`${color}Color2`],inputLabelBg=input[`${color}LabelBg`],outputColor=output[color];inputCol1&&(outputColor.col1=inputCol1),inputCol2&&(outputColor.col2=inputCol2),inputLabelBg&&(outputColor.labelBg=inputLabelBg)}return transfer("fontColor"),transfer("interactFontColor"),transferColor("default"),transferColor("highlight"),transferColor("multiHighlight"),transferColor("selected"),output}!function(ColorType){ColorType[ColorType.Default=0]="Default",ColorType[ColorType.Highlight=1]="Highlight",ColorType[ColorType.MultiHighlight=2]="MultiHighlight",ColorType[ColorType.Selected=3]="Selected"}(ColorType||(ColorType={}));class Detail{initialized=!1;visible=!1;getData;color;group;constructor(options,group="default"){this.getData=options.getData,this.color=new PIXI.Color(options.color||[0,0,0]),this.group=group}getRelative(wellbore){return this.getData(wellbore,this.group)}}class ShoeDetail extends Detail{widthTop;widthBottom;constructor(options,group="default"){super(options,group),this.widthTop=options.widthTop||1,this.widthBottom=options.widthBottom||1}getGraphics([top,bottom],interpolator){const from=interpolator.GetPoint(top).position,to=interpolator.GetPoint(bottom).position,normal=Vector2.sub(to,from).rotate90().normalize(),normalTop=normal.scale(this.widthTop),normalBottom=normal.scale(this.widthBottom),from1=Vector2.add(from,normalTop),from2=Vector2.sub(from,normalTop),to1=Vector2.add(to,normalBottom),to2=Vector2.sub(to,normalBottom);return(new PIXI.Graphics).beginFill(this.color,1).lineStyle(0).moveTo(from1.x,from1.y).lineTo(to1.x,to1.y).lineTo(to2.x,to2.y).lineTo(from2.x,from2.y).endFill()}}class Group{key;colors;order=0;mirrorLabels=!1;wellbores=[];details={};active=!0;activeFilter=null;isHardFilter;state={completionVisible:!0,wellboreVisible:!0};constructor(key,options){this.key=key,options?(isNaN(options.order)||(this.order=options.order),options.mirrorLabels&&(this.mirrorLabels=options.mirrorLabels),this.colors=getDefaultColors(options.colors)):this.colors=getDefaultColors()}registerDetail(key,detail){if(key in this.details)throw Error(`Detail already registered, ${key}, for group: ${this.key}!`);this.details[key]=((options,group="default")=>{const{shape:shape}=options;return new ShoeDetail(options,group)})(detail)}setDetailVisibility(key,visible){if(key in this.details){const detail=this.details[key];if(visible&&!detail.initialized&&(this.wellbores.forEach((wellbore=>{wellbore.tryDrawDetail(key,detail)})),detail.initialized=!0),visible===detail.visible)return;this.wellbores.forEach((wellbore=>{wellbore.setDetailsVisibility(key,visible)})),detail.visible=visible}}resetDetails(){Object.values(this.details).forEach((detail=>{detail.initialized=!1}))}append(wellbore){if(wellbore.zIndex=1e4*this.order+this.wellbores.length,this.activeFilter){const targetFilter=this.isHardFilter?FilterStatus.hard:FilterStatus.soft;wellbore.setFilter(this.activeFilter(wellbore.data)?FilterStatus.none:targetFilter),wellbore.root.recalculate(!0)}Object.entries(this.details).forEach((([key,detail])=>{detail.initialized&&wellbore.tryDrawDetail(key,detail)})),this.wellbores.push(wellbore)}forAll(wellboreFunc,rootFunc){const roots=new Set,wellbores=this.wellbores;for(let i=0;i<wellbores.length;i++){const wellbore=wellbores[i];wellboreFunc(wellbore),roots.add(wellbore.root)}roots.forEach((root=>rootFunc(root)))}setActive(active){this.active!==active&&(this.active=active,this.forAll((wellbore=>wellbore.update()),(root=>root.recalculate(!0))))}softFilter(filter){this.activeFilter=filter,this.isHardFilter=!1,this.forAll((wellbore=>wellbore.setFilter(filter(wellbore.data)?FilterStatus.none:FilterStatus.soft)),(root=>root.recalculate(!0)))}hardFilter(filter){this.activeFilter=filter,this.isHardFilter=!0,this.forAll((wellbore=>wellbore.setFilter(filter(wellbore.data)?FilterStatus.none:FilterStatus.hard)),(root=>root.recalculate(!0)))}clearFilter(){this.activeFilter=null,this.forAll((wellbore=>wellbore.setFilter(FilterStatus.none)),(root=>root.recalculate(!0)))}setCompletionVisibility(visible){this.state.completionVisible=visible,this.wellbores.forEach((wellbore=>{wellbore.setCompletionVisibility(visible)}))}setWellboreVisibility(visible){this.state.wellboreVisible=visible,this.wellbores.forEach((wellbore=>{wellbore.setWellboreVisibility(visible)}))}}class WellboreEventData{group;data;mouseEvent;constructor(group,data){this.group=group,this.data=data}static from(wellbore){return new WellboreEventData(wellbore.group.key,wellbore.data)}}class HighlightEvent{changed;originalEvent;eventData;constructor(eventData,changed,originalEvent){this.eventData=eventData,this.changed=changed,this.originalEvent=originalEvent}static from(wellbores,changed,originalEvent){return new HighlightEvent(wellbores.map((w=>new WellboreEventData(w.group.key,w.data))),changed,originalEvent)}get count(){return this.eventData.length}}function distanceToLine(point,lineStart,lineEnd){const lineDir=Vector2.sub(lineEnd,lineStart),lineAngle=Vector2.angleRight(lineDir),len=lineDir.magnitude,dir=Vector2.sub(point,lineStart).mutable.rotate(-lineAngle);return dir[0]<0?dir.magnitude:dir[0]>len?Vector2.distance(point,lineEnd):Math.abs(dir.y)}class LineDictionary{gridsize;tiles=new Map;lineValues=new Map;testActiveFunction;lineSeq;constructor(gridsize=10,testActive){this.gridsize=gridsize,this.lineSeq=0,this.testActiveFunction=testActive}add(points,value,id){const lineID=Number.isFinite(id)?id:++this.lineSeq,line={id:lineID,value:value,segments:[]};this.lineValues.set(lineID,line);for(let i=1;i<points.length;i++){const p1=points[i-1],p2=points[i];this.addSegment(p1[0],p1[1],p2[0],p2[1],line)}return line}addSegment(x1,y1,x2,y2,line){const segment={lineID:line.id,geometry:{x1:x1,y1:y1,x2:x2,y2:y2}};line.segments.push(segment);const intersections=function(x1,x2,y1,y2,gridsize){const intersections=new Set;let downwards,xMin,xMax,yMin,yMax,m,y0;if(x1<x2){xMin=Math.floor(x1/gridsize),xMax=Math.floor(x2/gridsize),m=(y2-y1)/(x2-x1),y0=(y1-x1*m)/gridsize,downwards=y2<y1;const key=`${Math.floor(x1/gridsize)}.${Math.floor(y1/gridsize)}`;intersections.add(key)}else{xMin=Math.floor(x2/gridsize),xMax=Math.floor(x1/gridsize),m=(y1-y2)/(x1-x2),y0=(y2-x2*m)/gridsize,downwards=y1<y2;const key=`${Math.floor(x2/gridsize)}.${Math.floor(y2/gridsize)}`;intersections.add(key)}y1<y2?(yMin=Math.floor(y1/gridsize),yMax=Math.floor(y2/gridsize)):(yMin=Math.floor(y2/gridsize),yMax=Math.floor(y1/gridsize));for(let x=xMin+1;x<=xMax;x++){const y=y0+x*m;intersections.add(`${x}.${Math.floor(y)}`)}for(let y=yMin+1;y<=yMax;y++){const x=(y-y0)/m;intersections.add(`${Math.floor(x)}.${Math.floor(downwards?y-1:y)}`)}return intersections}(x1,x2,y1,y2,this.gridsize);intersections.forEach((key=>{this.tiles.has(key)?this.tiles.get(key).push(segment):this.tiles.set(key,[segment])}))}getClosest(target,maxDist=1){const segments=this.getSegmentsOn3Grid(target);if(0===segments.size)return;let minDist=1/0,minLineID=-1;return segments.forEach((seg=>{const dist=distanceToLine(target,new Vector2(seg.geometry.x1,seg.geometry.y1),new Vector2(seg.geometry.x2,seg.geometry.y2));dist<minDist&&(minDist=dist,minLineID=seg.lineID)})),minDist>maxDist*this.gridsize?void 0:this.lineValues.get(minLineID).value}getAllClosest(target,epsilon=0,maxDist=1,filter){const segments=this.getSegmentsOn3Grid(target);if(0===segments.size)return[];let minDist=1/0,minID=-1,extraLines=[];if(segments.forEach((seg=>{const distance=distanceToLine(target,new Vector2(seg.geometry.x1,seg.geometry.y1),new Vector2(seg.geometry.x2,seg.geometry.y2));if(distance<minDist+epsilon)if(distance<minDist){const upperLimit=distance+epsilon,newLines=[];minDist<=upperLimit&&newLines.push({ID:minID,distance:minDist}),extraLines.forEach((d=>{d.distance<=upperLimit&&newLines.push(d)})),extraLines=newLines,minDist=distance,minID=seg.lineID}else extraLines.push({ID:seg.lineID,distance:distance})})),minDist>maxDist*this.gridsize)return[];const unique={[minID]:!0},uniqueLines=[];extraLines.forEach((d=>{unique.hasOwnProperty(d.ID)||(unique[d.ID]=!0,uniqueLines.push(d))}));const minT=this.lineValues.get(minID).value;let extraT=uniqueLines.map((d=>this.lineValues.get(d.ID).value));if(filter){const filtered=[];extraT.forEach((curT=>{filter(minT,curT)&&filtered.push(curT)})),extraT=filtered}return[minT,...extraT]}isActive(line){return!this.testActiveFunction||line&&this.testActiveFunction(line.value)}getSegmentsOn3Grid(target){const gridSegments=new Set,keyX=Math.floor(target[0]/this.gridsize),keyY=Math.floor(target[1]/this.gridsize);for(let x=-1;x<=1;x++)for(let y=-1;y<=1;y++){const key=`${keyX+x}.${keyY+y}`;this.tiles.has(key)&&this.tiles.get(key).forEach((tileSegment=>{gridSegments.has(tileSegment)||this.isActive(this.lineValues.get(tileSegment.lineID))&&gridSegments.add(tileSegment)}))}return gridSegments}clear(filter){if(filter){const segmentsToDelete=new Set;this.lineValues.forEach(((line,key)=>{filter(line.value,key)&&(line.segments.forEach(segmentsToDelete.add,segmentsToDelete),this.lineValues.delete(key))})),segmentsToDelete.size>0&&this.tiles.forEach(((list,key)=>{const filtered=list.filter((s=>!segmentsToDelete.has(s)));filtered.length>0?this.tiles.set(key,filtered):this.tiles.delete(key)}))}else this.tiles=new Map,this.lineValues=new Map}}class PointDictionary{distThreshold;gridSize;radius;tiles=new Map;pointValues=new Map;testActiveFunction;pointSeq=0;constructor(distThreshold,gridSize=2,radius,testActive){if(gridSize<radius)throw"Gridsize of point dictionary must be greater than scaled radius of root.";this.distThreshold=distThreshold,this.gridSize=gridSize,this.radius=radius,this.testActiveFunction=testActive}add(pos,val){const id=++this.pointSeq,point={val:val,pos:pos,id:id};this.pointValues.set(id,point);const keys=this.getKeys(pos);for(let i=0;i<keys.length;i++){const key=keys[i];if(this.tiles.has(key))this.tiles.get(key).set(id,point);else{const map=new Map;map.set(id,point),this.tiles.set(key,map)}}return id}getKeys(pos){const{radius:radius,gridSize:gridSize}=this,keyX=Math.floor(pos[0]/gridSize),keyY=Math.floor(pos[1]/gridSize),keys=[`${keyX}.${keyY}`],localX=pos[0]-keyX*gridSize,localY=pos[1]-keyY*gridSize,local=[localX,localY],addKey=(deltaX,deltaY)=>{keys.push(`${keyX+deltaX}.${keyY+deltaY}`)},tryAddDiagKey=(cornerLocal,deltaX,deltaY)=>{Vector2.distance(local,cornerLocal)<radius&&keys.push(`${keyX+deltaX}.${keyY+deltaY}`)};let l=!1,r=!1,d=!1,u=!1;return localX<radius&&(addKey(-1,0),l=!0),localX>gridSize-radius&&(addKey(1,0),r=!0),localY<radius&&(addKey(0,-1),d=!0),localY>gridSize-radius&&(addKey(0,1),u=!0),l?u?tryAddDiagKey([0,gridSize],-1,1):d&&tryAddDiagKey([0,0],-1,-1):r&&(u?tryAddDiagKey([gridSize,gridSize],1,1):d&&tryAddDiagKey([gridSize,0],1,-1)),keys}isActive(point){return!this.testActiveFunction||point&&this.testActiveFunction(point.val)}getKey(position){return`${Math.floor(position[0]/this.gridSize)}.${Math.floor(position[1]/this.gridSize)}`}getOverlapping(pos){const key=this.getKey(pos);if(!this.tiles.has(key))return null;const points=Array.from(this.tiles.get(key).values());for(let i=0;i<points.length;i++){const point=points[i];if(Vector2.distance(pos,point.pos)<this.distThreshold)return point}return null}getClosestUnder(pos,radius=this.radius){const key=this.getKey(pos);let minDist=1/0,closest=null;return this.tiles.has(key)?(this.tiles.get(key).forEach((point=>{const distance=Vector2.distance(pos,point.pos);this.isActive(point)&&distance<radius&&distance<minDist&&(minDist=distance,closest=point)})),closest):null}clear(filter){filter?this.pointValues.forEach((point=>{if(!filter(point.val,point.id))return;const keys=this.getKeys(point.pos);for(let i=0;i<keys.length;i++){const key=keys[i],tile=this.tiles.get(key);tile.delete(point.id),0===tile.size&&this.tiles.delete(key)}this.pointValues.delete(point.id)})):(this.tiles=new Map,this.pointValues=new Map)}}class Projector{project;constructor(project){this.project=project}get(coord,zoom){return this.project(coord,zoom)}getVector2(coord,zoom){return new Vector2(this.project(coord,zoom))}batch(coords,zoom){const output=new Array(coords.length);for(let i=0;i<coords.length;i++)output[i]=this.project(coords[i],zoom);return output}batchVector2(coords,zoom){const output=new Array(coords.length);for(let i=0;i<coords.length;i++)output[i]=new Vector2(this.project(coords[i],zoom));return output}}function forceHighlight(module,wellbore){const{highlight:highlight}=module,root=wellbore.root,wellbores=[wellbore];highlight&&!highlight.equals(root,wellbores)&&(highlight.set(root,wellbores),module.requestRedraw())}function clearHighlight(module,onHighlightOff){module.highlight.clear(),onHighlightOff&&onHighlightOff(),module.requestRedraw()}class Highlight{active=!1;root;wellbores;get single(){return 1===this.wellbores.length}get first(){return this.wellbores[0]}set(root,wellbores){if(!this.active)return this.root=root,this.wellbores=wellbores,this.highlightWellbores(),this.highlightRoot(),void(this.active=!0);this.root!==root||this.wellbores.length!==wellbores.length?(this.clear(),this.root=root,this.wellbores=wellbores,this.highlightWellbores(),this.highlightRoot()):(this.clearWellbores(),this.wellbores=wellbores,this.highlightWellbores()),this.active=!0}highlightRoot(){this.root.recalculate(!1)}highlightWellbores(){const multiple=this.wellbores.length>1;for(let i=0;i<this.wellbores.length;i++)this.wellbores[i].setHighlight(!0,multiple)}clear(){this.active&&(this.clearWellbores(),this.clearRoot(),this.active=!1)}clearRoot(){this.root.recalculate(!1),delete this.root}clearWellbores(){for(let i=0;i<this.wellbores.length;i++)this.wellbores[i].setHighlight(!1);delete this.wellbores}equals(root,wellbores){if(this.root!==root)return!1;if(this.wellbores.length!==wellbores.length)return!1;for(let i=0;i<this.wellbores.length;i++){const wellbore=this.wellbores[i];if(!wellbores.includes(wellbore))return!1}return!0}}class AsyncLoop{timers={};Start(key,config,interval=3){this.Stop(key);const{iterations:iterations,batchSize:batchSize,func:func,postFunc:postFunc,endFunc:endFunc}=config;let front=0;const batch=()=>{if(front>=iterations)return delete this.timers[key],void(endFunc&&endFunc());const tail=Math.min(front+batchSize,iterations);for(let i=front;i<tail;i++)func(i);postFunc&&postFunc(),front+=batchSize,this.timers[key]=setTimeout(batch,interval)};this.timers[key]=setTimeout(batch,interval)}Stop(key){key in this.timers&&(clearTimeout(this.timers[key]),delete this.timers[key])}StopAll(){const keys=Object.keys(this.timers);for(let i=0;i<keys.length;i++){const key=keys[i];clearTimeout(this.timers[key]),delete this.timers[key]}}}class DefaultEventHandler{map;element;callbacks;register(map,element,callbacks){this.map=map,this.element=element,this.callbacks=callbacks,element.addEventListener("mousemove",this.callbacks.mousemove),element.addEventListener("mouseout",this.callbacks.mouseout),element.addEventListener("click",this.callbacks.click),element.addEventListener("mousedown",this.callbacks.mousedown),element.addEventListener("mouseup",this.callbacks.mouseup)}unregister(){const{element:element}=this;element.removeEventListener("mousemove",this.callbacks.mousemove),element.removeEventListener("mouseout",this.callbacks.mouseout),element.removeEventListener("click",this.callbacks.click),element.removeEventListener("mousedown",this.callbacks.mousedown),element.removeEventListener("mouseup",this.callbacks.mouseup),this.map=null,this.element=null}}class RealtimeWellbore{map;root;prevCoords=[Number.MIN_VALUE,Number.MIN_VALUE];constructor(mapInput,wellbore){if(mapInput.utils&&"getMap"in mapInput.utils){const pixiOverlay=mapInput;this.map=pixiOverlay.utils.getMap()}else this.map=mapInput;this.root=wellbore.data.path[0]}get pixelCoordinates(){const{map:map}=this,containerPoint=map.latLngToContainerPoint(this.root),rect=map.getContainer().getBoundingClientRect(),coords=[rect.x+containerPoint.x,rect.y+containerPoint.y];return this.prevCoords=coords,coords}getPixelCoordinates(){const{prevCoords:prevCoords}=this,coords=this.pixelCoordinates;return{coords:coords,changed:!this.coordinatesEqual(coords,prevCoords,1e-5)}}coordinatesEqual(c1,c2,delta){return!(Math.abs(c1[0]-c2[0])>delta)&&!(Math.abs(c1[1]-c2[1])>delta)}}class WellboreModule extends ModuleInterface{config;groups={};roots=[];asyncLoop=new AsyncLoop;lineDict;pointDict;highlight=new Highlight;currentZoom=20;_deferredSelector;_deferredSelectorKeys=null;_projector;_eventHandler;_redrawAnimFrame=null;containers;scaling;marker;constructor(inputConfig