UNPKG

@soonspacejs/plugin-heat-map

Version:

Haet-map plugin for SoonSpace.js

2 lines (1 loc) 18.6 kB
import{Matrix4 as t,Vector2 as e,Shape as i,ShapeGeometry as a,Box2 as n,Plane as s,Vector3 as r,Matrix3 as o,PlaneGeometry as h,CanvasTexture as d,MeshStandardMaterial as l,Mesh as c,Box3 as u,DoubleSide as p}from"three";"function"==typeof SuppressedError&&SuppressedError;var m={defaultRadius:40,defaultGradient:{.25:"rgb(0,0,255)",.55:"rgb(0,255,0)",.85:"yellow",1:"rgb(255,0,0)"},defaultMaxOpacity:1,defaultMinOpacity:0,defaultBlur:.85,defaultXField:"x",defaultYField:"y",defaultValueField:"value",plugins:{}},g=function(){function t(){this.eStore={}}return t.prototype.on=function(t,e,i){this.eStore[t]||(this.eStore[t]=[]),this.eStore[t].push(function(t){return e.call(i,t)})},t.prototype.emit=function(t,e){this.eStore[t]&&this.eStore[t].forEach(function(t){return t(e)})},t}(),v=function(){function t(t){this.coordinator=new g,this.data=[],this.radi=[],this.min=10,this.max=1,this.xField=t.xField||m.defaultXField,this.yField=t.yField||m.defaultYField,this.valueField=t.valueField||m.defaultValueField,this.radius=t.radius||m.defaultRadius}return t.prototype._organiseData=function(t,e){var i=t[this.xField],a=t[this.yField],n=this.radi,s=this.data,r=this.max,o=this.min,h=t[this.valueField]||1,d=t.radius||this.radius;n[i]||(s[i]=[],n[i]=[]),n[i][a]?s[i][a]+=h:(s[i][a]=h,n[i][a]=d);var l=s[i][a];return l?l>r?(e?this.setDataMax(l):this.max=l,!1):l<o?(e?this.setDataMin(l):this.min=l,!1):void 0:{x:i,y:a,value:h,radius:d,min:o,max:r}},t.prototype._unOrganizeData=function(){for(var t=[],e=0;e<this.radi.length;e++)for(var i=0;i<this.radi[e].length;i++)t.push({x:e,y:i,radius:this.radi[e][i],value:this.radi[e][i]});return{min:this.min,max:this.max,data:t}},t.prototype._onExtremaChange=function(){this.coordinator.emit("extremachange",{min:this.min,max:this.max})},t.prototype.addData=function(t){var e=this._organiseData(t,!0);e&&(0===this.data.length&&(this.min=e.value,this.max=e.value),this.coordinator.emit("renderpartial",{min:this.min,max:this.max,data:[e]}))},t.prototype.setData=function(t){var e=t.data;this.data=[],this.radi=[];for(var i=0;i<e.length;i++)this._organiseData(e[i],!1);return this.min=t.min||0,this.max=t.max||100,this._onExtremaChange(),this.coordinator.emit("renderall",this._getInternalData()),this},t.prototype.setDataMax=function(t){return this.max=t,this._onExtremaChange(),this.coordinator.emit("renderall",this._getInternalData()),this},t.prototype.setDataMin=function(t){return this.min=t,this._onExtremaChange(),this.coordinator.emit("renderall",this._getInternalData()),this},t.prototype._getInternalData=function(){return{max:this.max,min:this.min,data:this.data,radi:this.radi}},t.prototype.getData=function(){return this._unOrganizeData()},t}(),y=function(){function t(t){this.canvas=t.canvas||document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.shadowCanvas=t.shadowCanvas||document.createElement("canvas"),this.shadowCtx=this.shadowCanvas.getContext("2d"),this.width=t.width||512,this.height=t.height||512,this.max=100,this.min=1,this.blur=1,this.opacity=1,this.maxOpacity=1,this.minOpacity=0,this.useGradientOpacity=!1,this.canvas.style.cssText=this.shadowCanvas.style.cssText="position:absolute;left:0;top:0;",t.container&&(t.container.style.position="relative",t.container.appendChild(this.canvas)),this.renderBoundaries=[1e4,1e4,0,0],this.palette=this._getColorPalette(t),this.templates=[],this._setStyles(t)}return t.prototype.renderPartial=function(t){t.data.length>0&&(this._drawAlpha(t),this._colorize())},t.prototype.renderAll=function(t){this._clear(),t.data.length>0&&(this._drawAlpha(this._prepareData(t)),this._colorize())},t.prototype.updateConfig=function(t){t.gradient&&this._updateGradient(t),this._setStyles(t)},t.prototype.setDimensions=function(t,e){this.width=this.canvas.width=this.shadowCanvas.width=t,this.height=this.canvas.height=this.shadowCanvas.height=e},t.prototype.getValueAt=function(t){if(!this.shadowCtx)return 0;var e=this.shadowCtx.getImageData(t.x,t.y,1,1);return Math.abs(this.max-this.min)*(e.data[3]/255)|0},t.prototype.getDataURL=function(){return this.canvas.toDataURL()},t.prototype._getColorPalette=function(t){var e=t.gradient||m.defaultGradient,i=document.createElement("canvas"),a=i.getContext("2d");if(i.width=256,i.height=1,!a)return new Uint8ClampedArray(1024);var n=a.createLinearGradient(0,0,256,1);for(var s in e)n.addColorStop(Number(s),e[s]);return a.fillStyle=n,a.fillRect(0,0,256,1),a.getImageData(0,0,256,1).data},t.prototype._getPointTemplate=function(t,e){var i=document.createElement("canvas"),a=i.getContext("2d");if(!a)return i;var n=t,s=t;if(i.width=i.height=2*t,1===e)a.beginPath(),a.arc(n,s,t,0,2*Math.PI,!1),a.fillStyle="rgba(0,0,0,1)",a.fill();else{var r=a.createRadialGradient(n,s,t*e,n,s,t);r.addColorStop(0,"rgba(0,0,0,1)"),r.addColorStop(1,"rgba(0,0,0,0)"),a.fillStyle=r,a.fillRect(0,0,2*t,2*t)}return i},t.prototype._prepareData=function(t){for(var e=[],i=t.min,a=t.max,n=t.radi,s=t.data,r=Object.keys(s),o=r.length;o--;)for(var h=r[o],d=Object.keys(s[h]),l=d.length;l--;){var c=d[l],u=s[h][c],p=n[h][c];e.push({x:Number(h),y:Number(c),value:u,radius:p})}return{min:i,max:a,data:e}},t.prototype._setStyles=function(t){this.blur=0===t.blur?0:t.blur||m.defaultBlur,t.backgroundColor&&(this.canvas.style.backgroundColor=t.backgroundColor),this.width=this.canvas.width=this.shadowCanvas.width=t.width||this.width,this.height=this.canvas.height=this.shadowCanvas.height=t.height||this.height,this.opacity=255*(t.opacity||0),this.maxOpacity=255*(t.maxOpacity||m.defaultMaxOpacity),this.minOpacity=255*(t.minOpacity||m.defaultMinOpacity),this.useGradientOpacity=!!t.useGradientOpacity},t.prototype._updateGradient=function(t){this.palette=this._getColorPalette(t)},t.prototype._drawAlpha=function(t){for(var e=this.min=t.min||0,i=this.max=t.max||100,a=t.data||[],n=a.length,s=1-this.blur;n--;){var r=a[n],o=r.x,h=r.y,d=r.radius,l=Math.min(r.value,i),c=o-d,u=h-d;if(!this.shadowCtx)return;var p=void 0;this.templates[d]?p=this.templates[d]:this.templates[d]=p=this._getPointTemplate(d,s);var m=(l-e)/(i-e);this.shadowCtx.globalAlpha=m<.01?.01:m,this.shadowCtx.drawImage(p,c,u),c<this.renderBoundaries[0]&&(this.renderBoundaries[0]=c),u<this.renderBoundaries[1]&&(this.renderBoundaries[1]=u),c+2*d>this.renderBoundaries[2]&&(this.renderBoundaries[2]=c+2*d),u+2*d>this.renderBoundaries[3]&&(this.renderBoundaries[3]=u+2*d)}},t.prototype._colorize=function(){var t=this.renderBoundaries[0],e=this.renderBoundaries[1],i=this.renderBoundaries[2]-t,a=this.renderBoundaries[3]-e,n=this.width,s=this.height;if(t<0&&(t=0),e<0&&(e=0),t+i>n&&(i=n-t),e+a>s&&(a=s-e),this.ctx&&this.shadowCtx){for(var r=this.shadowCtx.getImageData(t,e,i,a),o=3;o<r.data.length;o+=4){var h,d=r.data[o],l=4*d;l&&(h=this.opacity>0?this.opacity:d<this.maxOpacity?d<this.minOpacity?this.minOpacity:d:this.maxOpacity,r.data[o-3]=this.palette[l],r.data[o-2]=this.palette[l+1],r.data[o-1]=this.palette[l+2],r.data[o]=this.useGradientOpacity?this.palette[l+3]:h)}this.ctx.putImageData(r,t,e),this.renderBoundaries=[1e3,1e3,0,0]}},t.prototype._clear=function(){this.ctx&&this.shadowCtx&&(this.ctx.clearRect(0,0,this.width,this.height),this.shadowCtx.clearRect(0,0,this.width,this.height))},t}(),f=function(){function t(t){this.config=t,this.renderer=new y(this.config),this.store=new v(this.config),this._init()}return t.prototype._init=function(){var t=this;this.store.coordinator.on("renderpartial",this.renderer.renderPartial,this.renderer),this.store.coordinator.on("renderall",this.renderer.renderAll,this.renderer),this.store.coordinator.on("extremachange",function(e){t.config.onExtremaChange&&t.config.onExtremaChange({min:e.min,max:e.max,gradient:t.config.gradient||m.defaultGradient})})},t.prototype.addData=function(t){return this.store.addData(t),this},t.prototype.setData=function(t){return this.store.setData(t),this},t.prototype.setDataMaxx=function(t){return this.store.setDataMax(t),this},t.prototype.setDataMin=function(t){return this.store.setDataMin(t),this},t.prototype.repaint=function(){return this.store.coordinator.emit("renderall",this.store._getInternalData()),this},t.prototype.getData=function(){return this.store.getData()},t.prototype.getDataURL=function(){return this.renderer.getDataURL()},t.prototype.getValueAt=function(t){return this.renderer.getValueAt(t)},t}();function x(h){const d=function(t){const[e,i,a]=t,n=new s;n.setFromCoplanarPoints(i,e,a);const h=n.normal,d=h.clone().cross(new r(0,0,1));d.equals(new r)&&d.set(1,0,0);const l=h.clone().cross(d);d.normalize(),l.normalize(),h.normalize();const c=new o;return c.elements=[d.x,d.y,d.z,l.x,l.y,l.z,h.x,h.y,h.z],c}(h),l=new t;l.setFromMatrix3(d);const c=h[0],u=l.clone();u.setPosition(c);const p=u.clone().invert(),m=h.map(t=>{const i=t.clone().applyMatrix4(p);return new e(i.x,i.y)}),g=new i(m),v=new a(g),y=new n;y.setFromPoints(m);const f=function(t){const i=t.min,a=t.getSize(new e),n=new o;return n.elements=[a.x,0,0,0,a.y,0,i.x,i.y,1],n.invert()}(y);return v.getAttribute("uv").applyMatrix3(f),v.applyMatrix4(l),{geometry:v,polygonBox:y,modelMatrix:l,planeMatrix:u,projectionMatrix:p,position:c}}function w(t,e,i,a,n,s){const{min:o,max:h,radius:d,beforePointUpdate:l,value:c,distanceInterval:u}=i,p=n.object,m=s.viewport.getIntersects(t,[p]);if(!((null==m?void 0:m.length)>0))return null;const g=m[0].point.clone();if(a.length>0&&u){const{x:t,y:e,z:i}=a[a.length-1];if(u>new r(t,e,i).distanceTo(g))return null}const v=Array.isArray(c)?function(t,e){if(t>=e)throw new Error("Min value must be less than max value.");return Math.floor(Math.random()*(e-t+1))+t}(c[0],c[1]):c,y=Object.assign(Object.assign({},g),{radius:d,value:v}),f=null==l?void 0:l(e,y,a);return!1===f?null:"object"==typeof f?f:y}class P{constructor(t,e){this.heatMapPlugin=t,this.store=new Map,this.intervalId=null,this.currentMouseEvent=null,this.events={},this.isDragging=!1,this.isStarting=!1,this.dataPoints=[],this.start=()=>{const{addTriggerType:t,doneTriggerType:e,undoTriggerType:i}=this.createDrawingParam;return new Promise((a,n)=>{this.startResolve=a,this.startReject=n,this.isStarting?console.warn("请先取消绘制再调用start方法"):(this.isStarting=!0,this.handleEventListeners("add",t,this.handleAddPoint),this.handleEventListeners("add",e,this.done),this.handleEventListeners("add",i,this.popPoint))})},this.cancel=()=>{var t;const{id:e}=this.createDrawingParam,i=[...this.dataPoints];this.heatMapPlugin.setDataPolygon(e,[]),this.remove(),null===(t=this.startReject)||void 0===t||t.call(this,i)},this.dispose=()=>{const{id:t}=this.createDrawingParam;this.remove(),this.heatMapPlugin.removeById(t)},this.done=()=>{var t;const e=[...this.dataPoints];this.remove(),null===(t=this.startResolve)||void 0===t||t.call(this,e)},this.remove=()=>{const{addTriggerType:t,doneTriggerType:e,undoTriggerType:i}=this.createDrawingParam;this.dataPoints=[],this.lastTime=void 0,this.setMouseEvent(null),this.handleEventListeners("remove",t,this.handleAddPoint),this.handleEventListeners("remove",e,this.done),this.handleEventListeners("remove",i,this.popPoint),this.events={},this.isDragging=!1,this.ssp.controls.enabled=!0,this.isStarting=!1},this.pushPoint=t=>{if(!t)return;const{id:e,onAdd:i}=this.createDrawingParam,a=t;this.dataPoints.push(a),this.heatMapPlugin.setDataPolygon(e,this.dataPoints),null==i||i(a,this.dataPoints)},this.popPoint=()=>{var t;const e=null===(t=this.dataPoints)||void 0===t?void 0:t.length;if(!(e>0))return;const{id:i,beforePointUpdate:a,onUndo:n}=this.createDrawingParam,s=null==a?void 0:a("undo",this.dataPoints[e-1],this.dataPoints);if(!1===s)return;"object"==typeof s&&(this.dataPoints[e-1]=s);const r=this.dataPoints.pop();r&&(this.heatMapPlugin.setDataPolygon(i,this.dataPoints),null==n||n(r,this.dataPoints))},this.handleAddPoint=t=>{if(!(null==t?void 0:t.clientX))throw new Error("addTriggerType仅支持:time、drag、click、dblClick、rightClick、mouseDown、mouseMove、mouseUp、mouseWheel");this.setMouseEvent(t);const e=this.getPoint(t,"add");this.pushPoint(e)},this.setMouseEvent=t=>{this.currentMouseEvent=t},this.handleEventListeners=(t,e,i)=>{(null==e?void 0:e.length)>0&&(e.forEach(e=>{var a,n,s,r;if("object"!=typeof e)if("time"!==e){if("drag"===e){const a=this.ssp.viewport.container,n=e;return void("add"===t?(this.events[n]=t=>{this.onPointerMove(i,t)},a.addEventListener("pointerdown",this.onPointerdown),a.addEventListener("pointermove",this.events[n]),a.addEventListener("pointerup",this.onPointerUp)):(a.removeEventListener("pointerdown",this.onPointerdown),a.removeEventListener("pointermove",this.events[n]),a.removeEventListener("pointerup",this.onPointerUp)))}null===(r=null===(s=this.ssp.signals[e])||void 0===s?void 0:s[t])||void 0===r||r.call(s,i)}else if("add"===t){const{timeInterval:t}=this.createDrawingParam||{};this.ssp.signals.mouseMove.add(this.setMouseEvent),this.intervalId=window.setInterval(()=>{this.currentMouseEvent&&i(this.currentMouseEvent)},t)}else this.ssp.signals.mouseMove.remove(this.setMouseEvent);else for(const[s,r]of Object.entries(e)){const e=`${s}_${r.join("_")}`;"add"===t&&(this.events[e]=t=>{this.keyEvents(r,i,t)}),null===(n=null===(a=this.ssp.signals[s])||void 0===a?void 0:a[t])||void 0===n||n.call(a,this.events[e])}}),"remove"===t&&this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null))},this.onPointerdown=t=>{this.ssp.controls.enabled=!1,this.isDragging=!0},this.onPointerMove=(t,e)=>{e.preventDefault(),this.isDragging&&t(e)},this.onPointerUp=t=>{this.isDragging=!1,this.ssp.controls.enabled=!0};const{data:i=[]}=e;this.ssp=t.ssp,this.store=t.store,this.createDrawingParam=e,this.dataPoints=[...i],this.object=t.createPolygon(e)}keyEvents(t,e,i){(null==t?void 0:t.includes(i.code))&&e()}getPoint(t,e){const{id:i,timeInterval:a=100}=this.createDrawingParam;if(this.lastTime&&a&&a>(new Date).getTime()-this.lastTime.getTime())return null;this.lastTime=new Date;const n=this.store.get(i);return w(t,e,this.createDrawingParam,this.dataPoints,n,this.ssp)}}class D{constructor(t){this.ssp=t,this.store=new Map,this.maxCanvasSize=512,this.hmInstance=null}create(t){const{id:e,name:i,yAxisHeight:a,minPosition:n,maxPosition:s,data:o,min:u=0,max:p=100,radius:m=100,canvasScalar:g=1}=t,v=new r((s.x+n.x)/2,a,(s.z+n.z)/2),y=s.x-n.x,f=s.z-n.z,x=this._formatCanvasSize(y*g,f*g),{canvas:w,hmInstance:P}=this.createInitData(Object.assign(Object.assign({},x),{radius:m}));P.setData({max:p,min:u,data:this._formatData(o,n,{width:y,height:f},x)});const D=new h(y,f),b=new d(w),C=new l({map:b,depthWrite:!1,transparent:!0}),M=new c(D,C),O=this.ssp.createPluginObject({id:e,name:i,position:v.clone(),rotation:{x:-Math.PI/2,y:0,z:0}},M);return this.store.set(e,{object:O,canvas:w,param:Object.assign(Object.assign({},t),{min:u,max:p}),width:y,height:f}),O}createPolygon(t){const{id:i,name:a,points:n,data:s,min:o=0,max:h=100,radius:c=100}=t,m=n.map(t=>new r(t.x,t.y,t.z)),{geometry:g,projectionMatrix:v,polygonBox:y,position:f}=x(m);(new u).setFromPoints(m);const{x:w,y:P}=y.getSize(new e),D=this._formatCanvasSize(w,P),{canvas:b,hmInstance:C}=this.createInitData(Object.assign(Object.assign({},D),{radius:c}));s&&s.length>0&&C.setData({max:h,min:o,data:this._formatData_Polygon(s,v,y,D)});const M=new d(b),O=new l({map:M,depthWrite:!1,transparent:!0,side:p}),_=new this.ssp.library.BaseMesh({id:`${i}_mesh`,name:a},g,O);_.renderOrder=0;const I=this.ssp.createPluginObject({id:i,name:a,position:f},_);return this.store.set(i,{object:I,canvas:b,param:Object.assign(Object.assign({},t),{min:o,max:h}),width:w,height:P,projectionMatrix:v,polygonBox:y,position:f.clone()}),I}createDrawing(t){const{data:e=[],addTriggerType:i=["click","mouseMove"],doneTriggerType:a=["dblClick",{keyDown:["Enter"]}],undoTriggerType:n=["rightClick",{keyDown:["Backspace"]}],timeInterval:s=100,min:r=0,max:o=100,radius:h=100,value:d=[r,o],distanceInterval:l=5}=t,c=function(t,e){var i={};for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&e.indexOf(a)<0&&(i[a]=t[a]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var n=0;for(a=Object.getOwnPropertySymbols(t);n<a.length;n++)e.indexOf(a[n])<0&&Object.prototype.propertyIsEnumerable.call(t,a[n])&&(i[a[n]]=t[a[n]])}return i}(t,["data","addTriggerType","doneTriggerType","undoTriggerType","timeInterval","min","max","radius","value","distanceInterval"]),u=Object.assign({data:e,addTriggerType:i,doneTriggerType:a,undoTriggerType:n,timeInterval:s,min:r,max:o,radius:h,value:d,distanceInterval:l},c);return new P(this,u)}setData(t,e){const i=this.store.get(t);if(i){const{object:t,canvas:a,param:{minPosition:n,min:s,max:r},width:o,height:h}=i,l=this.createInitData(),{canvas:c,hmInstance:u}=l;u.renderer.updateConfig({width:a.width,height:a.height}),u.setData({max:r,min:s,data:this._formatData(e,n,{width:o,height:h},this._formatCanvasSize(o,h))});const p=t.children[0].material;return this.ssp.render(()=>{const t=new d(c);p.map&&p.map.dispose(),p.map=t}),t}return console.warn(`In soonspacejs: 插件(plugin-heat-map)未找到 id 为 '"${t}"' 的热力图对象!`)}setDataPolygon(t,i){const a=this.store.get(t);if(a){const{object:n,canvas:s,param:{min:o,max:h},projectionMatrix:l,polygonBox:c,position:u}=a;if(!l)throw new Error(`${t} 不是多边形热力图类型`);const p=this.getById(t),m=null==p?void 0:p.getWorldPosition(new r),g=this.createInitData(),{canvas:v,hmInstance:y}=g;y.renderer.updateConfig({width:s.width,height:s.height});const f=c.getSize(new e);y.setData({max:h,min:o,data:this._formatData_Polygon(i,l,c,this._formatCanvasSize(f.x,f.y),u,m)});const x=n.children[0].material;return this.ssp.render(()=>{const t=new d(v);x.map&&x.map.dispose(),x.map=t}),n}return console.warn(`In soonspacejs: 插件(plugin-heat-map)未找到 id 为 '"${t}"' 的热力图对象!`)}getById(t){return this.ssp.getObjectById(t)}getByName(t){return this.ssp.getObjectByName(t)}removeById(t){return!!this.store.has(t)&&(this.ssp.removeObjectById(t),this.store.delete(t),!0)}createInitData(t){const e=this.hmInstance=new f(t||{});return{hmInstance:e,canvas:e.renderer.canvas}}_formatCanvasSize(t,e){const i=t/e;return t>this.maxCanvasSize&&(e=(t=this.maxCanvasSize)/i),e>this.maxCanvasSize&&(t=i*(e=this.maxCanvasSize)),{width:t,height:e}}_formatData(t,e,i,a){return t.map(t=>Object.assign(Object.assign({},t),{x:Math.trunc((t.x-e.x)/i.width*a.width),y:Math.trunc((t.z-e.z)/i.height*a.height)}))}_formatData_Polygon(t,i,a,n,s,o){const h=s&&o?o.clone().sub(s):new r(0,0,0);return t.map(t=>{const s=new r(t.x,t.y,t.z);s.sub(h),s.applyMatrix4(i);const{x:o,y:d}=a.getParameter(new e(s.x,s.y),new e);return Object.assign(Object.assign({},t),{x:Math.trunc(o*n.width),y:Math.trunc((1-d)*n.height)})})}}export{D as default};