UNPKG

three-globe

Version:

Globe data visualization as a ThreeJS reusable 3D object

1,314 lines (1,251 loc) 205 kB
import { BackSide, BufferAttribute, Color, Mesh, ShaderMaterial, Group, LineBasicMaterial, LineSegments, MeshPhongMaterial, SphereGeometry, SRGBColorSpace, TextureLoader, BufferGeometry, CylinderGeometry, Matrix4, MeshLambertMaterial, Object3D, Vector3, CubicBezierCurve3, Curve, Line, NormalBlending, QuadraticBezierCurve3, TubeGeometry, DoubleSide, MeshBasicMaterial, CircleGeometry, Euler, BoxGeometry, Camera, Frustum, Vector2 } from 'three'; import Kapsule from 'kapsule'; import { Tween, Easing, Group as Group$1 } from '@tweenjs/tween.js'; import SlippyMap from 'three-slippy-map-globe'; import GeoJsonGeometry from 'three-geojson-geometry'; import { geoGraticule10, geoDistance as geoDistance$1, geoInterpolate } from 'd3-geo'; import * as _bfg from 'three/addons/utils/BufferGeometryUtils.js'; import accessorFn from 'accessor-fn'; import { color } from 'd3-color'; import tinyColor from 'tinycolor2'; import DataBindMapper from 'data-bind-mapper'; import _FrameTicker from 'frame-ticker'; import { scaleLinear, scaleQuantize } from 'd3-scale'; import ConicPolygonGeometry from 'three-conic-polygon-geometry'; import indexBy from 'index-array-by'; import { latLngToCell, cellToLatLng, cellToBoundary, polygonToCells } from 'h3-js'; import { interpolateTurbo } from 'd3-scale-chromatic'; import { sum, max } from 'd3-array'; import yaOctree from 'yaot'; import { Line2 } from 'three/addons/lines/Line2.js'; import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; import { interpolateArray } from 'd3-interpolate'; import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; import { Font } from 'three/addons/loaders/FontLoader.js'; import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } function _assertThisInitialized(e) { if (undefined === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _classPrivateFieldGet2(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _classPrivateFieldSet2(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return p; } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { writable: false }), e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; } function _get() { return _get = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) { var p = _superPropBase(e, t); if (p) { var n = Object.getOwnPropertyDescriptor(p, t); return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value; } }, _get.apply(null, arguments); } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: true, configurable: true } }), Object.defineProperty(t, "prototype", { writable: false }), e && _setPrototypeOf(t, e); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = true, o = false; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = true, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; } function _possibleConstructorReturn(t, e) { if (e && ("object" == typeof e || "function" == typeof e)) return e; if (undefined !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _superPropBase(t, o) { for (; !{}.hasOwnProperty.call(t, o) && null !== (t = _getPrototypeOf(t));); return t; } function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(t.prototype ), o, e); return "function" == typeof p ? function (t) { return p.apply(e, t); } : p; } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (undefined !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : undefined; } } var _materialDispose = function materialDispose(material) { if (material instanceof Array) { material.forEach(_materialDispose); } else { if (material.map) { material.map.dispose(); } material.dispose(); } }; var _deallocate = function deallocate(obj) { if (obj.geometry) { obj.geometry.dispose(); } if (obj.material) { _materialDispose(obj.material); } if (obj.texture) { obj.texture.dispose(); } if (obj.children) { obj.children.forEach(_deallocate); } }; var emptyObject = function emptyObject(obj) { if (obj && obj.children) while (obj.children.length) { var childObj = obj.children[0]; obj.remove(childObj); _deallocate(childObj); } }; function linkKapsule (kapsulePropName, kapsuleType) { var dummyK = new kapsuleType(); // To extract defaults return { linkProp: function linkProp(prop) { // link property config return { "default": dummyK[prop](), onChange: function onChange(v, state) { state[kapsulePropName][prop](v); }, triggerUpdate: false }; }, linkMethod: function linkMethod(method) { // link method pass-through return function (state) { var kapsuleInstance = state[kapsulePropName]; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var returnVal = kapsuleInstance[method].apply(kapsuleInstance, args); return returnVal === kapsuleInstance ? this // chain based on the parent object, not the inner kapsule : returnVal; }; } }; } var GLOBE_RADIUS = 100; function getGlobeRadius() { return GLOBE_RADIUS; } function polar2Cartesian(lat, lng) { var relAltitude = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var phi = (90 - lat) * Math.PI / 180; var theta = (90 - lng) * Math.PI / 180; var r = GLOBE_RADIUS * (1 + relAltitude); return { x: r * Math.sin(phi) * Math.cos(theta), y: r * Math.cos(phi), z: r * Math.sin(phi) * Math.sin(theta) }; } function cartesian2Polar(_ref) { var x = _ref.x, y = _ref.y, z = _ref.z; var r = Math.sqrt(x * x + y * y + z * z); var phi = Math.acos(y / r); var theta = Math.atan2(z, x); return { lat: 90 - phi * 180 / Math.PI, lng: 90 - theta * 180 / Math.PI - (theta < -Math.PI / 2 ? 360 : 0), // keep within [-180, 180] boundaries altitude: r / GLOBE_RADIUS - 1 }; } function deg2Rad$1(deg) { return deg * Math.PI / 180; } var THREE$h = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { BackSide: BackSide, BufferAttribute: BufferAttribute, Color: Color, Mesh: Mesh, ShaderMaterial: ShaderMaterial }; var vertexShader = "\nuniform float hollowRadius;\n\nvarying vec3 vVertexWorldPosition;\nvarying vec3 vVertexNormal;\nvarying float vCameraDistanceToObjCenter;\nvarying float vVertexAngularDistanceToHollowRadius;\n\nvoid main() { \n vVertexNormal\t= normalize(normalMatrix * normal);\n vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;\n \n vec4 objCenterViewPosition = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);\n vCameraDistanceToObjCenter = length(objCenterViewPosition);\n \n float edgeAngle = atan(hollowRadius / vCameraDistanceToObjCenter);\n float vertexAngle = acos(dot(normalize(modelViewMatrix * vec4(position, 1.0)), normalize(objCenterViewPosition)));\n vVertexAngularDistanceToHollowRadius = vertexAngle - edgeAngle;\n\n gl_Position\t= projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}"; var fragmentShader = "\nuniform vec3 color;\nuniform float coefficient;\nuniform float power;\nuniform float hollowRadius;\n\nvarying vec3 vVertexNormal;\nvarying vec3 vVertexWorldPosition;\nvarying float vCameraDistanceToObjCenter;\nvarying float vVertexAngularDistanceToHollowRadius;\n\nvoid main() {\n if (vCameraDistanceToObjCenter < hollowRadius) discard; // inside the hollowRadius\n if (vVertexAngularDistanceToHollowRadius < 0.0) discard; // frag position is within the hollow radius\n\n vec3 worldCameraToVertex = vVertexWorldPosition - cameraPosition;\n vec3 viewCameraToVertex\t= (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;\n viewCameraToVertex = normalize(viewCameraToVertex);\n float intensity\t= pow(\n coefficient + dot(vVertexNormal, viewCameraToVertex),\n power\n );\n gl_FragColor = vec4(color, intensity);\n}"; // Based off: http://stemkoski.blogspot.fr/2013/07/shaders-in-threejs-glow-and-halo.html function createGlowMaterial(coefficient, color, power, hollowRadius) { return new THREE$h.ShaderMaterial({ depthWrite: false, transparent: true, vertexShader: vertexShader, fragmentShader: fragmentShader, uniforms: { coefficient: { value: coefficient }, color: { value: new THREE$h.Color(color) }, power: { value: power }, hollowRadius: { value: hollowRadius } } }); } function createGlowGeometry(geometry, size) { var glowGeometry = geometry.clone(); // Resize vertex positions according to normals var position = new Float32Array(geometry.attributes.position.count * 3); for (var idx = 0, len = position.length; idx < len; idx++) { var normal = geometry.attributes.normal.array[idx]; var curPos = geometry.attributes.position.array[idx]; position[idx] = curPos + normal * size; } glowGeometry.setAttribute('position', new THREE$h.BufferAttribute(position, 3)); return glowGeometry; } var GlowMesh = /*#__PURE__*/function (_THREE$Mesh) { function GlowMesh(geometry) { var _this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$color = _ref.color, color = _ref$color === undefined ? 'gold' : _ref$color, _ref$size = _ref.size, size = _ref$size === undefined ? 2 : _ref$size, _ref$coefficient = _ref.coefficient, coefficient = _ref$coefficient === undefined ? 0.5 : _ref$coefficient, _ref$power = _ref.power, power = _ref$power === undefined ? 1 : _ref$power, _ref$hollowRadius = _ref.hollowRadius, hollowRadius = _ref$hollowRadius === undefined ? 0 : _ref$hollowRadius, _ref$backside = _ref.backside, backside = _ref$backside === undefined ? true : _ref$backside; _classCallCheck(this, GlowMesh); _this = _callSuper(this, GlowMesh); var glowGeometry = createGlowGeometry(geometry, size); var glowMaterial = createGlowMaterial(coefficient, color, power, hollowRadius); backside && (glowMaterial.side = THREE$h.BackSide); _this.geometry = glowGeometry; _this.material = glowMaterial; return _this; } _inherits(GlowMesh, _THREE$Mesh); return _createClass(GlowMesh); }(THREE$h.Mesh); var THREE$g = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { Color: Color, Group: Group, LineBasicMaterial: LineBasicMaterial, LineSegments: LineSegments, Mesh: Mesh, MeshPhongMaterial: MeshPhongMaterial, SphereGeometry: SphereGeometry, SRGBColorSpace: SRGBColorSpace, TextureLoader: TextureLoader }; // var GlobeLayerKapsule = Kapsule({ props: { globeImageUrl: {}, bumpImageUrl: {}, showGlobe: { "default": true, onChange: function onChange(showGlobe, state) { state.globeGroup.visible = !!showGlobe; }, triggerUpdate: false }, showGraticules: { "default": false, onChange: function onChange(showGraticules, state) { state.graticulesObj.visible = !!showGraticules; }, triggerUpdate: false }, showAtmosphere: { "default": true, onChange: function onChange(showAtmosphere, state) { state.atmosphereObj && (state.atmosphereObj.visible = !!showAtmosphere); }, triggerUpdate: false }, atmosphereColor: { "default": 'lightskyblue' }, atmosphereAltitude: { "default": 0.15 }, globeTileEngineUrl: { onChange: function onChange(v, state) { state.tileEngine.tileUrl = v; }, triggerUpdate: false }, globeTileEngineMaxLevel: { "default": 17, onChange: function onChange(v, state) { state.tileEngine.maxLevel = v; }, triggerUpdate: false }, updatePov: { onChange: function onChange(v, state) { state.tileEngine.updatePov(v); }, triggerUpdate: false }, onReady: { "default": function _default() {}, triggerUpdate: false } }, methods: { globeMaterial: function globeMaterial(state, _globeMaterial) { if (_globeMaterial !== undefined) { state.globeObj.material = _globeMaterial || state.defaultGlobeMaterial; return this; } return state.globeObj.material; }, _destructor: function _destructor(state) { emptyObject(state.globeObj); emptyObject(state.tileEngine); emptyObject(state.graticulesObj); } }, stateInit: function stateInit() { // create globe var globeGeometry = new THREE$g.SphereGeometry(GLOBE_RADIUS, 75, 75); var defaultGlobeMaterial = new THREE$g.MeshPhongMaterial({ color: 0x000000 }); var globeObj = new THREE$g.Mesh(globeGeometry, defaultGlobeMaterial); globeObj.rotation.y = -Math.PI / 2; // face prime meridian along Z axis // Create empty tile engine var tileEngine = new SlippyMap(GLOBE_RADIUS); // Group including globe and tile engine var globeGroup = new THREE$g.Group(); globeGroup.__globeObjType = 'globe'; // Add object type globeGroup.add(globeObj); globeGroup.add(tileEngine); // create graticules var graticulesObj = new THREE$g.LineSegments(new GeoJsonGeometry(geoGraticule10(), GLOBE_RADIUS, 2), new THREE$g.LineBasicMaterial({ color: 'lightgrey', transparent: true, opacity: 0.1 })); return { globeGroup: globeGroup, globeObj: globeObj, graticulesObj: graticulesObj, defaultGlobeMaterial: defaultGlobeMaterial, tileEngine: tileEngine }; }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; state.scene.add(state.globeGroup); // add globe state.scene.add(state.graticulesObj); // add graticules state.ready = false; }, update: function update(state, changedProps) { var globeMaterial = state.globeObj.material; // Hide globeObj if it's representing tiles state.globeObj.visible = !state.globeTileEngineUrl; if (changedProps.hasOwnProperty('globeImageUrl')) { if (!state.globeImageUrl) { // Black globe if no image !globeMaterial.color && (globeMaterial.color = new THREE$g.Color(0x000000)); } else { new THREE$g.TextureLoader().load(state.globeImageUrl, function (texture) { texture.colorSpace = THREE$g.SRGBColorSpace; globeMaterial.map = texture; globeMaterial.color = null; globeMaterial.needsUpdate = true; // ready when first globe image finishes loading (asynchronously to allow 1 frame to load texture) !state.ready && (state.ready = true) && setTimeout(state.onReady); }); } } if (changedProps.hasOwnProperty('bumpImageUrl')) { if (!state.bumpImageUrl) { globeMaterial.bumpMap = null; globeMaterial.needsUpdate = true; } else { state.bumpImageUrl && new THREE$g.TextureLoader().load(state.bumpImageUrl, function (texture) { globeMaterial.bumpMap = texture; globeMaterial.needsUpdate = true; }); } } if (changedProps.hasOwnProperty('atmosphereColor') || changedProps.hasOwnProperty('atmosphereAltitude')) { if (state.atmosphereObj) { // recycle previous atmosphere object state.scene.remove(state.atmosphereObj); emptyObject(state.atmosphereObj); } if (state.atmosphereColor && state.atmosphereAltitude) { var obj = state.atmosphereObj = new GlowMesh(state.globeObj.geometry, { color: state.atmosphereColor, size: GLOBE_RADIUS * state.atmosphereAltitude, hollowRadius: GLOBE_RADIUS, coefficient: 0.1, power: 3.5 // dispersion }); obj.visible = !!state.showAtmosphere; obj.__globeObjType = 'atmosphere'; // Add object type state.scene.add(obj); } } if (!state.ready && (!state.globeImageUrl || state.globeTileEngineUrl)) { // ready immediately if there's no globe image state.ready = true; state.onReady(); } } }); var colorStr2Hex = function colorStr2Hex(str) { return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str; }; var colorAlpha = function colorAlpha(str) { return str && isNaN(str) ? color(str).opacity : 1; }; var color2ShaderArr = function color2ShaderArr(str) { var includeAlpha = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var sRGBColorSpace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var color; var alpha = 1; var rgbaMatch = /^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.eE+-]+)\s*\)$/.exec(str.trim().toLowerCase()); if (rgbaMatch) { var _rgbaMatch$slice = rgbaMatch.slice(1), _rgbaMatch$slice2 = _slicedToArray(_rgbaMatch$slice, 4), r = _rgbaMatch$slice2[0], g = _rgbaMatch$slice2[1], b = _rgbaMatch$slice2[2], a = _rgbaMatch$slice2[3]; color = new Color("rgb(".concat(+r, ",").concat(+g, ",").concat(+b, ")")); alpha = Math.min(+a, 1); } else { color = new Color(str); } sRGBColorSpace && color.convertLinearToSRGB(); // vertexColors expects linear, but shaders expect sRGB var rgbArr = color.toArray(); return includeAlpha ? [].concat(_toConsumableArray(rgbArr), [alpha]) : rgbArr; }; function setMaterialOpacity(material, opacity, depthWrite) { material.opacity = opacity; material.transparent = opacity < 1; material.depthWrite = opacity >= 1 ; // depthWrite=false recommended for transparent materials, to prevent transparency issues https://discourse.threejs.org/t/threejs-and-the-transparent-problem/11553/31 return material; } var THREE$f = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { BufferAttribute: BufferAttribute }; function array2BufferAttr(data) { var itemSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var ArrayClass = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Float32Array; if (itemSize === 1) { // edge case handle for improved performance return new THREE$f.BufferAttribute(new ArrayClass(data), itemSize); } var ba = new THREE$f.BufferAttribute(new ArrayClass(data.length * itemSize), itemSize); for (var idx = 0, l = data.length; idx < l; idx++) { ba.set(data[idx], idx * itemSize); } return ba; } function bufferAttr2Array(ba) { var itemSize = ba.itemSize; var res = []; for (var i = 0; i < ba.count; i++) { res.push(ba.array.slice(i * itemSize, (i + 1) * itemSize)); } return res; } var _dataBindAttr = /*#__PURE__*/new WeakMap(); var _objBindAttr = /*#__PURE__*/new WeakMap(); var _removeDelay = /*#__PURE__*/new WeakMap(); var ThreeDigest = /*#__PURE__*/function (_DataBindMapper) { function ThreeDigest(scene) { var _this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$dataBindAttr = _ref.dataBindAttr, dataBindAttr = _ref$dataBindAttr === undefined ? '__data' : _ref$dataBindAttr, _ref$objBindAttr = _ref.objBindAttr, objBindAttr = _ref$objBindAttr === undefined ? '__threeObj' : _ref$objBindAttr, _ref$removeDelay = _ref.removeDelay, removeDelay = _ref$removeDelay === undefined ? 0 : _ref$removeDelay; _classCallCheck(this, ThreeDigest); _this = _callSuper(this, ThreeDigest); _defineProperty(_this, "scene", undefined); _classPrivateFieldInitSpec(_this, _dataBindAttr, undefined); _classPrivateFieldInitSpec(_this, _objBindAttr, undefined); _classPrivateFieldInitSpec(_this, _removeDelay, undefined); _this.scene = scene; _classPrivateFieldSet2(_dataBindAttr, _this, dataBindAttr); _classPrivateFieldSet2(_objBindAttr, _this, objBindAttr); _classPrivateFieldSet2(_removeDelay, _this, removeDelay); _this.onRemoveObj(function () {}); return _this; } _inherits(ThreeDigest, _DataBindMapper); return _createClass(ThreeDigest, [{ key: "onCreateObj", value: function onCreateObj(fn) { var _this2 = this; _superPropGet(ThreeDigest, "onCreateObj", this)([function (d) { var obj = fn(d); d[_classPrivateFieldGet2(_objBindAttr, _this2)] = obj; obj[_classPrivateFieldGet2(_dataBindAttr, _this2)] = d; _this2.scene.add(obj); return obj; }]); return this; } }, { key: "onRemoveObj", value: function onRemoveObj(fn) { var _this3 = this; _superPropGet(ThreeDigest, "onRemoveObj", this)([function (obj, dId) { var d = _superPropGet(ThreeDigest, "getData", _this3)([obj]); fn(obj, dId); var removeFn = function removeFn() { _this3.scene.remove(obj); emptyObject(obj); delete d[_classPrivateFieldGet2(_objBindAttr, _this3)]; }; _classPrivateFieldGet2(_removeDelay, _this3) ? setTimeout(removeFn, _classPrivateFieldGet2(_removeDelay, _this3)) : removeFn(); }]); return this; } }]); }(DataBindMapper); var THREE$e = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { BufferGeometry: BufferGeometry, Color: Color, CylinderGeometry: CylinderGeometry, Matrix4: Matrix4, Mesh: Mesh, MeshLambertMaterial: MeshLambertMaterial, Object3D: Object3D, Vector3: Vector3 }; var bfg$2 = Object.assign({}, _bfg); var BufferGeometryUtils$2 = bfg$2.BufferGeometryUtils || bfg$2; // var PointsLayerKapsule = Kapsule({ props: { pointsData: { "default": [] }, pointLat: { "default": 'lat' }, pointLng: { "default": 'lng' }, pointColor: { "default": function _default() { return '#ffffaa'; } }, pointAltitude: { "default": 0.1 }, // in units of globe radius pointRadius: { "default": 0.25 }, // in deg pointResolution: { "default": 12, triggerUpdate: false }, // how many slice segments in the cylinder's circumference pointsMerge: { "default": false }, // boolean. Whether to merge all points into a single mesh for rendering performance pointsTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, init: function init(threeObj, state, _ref) { var tweenGroup = _ref.tweenGroup; // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; state.tweenGroup = tweenGroup; state.dataMapper = new ThreeDigest(threeObj, { objBindAttr: '__threeObjPoint' }); }, update: function update(state, changedProps) { // Data accessors var latAccessor = accessorFn(state.pointLat); var lngAccessor = accessorFn(state.pointLng); var altitudeAccessor = accessorFn(state.pointAltitude); var radiusAccessor = accessorFn(state.pointRadius); var colorAccessor = accessorFn(state.pointColor); // shared geometry var pointGeometry = new THREE$e.CylinderGeometry(1, 1, 1, state.pointResolution); pointGeometry.applyMatrix4(new THREE$e.Matrix4().makeRotationX(Math.PI / 2)); pointGeometry.applyMatrix4(new THREE$e.Matrix4().makeTranslation(0, 0, -0.5)); var pxPerDeg = 2 * Math.PI * GLOBE_RADIUS / 360; var pointMaterials = {}; // indexed by color if (!state.pointsMerge && changedProps.hasOwnProperty('pointsMerge')) { emptyObject(state.scene); // Empty trailing merged objects } state.dataMapper.scene = state.pointsMerge ? new THREE$e.Object3D() : state.scene; // use fake scene if merging points state.dataMapper.onCreateObj(createObj).onUpdateObj(updateObj).digest(state.pointsData); if (state.pointsMerge) { // merge points into a single mesh var pointsGeometry = !state.pointsData.length ? new THREE$e.BufferGeometry() : (BufferGeometryUtils$2.mergeGeometries || BufferGeometryUtils$2.mergeBufferGeometries)(state.pointsData.map(function (d) { var obj = state.dataMapper.getObj(d); var geom = obj.geometry.clone(); // apply mesh world transform to vertices obj.updateMatrix(); geom.applyMatrix4(obj.matrix); // color vertices var color = color2ShaderArr(colorAccessor(d)); geom.setAttribute('color', array2BufferAttr(Array(geom.getAttribute('position').count).fill(color), 4)); return geom; })); var points = new THREE$e.Mesh(pointsGeometry, new THREE$e.MeshLambertMaterial({ color: 0xffffff, transparent: true, vertexColors: true })); points.__globeObjType = 'points'; // Add object type points.__data = state.pointsData; // Attach obj data state.dataMapper.clear(); // Unbind merged points emptyObject(state.scene); state.scene.add(points); } // function createObj() { var obj = new THREE$e.Mesh(pointGeometry); obj.__globeObjType = 'point'; // Add object type return obj; } function updateObj(obj, d) { var applyUpdate = function applyUpdate(td) { var _obj$__currentTargetD = obj.__currentTargetD = td, r = _obj$__currentTargetD.r, alt = _obj$__currentTargetD.alt, lat = _obj$__currentTargetD.lat, lng = _obj$__currentTargetD.lng; // position cylinder ground Object.assign(obj.position, polar2Cartesian(lat, lng)); // orientate outwards var globeCenter = state.pointsMerge ? new THREE$e.Vector3(0, 0, 0) : state.scene.localToWorld(new THREE$e.Vector3(0, 0, 0)); // translate from local to world coords obj.lookAt(globeCenter); // scale radius and altitude obj.scale.x = obj.scale.y = Math.min(30, r) * pxPerDeg; obj.scale.z = Math.max(alt * GLOBE_RADIUS, 0.1); // avoid non-invertible matrix }; var targetD = { alt: +altitudeAccessor(d), r: +radiusAccessor(d), lat: +latAccessor(d), lng: +lngAccessor(d) }; var currentTargetD = obj.__currentTargetD || Object.assign({}, targetD, { alt: -1e-3 }); if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (state.pointsMerge || !state.pointsTransitionDuration || state.pointsTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate state.tweenGroup.add(new Tween(currentTargetD).to(targetD, state.pointsTransitionDuration).easing(Easing.Quadratic.InOut).onUpdate(applyUpdate).start()); } } if (!state.pointsMerge) { // Update materials on individual points var color = colorAccessor(d); var opacity = color ? colorAlpha(color) : 0; var showCyl = !!opacity; obj.visible = showCyl; if (showCyl) { if (!pointMaterials.hasOwnProperty(color)) { pointMaterials[color] = new THREE$e.MeshLambertMaterial({ color: colorStr2Hex(color), transparent: opacity < 1, opacity: opacity }); } obj.material = pointMaterials[color]; } } } } }); var dashedLineShaders = function dashedLineShaders() { return { uniforms: { // dash param defaults, all relative to full length dashOffset: { value: 0 }, dashSize: { value: 1 }, gapSize: { value: 0 }, dashTranslate: { value: 0 } // used for animating the dash }, vertexShader: "\n uniform float dashTranslate; \n\n attribute vec4 color;\n varying vec4 vColor;\n \n attribute float relDistance;\n varying float vRelDistance;\n\n void main() {\n // pass through colors and distances\n vColor = color;\n vRelDistance = relDistance + dashTranslate;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n ", fragmentShader: "\n uniform float dashOffset; \n uniform float dashSize;\n uniform float gapSize; \n \n varying vec4 vColor;\n varying float vRelDistance;\n \n void main() {\n // ignore pixels in the gap\n if (vRelDistance < dashOffset) discard;\n if (mod(vRelDistance - dashOffset, dashSize + gapSize) > dashSize) discard;\n \n // set px color: [r, g, b, a], interpolated between vertices \n gl_FragColor = vColor; \n }\n " }; }; var invisibleUndergroundShaderExtend = function invisibleUndergroundShaderExtend(shader) { shader.uniforms.surfaceRadius = { type: 'float', value: 0 }; shader.vertexShader = ('attribute float surfaceRadius;\nvarying float vSurfaceRadius;\nvarying vec3 vPos;\n' + shader.vertexShader).replace('void main() {', ['void main() {', 'vSurfaceRadius = surfaceRadius;', 'vPos = position;'].join('\n')); shader.fragmentShader = ('uniform float surfaceRadius;\nvarying float vSurfaceRadius;\nvarying vec3 vPos;\n' + shader.fragmentShader).replace('void main() {', ['void main() {', 'if (length(vPos) < max(surfaceRadius, vSurfaceRadius)) discard;'].join('\n')); return shader; }; var setRadiusShaderExtend = function setRadiusShaderExtend(shader) { shader.vertexShader = "\n attribute float r;\n \n const float PI = 3.1415926535897932384626433832795;\n float toRad(in float a) {\n return a * PI / 180.0;\n }\n \n vec3 Polar2Cartesian(in vec3 c) { // [lat, lng, r]\n float phi = toRad(90.0 - c.x);\n float theta = toRad(90.0 - c.y);\n float r = c.z;\n return vec3( // x,y,z\n r * sin(phi) * cos(theta),\n r * cos(phi),\n r * sin(phi) * sin(theta)\n );\n }\n \n vec2 Cartesian2Polar(in vec3 p) {\n float r = sqrt(p.x * p.x + p.y * p.y + p.z * p.z);\n float phi = acos(p.y / r);\n float theta = atan(p.z, p.x);\n return vec2( // lat,lng\n 90.0 - phi * 180.0 / PI,\n 90.0 - theta * 180.0 / PI - (theta < -PI / 2.0 ? 360.0 : 0.0)\n );\n }\n ".concat(shader.vertexShader.replace('}', " \n vec3 pos = Polar2Cartesian(vec3(Cartesian2Polar(position), r));\n gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);\n }\n "), "\n "); return shader; }; // var applyShaderExtensionToMaterial = function applyShaderExtensionToMaterial(material, extensionFn) { material.onBeforeCompile = function (shader) { material.userData.shader = extensionFn(shader); }; return material; }; var setExtendedMaterialUniforms = function setExtendedMaterialUniforms(material) { var uniformsFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function (u) { return u; }; if (material.userData.shader) { uniformsFn(material.userData.shader.uniforms); } else { var curFn = material.onBeforeCompile; material.onBeforeCompile = function (shader) { curFn(shader); uniformsFn(shader.uniforms); }; } }; var _excluded = ["stroke"]; var THREE$d = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { BufferGeometry: BufferGeometry, CubicBezierCurve3: CubicBezierCurve3, Curve: Curve, Group: Group, Line: Line, Mesh: Mesh, NormalBlending: NormalBlending, QuadraticBezierCurve3: QuadraticBezierCurve3, ShaderMaterial: ShaderMaterial, TubeGeometry: TubeGeometry, Vector3: Vector3 }; var FrameTicker$2 = _FrameTicker["default"] || _FrameTicker; // var ArcsLayerKapsule = Kapsule({ props: { arcsData: { "default": [] }, arcStartLat: { "default": 'startLat' }, arcStartLng: { "default": 'startLng' }, arcEndLat: { "default": 'endLat' }, arcEndLng: { "default": 'endLng' }, arcColor: { "default": function _default() { return '#ffffaa'; } }, // single color, array of colors or color interpolation fn arcAltitude: {}, // in units of globe radius arcAltitudeAutoScale: { "default": 0.5 }, // scale altitude proportional to great-arc distance between the two points arcStroke: {}, // in deg arcCurveResolution: { "default": 64, triggerUpdate: false }, // how many straight segments in the curve arcCircularResolution: { "default": 6, triggerUpdate: false }, // how many slice segments in the tube's circumference arcDashLength: { "default": 1 }, // in units of line length arcDashGap: { "default": 0 }, arcDashInitialGap: { "default": 0 }, arcDashAnimateTime: { "default": 0 }, // ms arcsTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, methods: { pauseAnimation: function pauseAnimation(state) { var _state$ticker; (_state$ticker = state.ticker) === null || _state$ticker === undefined || _state$ticker.pause(); }, resumeAnimation: function resumeAnimation(state) { var _state$ticker2; (_state$ticker2 = state.ticker) === null || _state$ticker2 === undefined || _state$ticker2.resume(); }, _destructor: function _destructor(state) { var _state$ticker3; state.sharedMaterial.dispose(); (_state$ticker3 = state.ticker) === null || _state$ticker3 === undefined || _state$ticker3.dispose(); } }, stateInit: function stateInit(_ref) { var tweenGroup = _ref.tweenGroup; return { tweenGroup: tweenGroup, ticker: new FrameTicker$2(), sharedMaterial: new THREE$d.ShaderMaterial(_objectSpread2(_objectSpread2({}, dashedLineShaders()), {}, { transparent: true, blending: THREE$d.NormalBlending })) }; }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; state.dataMapper = new ThreeDigest(threeObj, { objBindAttr: '__threeObjArc' }).onCreateObj(function () { var obj = new THREE$d.Group(); // populated in updateObj obj.__globeObjType = 'arc'; // Add object type return obj; }); // Kick-off dash animations state.ticker.onTick.add(function (_, timeDelta) { state.dataMapper.entries().map(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 2), obj = _ref3[1]; return obj; }).filter(function (o) { return o.children.length && o.children[0].material && o.children[0].__dashAnimateStep; }).forEach(function (o) { var obj = o.children[0]; var step = obj.__dashAnimateStep * timeDelta; var curTranslate = obj.material.uniforms.dashTranslate.value % 1e9; // reset after 1B loops obj.material.uniforms.dashTranslate.value = curTranslate + step; }); }); }, update: function update(state) { // Data accessors var startLatAccessor = accessorFn(state.arcStartLat); var startLngAccessor = accessorFn(state.arcStartLng); var endLatAccessor = accessorFn(state.arcEndLat); var endLngAccessor = accessorFn(state.arcEndLng); var altitudeAccessor = accessorFn(state.arcAltitude); var altitudeAutoScaleAccessor = accessorFn(state.arcAltitudeAutoScale); var strokeAccessor = accessorFn(state.arcStroke); var colorAccessor = accessorFn(state.arcColor); var dashLengthAccessor = accessorFn(state.arcDashLength); var dashGapAccessor = accessorFn(state.arcDashGap); var dashInitialGapAccessor = accessorFn(state.arcDashInitialGap); var dashAnimateTimeAccessor = accessorFn(state.arcDashAnimateTime); state.dataMapper.onUpdateObj(function (group, arc) { var stroke = strokeAccessor(arc); var useTube = stroke !== null && stroke !== undefined; if (!group.children.length || useTube !== (group.children[0].type === 'Mesh')) { // create or swap object types emptyObject(group); var _obj = useTube ? new THREE$d.Mesh() : new THREE$d.Line(new THREE$d.BufferGeometry()); _obj.material = state.sharedMaterial.clone(); // Separate material instance per object to have dedicated uniforms (but shared shaders) group.add(_obj); } var obj = group.children[0]; // set dash uniforms Object.assign(obj.material.uniforms, { dashSize: { value: dashLengthAccessor(arc) }, gapSize: { value: dashGapAccessor(arc) }, dashOffset: { value: dashInitialGapAccessor(arc) } }); // set dash animation step var dashAnimateTime = dashAnimateTimeAccessor(arc); obj.__dashAnimateStep = dashAnimateTime > 0 ? 1000 / dashAnimateTime : 0; // per second // calculate vertex colors (to create gradient) var vertexColorArray = calcColorVertexArray(colorAccessor(arc), // single, array of colors or interpolator state.arcCurveResolution, // numSegments useTube ? state.arcCircularResolution + 1 : 1 // num vertices per segment ); // calculate vertex relative distances (for dashed lines) var vertexRelDistanceArray = calcVertexRelDistances(state.arcCurveResolution, // numSegments useTube ? state.arcCircularResolution + 1 : 1, // num vertices per segment true // run from end to start, to animate in the correct direction ); obj.geometry.setAttribute('color', vertexColorArray); obj.geometry.setAttribute('relDistance', vertexRelDistanceArray); var applyUpdate = function applyUpdate(td) { var _group$__currentTarge = group.__currentTargetD = td, stroke = _group$__currentTarge.stroke, curveD = _objectWithoutProperties(_group$__currentTarge, _excluded); var curve = calcCurve(curveD); if (useTube) { obj.geometry && obj.geometry.dispose(); obj.geometry = new THREE$d.TubeGeometry(curve, state.arcCurveResolution, stroke / 2, state.arcCircularResolution); obj.geometry.setAttribute('color', vertexColorArray); obj.geometry.setAttribute('relDistance', vertexRelDistanceArray); } else { obj.geometry.setFromPoints(curve.getPoints(state.arcCurveResolution)); } }; var targetD = { stroke: stroke, alt: altitudeAccessor(arc), altAutoScale: +altitudeAutoScaleAccessor(arc), startLat: +startLatAccessor(arc), startLng: +startLngAccessor(arc), endLat: +endLatAccessor(arc), endLng: +endLngAccessor(arc) }; var currentTargetD = group.__currentTargetD || Object.assign({}, targetD, { altAutoScale: -1e-3 }); if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (!state.arcsTransitionDuration || state.arcsTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate state.tweenGroup.add(new Tween(currentTargetD).to(targetD, state.arcsTransitionDuration).easing(Easing.Quadratic.InOut).onUpdate(applyUpdate).start()); } } }).digest(state.arcsData); // function calcCurve(_ref4) { var alt = _ref4.alt, altAutoScale = _ref4.altAutoScale, startLat = _ref4.startLat, startLng = _ref4.startLng, endLat = _ref4.endLat, endLng = _ref4.endLng; var getVec = function getVec(_ref5) { var _ref6 = _slicedToArray(_ref5, 3), lng = _ref6[0], lat = _ref6[1], alt = _ref6[2]; var _polar2Cartesian = polar2Cartesian(lat, lng, alt), x = _polar2Cartesian.x, y = _polar2Cartesian.y, z = _polar2Cartesian.z; return new THREE$d.Vector3(x, y, z); }; //calculate curve var startPnt = [startLng, startLat]; var endPnt = [endLng, endLat]; var altitude = alt; (altitude === null || altitude === undefined) && ( // by default set altitude proportional to the great-arc distance altitude = geoDistance$1(startPnt, endPnt) / 2 * altAutoScale); if (altitude) { var interpolate = geoInterpolate(startPnt, endPnt); var _map = [0.25, 0.75].map(function (t) { return [].concat(_toConsumableArray(interpolate(t)), [altitude * 1.5]); }), _map2 = _slicedToArray(_map, 2), m1Pnt = _map2[0], m2Pnt = _map2[1]; var curve = _construct(THREE$d.CubicBezierCurve3, _toConsumableArray([startPnt, m1Pnt, m2Pnt, endPnt].map(getVec))); //const mPnt = [...interpolate(0.5), altitude * 2]; //curve = new THREE.QuadraticBezierCurve3(...[startPnt, mPnt, endPnt].map(getVec)); return curve; } else { // ground line var _alt = 0.001; // slightly above the ground to prevent occlusion return calcSphereArc.apply(undefined, _toConsumableArray([[].concat(startPnt, [_alt]), [].concat(endPnt, [_alt])].map(getVec))); } // function calcSphereArc(startVec, endVec) { var angle = startVec.angleTo(endVec); var getGreatCirclePoint = angle === 0 ? function () { return startVec.clone(); } // points exactly overlap : function (t) { return new THREE$d.Vector3().addVectors(startVec.clone().multiplyScalar(Math.sin((1 - t) * angle)), endVec.clone().multiplyScalar(Math.sin(t * angle))).divideScalar(Math.sin(angle)); }; var sphereArc = new THREE$d.Curve(); sphereArc.getPoint = getGreatCirclePoint; return sphereArc; } } function calcColorVertexArray(colors, numSegments) { var numVerticesPerSegment = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var numVerticesGroup = numSegments + 1; // one between every two segments and two at the ends var getVertexColor; if (colors instanceof Array || colors instanceof Function) { var colorInterpolator = colors instanceof Array ? scaleLinear() // array of colors, interpolate at each step .domain(colors.map(function (_, idx) { return idx / (colors.length - 1); })) // same number of stops as colors .range(colors) : colors; // already interpolator fn getVertexColor = function getVertexColor(t) { return color2ShaderArr(colorInterpolator(t), true, true); }; } else { // single color, use constant var vertexColor = color2ShaderArr(colors, true, true); getVertexColor = function getVertexColor() { return vertexColor; }; } var vertexColors = []; for (var v = 0, l = numVerticesGroup; v < l; v++) { var _vertexColor = getVertexColor(v / (l - 1)); for (var s = 0; s < numVerticesPerSegment; s++) { vertexColors.push(_vertexColor); } } return array2BufferAttr(vertexColors, 4); } function calcVertexRelDistances(numSegments) { var numVerticesPerSegment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var invert = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var numVerticesGroup = numSegments + 1; // one between every two segments and two at the ends var vertexDistances = []; for (var v = 0, l = numVerticesGroup; v < l; v++) { var relDistance = v / (l - 1); for (var s = 0; s < numVerticesPerSegment; s++) { vertexDistances.push(relDistance); } } invert && vert