UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

6 lines (5 loc) 10.2 kB
/* All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://js.arcgis.com/4.33/esri/copyright.txt for details. */ import{_ as e}from"../../../chunks/tslib.es6.js";import t from"../../../core/Accessor.js";import{createTask as n}from"../../../core/asyncUtils.js";import i from"../../../core/Evented.js";import"../../../core/has.js";import{throwIfAborted as s,isAborted as a}from"../../../core/promiseUtils.js";import{watch as r,syncAndInitial as o,sync as p}from"../../../core/reactiveUtils.js";import{property as d}from"../../../core/accessorSupport/decorators/property.js";import"../../../core/Logger.js";import"../../../core/RandomLCG.js";import{subclass as c}from"../../../core/accessorSupport/decorators/subclass.js";import{project as h,initializeProjection as l}from"../../../geometry/projectionUtils.js";import{absoluteHeightElevationInfo as u}from"../../../support/elevationInfoUtils.js";import{fromPoint as f,markAsTarget as g,toElevationAlignedDehydratedPoint as _}from"../sketch/normalizedPoint.js";import{defaults as S}from"./Settings.js";import{SnappingDomain as m}from"./SnappingDomain.js";import v from"./SnappingOptions.js";import{sortCandidatesInPlace as y,squaredScreenDistance as C}from"./snappingUtils.js";import{DrapedEdgeSnappingCandidate as w}from"./candidates/DrapedEdgeSnappingCandidate.js";import{EdgeSnappingCandidate as E}from"./candidates/EdgeSnappingCandidate.js";import{IntersectionSnappingCandidate as P}from"./candidates/IntersectionSnappingCandidate.js";import{LineSnappingCandidate as T}from"./candidates/LineSnappingCandidate.js";import{ParallelLineSnappingCandidate as j}from"./candidates/ParallelLineSnappingCandidate.js";import{RightAngleSnappingCandidate as M,SelfSnappingRightAngleType as R}from"./candidates/RightAngleSnappingCandidate.js";import{RightAngleTriangleSnappingCandidate as q}from"./candidates/RightAngleTriangleSnappingCandidate.js";import{vectorToScreenPoint as A}from"../support/viewUtils.js";import{loadGeodesicLengthMeasurementUtils as I}from"../../support/geodesicLengthMeasurementUtils.js";let x=class extends(i.EventedMixin(t)){constructor(e){super(e),this.options=new v,this._engineCache=new Map,this._loadTask=null,this._engines=[],this._currentMainCandidate=null,this._currentOtherActiveCandidates=[],this._currentSnappedType=b.MAIN}initialize(){this.addHandles([r((()=>{const{distance:e,touchSensitivityMultiplier:t,effectiveSelfEnabled:n,effectiveFeatureEnabled:i,effectiveGridEnabled:s}=this.options;return{selfEnabled:n,featureEnabled:i,gridEnabled:"2d"===this.view.type&&s,viewReady:this.view.ready,viewSpatialReference:this.view.spatialReference,distance:e,touchSensitivityMultiplier:t}}),((e,t)=>{t&&(this.doneSnapping(),this.emit("changed")),this._loadTask?.abort(),this._loadTask=n((n=>this._updateEngines(e,t,n)))}),o),r((()=>this.options),(e=>{for(const t of this._engines)t.options=e}),p)])}destroy(){this._loadTask?.abort(),this._destroyEngines()}get updating(){return this._engines.some((e=>e.updating))||!this._loadTask?.finished}_destroyEngines(){this._engineCache.forEach((e=>e.destroy())),this._engineCache.clear(),this._engines=[]}async _updateEngines(e,t,n){if(!e.viewReady)return void this._destroyEngines();t?.viewSpatialReference!==e.viewSpatialReference&&this._destroyEngines();const i=this._engineCache,s=await Promise.allSettled([e.featureEnabled&&!i.has("feature")?this._createFeatureSnappingEngine(n):void 0,e.selfEnabled&&!i.has("self")?this._createSelfSnappingEngine(n):void 0,e.gridEnabled&&!i.has("grid")?this._createGridSnappingEngine(n):void 0]);if(n.aborted)for(const a of s)"fulfilled"===a.status&&a.value?.engine.destroy();else{for(const e of s)"fulfilled"===e.status&&e.value&&i.set(e.value.type,e.value.engine);this._engines=Array.from(i.values())}}async _createSelfSnappingEngine(e){const[{SelfSnappingEngine:t},n]=await Promise.all([import("./SelfSnappingEngine.js"),I()]);return s(e),{type:"self",engine:new t({view:this.view,options:this.options,geodesicLengthMeasurementUtils:n})}}async _createGridSnappingEngine(e){const{view:t}=this;if("2d"!==t.type)return;const{GridSnappingEngine:n}=await import("./GridSnappingEngine.js");return s(e),{type:"grid",engine:new n({view:t,options:this.options})}}async _createFeatureSnappingEngine(e){const{FeatureSnappingEngine:t}=await import("./FeatureSnappingEngine.js");s(e);const{view:n,options:i}=this,{spatialReference:a}=n;return{type:"feature",engine:new t({view:n,options:i,spatialReference:a})}}get _squaredMouseProximityThreshold(){return this.options.distance*this.options.distance}get _squaredTouchProximityThreshold(){const{distance:e,touchSensitivityMultiplier:t}=this.options,n=e*t;return n*n}snap(e){return k(e)?this._snapMultiPoint(e):this._snapSinglePoint(e)}update(e){const{point:t,context:n}=e;this._removeVisualization();const i=this._currentMainCandidate;if(null==i)return t;const s=this._selectUpdateInput(e);if(null==s)return t;const{spatialReference:a}=n,r=h(s,a);if(null==r)return t;const{view:o}=this,{elevationInfo:p,visualizer:d}=n,c=[],l=f(r,o,p),u=i.constraint.closestTo(l);if(!this._arePointsWithinScreenThreshold(l,u,n)||!L(i,n.drawConstraints))return this._resetSnappingState(),t;i.targetPoint=g(u),c.push(...i.hints);for(const h of this._currentOtherActiveCandidates)L(h,n.drawConstraints)&&(h.targetPoint=g(u),c.push(...h.hints));return null!=d&&this.addHandles(d.draw(c,{spatialReference:a,elevationInfo:H(n),view:o,selfSnappingZ:n.selfSnappingZ}),z),_(u,o,t,n)}doneSnapping(){this._removeVisualization(),this._resetSnappingState()}_selectUpdateInput({point:e,scenePoint:t}){switch(this._currentSnappedType){case b.MAIN:return e;case b.SCENE:return t}}_resetSnappingState(){this._currentMainCandidate=null,this._currentOtherActiveCandidates=[],this._currentSnappedType=b.MAIN}_removeVisualization(){this.removeHandles(z)}async _snapSinglePoint({point:e,context:t,signal:n}){const{view:i}=this,{elevationInfo:s}=t,a=f(e,i,s),r=await this._fetchCandidates(a,m.ALL,t,n);return this._createSnapResult(a,b.MAIN,r,i,e,t,n)}async _snapMultiPoint({point:e,scenePoint:t,context:n,signal:i}){const{view:s}=this,{coordinateHelper:a,elevationInfo:r,spatialReference:o}=n;await l(t.spatialReference,o);const p=h(t,o),d=f(p,s,r),c=await this._fetchCandidates(d,m.FEATURE,n,i);if(c.length>0){const e=await this._fetchCandidates(d,m.SELF,n,i);return this._createSnapResult(d,b.SCENE,[...c,...e],s,p,n,i)}const u=f(e,s,r),g=await this._fetchCandidates(u,m.SELF,n,i);return this._createSnapResult(u,b.MAIN,g,s,{z:a.hasZ()&&e.hasZ?e.z??0:void 0,m:a.hasM()&&e.hasM?e.m??0:void 0},n,i)}async _fetchCandidates(e,t,n,i){return(await Promise.all(this._engines.map((s=>s.fetchCandidates(e,t,n,i))))).flat()}_createSnapResult(e,t,n,i,s,r,o){return{get valid(){return!a(o)},apply:()=>{const{spatialReference:a}=r,{snappedPoint:o,hints:p}=this._processCandidates(e,t,n,r);return this._removeVisualization(),null!=r.visualizer&&this.addHandles(r.visualizer.draw(p,{spatialReference:a,elevationInfo:u,view:i,selfSnappingZ:r.selfSnappingZ}),z),_(o,i,s,r)}}}_processCandidates(e,t,n,i){if(n.length<1)return this.doneSnapping(),{snappedPoint:e,hints:[]};this._currentSnappedType!==t&&this._resetSnappingState(),y(e,n);const s=this._currentMainCandidate;if(null!=s){const a=N(s,n);if(a>=0){if(!(n[a]instanceof P))return this._intersectWithOtherCandidates(a,n,e,t,i);if(this._arePointsWithinScreenThreshold(e,s.targetPoint,i))return this._updateSnappingCandidate(s,t,n,i)}}return this._intersectWithOtherCandidates(0,n,e,t,i)}_intersectWithOtherCandidates(e,t,n,i,s){const{coordinateHelper:a}=s,r=t[e],o=[];for(let p=0;p<t.length;++p){if(p===e)continue;const i=t[p],s=r.constraint.intersect(i.constraint);if(s)for(const e of s.closestPoints(r.targetPoint))o.push([new P(g(e),r,i,i.isDraped),this._squaredScreenDistance(n,e,a)])}return o.length>0&&(o.sort(((e,t)=>e[1]-t[1])),o[0][1]<this._squaredPointProximityThreshold(s.pointer))?this._updateSnappingCandidate(o[0][0],i,t,s):L(r,s.drawConstraints)?this._updateSnappingCandidate(r,i,t,s):{snappedPoint:n,hints:[]}}_updateSnappingCandidate(e,t,n,i){this.doneSnapping(),this._currentMainCandidate=e,this._currentSnappedType=t;const s=this._currentMainCandidate.targetPoint,a=[];a.push(...e.hints);for(const r of n){if(e instanceof P){if(r.constraint.equals(e.first.constraint)||r.constraint.equals(e.second.constraint))continue}else if(r.constraint.equals(e.constraint))continue;const t=r.constraint.closestTo(s);this._squaredScreenDistance(t,s,i.coordinateHelper)<U()&&(r.targetPoint=s,this._currentOtherActiveCandidates.push(r),a.push(...r.hints))}return{snappedPoint:s,hints:a}}_squaredPointProximityThreshold(e){return"touch"===e?this._squaredTouchProximityThreshold:this._squaredMouseProximityThreshold}_arePointsWithinScreenThreshold(e,t,n){return this._squaredScreenDistance(e,t,n.coordinateHelper)<this._squaredPointProximityThreshold(n.pointer)}_squaredScreenDistance(e,t,n){return C(this._toScreen(e,n),this._toScreen(t,n))}_toScreen(e,t){return A(e,t.spatialReference,u,this.view)}get test(){}};var b;e([d({constructOnly:!0})],x.prototype,"view",void 0),e([d()],x.prototype,"options",void 0),e([d({readOnly:!0})],x.prototype,"updating",null),e([d()],x.prototype,"_loadTask",void 0),e([d()],x.prototype,"_engines",void 0),e([d()],x.prototype,"_squaredMouseProximityThreshold",null),e([d()],x.prototype,"_squaredTouchProximityThreshold",null),x=e([c("esri.views.interactive.snapping.SnappingManager")],x),function(e){e[e.MAIN=0]="MAIN",e[e.SCENE=1]="SCENE"}(b||(b={}));const z="visualization-handle";function U(){return S.satisfiesConstraintScreenThreshold*S.satisfiesConstraintScreenThreshold}function L(e,t){return!t||null==t.direction&&null==t.distance||!(e instanceof w||e instanceof E||e instanceof T||e instanceof j||e instanceof q)&&(!(e instanceof M)||null==t.direction&&e.selfSnappingType===R.LastVertex)}function N(e,t){return e instanceof P?O(t,e.first)>=0&&O(t,e.second)>=0?0:-1:O(t,e)}function O(e,t){let n=-1;for(let i=0;i<e.length;++i)if(t.constraint.equals(e[i].constraint)){n=i;break}return n}function k(e){return null!=e.scenePoint}function H({coordinateHelper:e,elevationInfo:t}){return e.hasZ()?u:t}export{x as SnappingManager};