}\n */\n getEntityProducers()\n {\n let entity_map = this._getEntityMap();\n return entity_map.keys();\n }\n\n /**\n * @summary Flake ツリーに producer を追加\n *\n * 事前条件:\n * - this._entity_map !== null\n * - this と this の子孫に producer が存在しない\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n */\n addEntityProducer( producer )\n {\n switch ( producer.getAreaStatus( this ) ) {\n\n case Entity.AreaStatus.PARTIAL: {\n // エントリに producer を追加\n this._entity_map.set( producer, false );\n\n // this の子孫も同様の処理\n for ( let child of this._children ) {\n if ( child !== null && child._entity_map !== null ) {\n child.addEntityProducer( producer );\n }\n }\n } break;\n\n case Entity.AreaStatus.FULL: {\n this._addEntityFullProducer( producer );\n } break;\n\n default: // Entity.AreaStatus.EMPTY\n break;\n }\n }\n\n /**\n * @summary Flake ツリーに producer を追加\n *\n * 事前条件:\n * - producer.getAreaStatus( this ) === Entity.AreaStatus.FULL\n * - this._entity_map !== null\n * - this と this の子孫に producer が存在しない\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n *\n * @private\n */\n _addEntityFullProducer( producer )\n {\n // エントリに producer を追加\n this._entity_map.set( producer, true );\n\n // this の子孫も同様の処理\n for ( let child of this._children ) {\n if ( child !== null && child._entity_map !== null ) {\n child._addEntityFullProducer( producer );\n }\n }\n }\n\n /**\n * @summary Flake ツリーから producer を削除\n *\n * 事前条件:\n * - this._entity_map !== null\n * 事後条件:\n * - this と this の子孫に producer が存在しない\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n */\n removeEntityProducer( producer )\n {\n if ( !this._entity_map.has( producer ) ) {\n // もともと producer は this と this の子孫に存在しない\n return;\n }\n\n // エントリから producer を削除\n this._entity_map.delete( producer );\n\n // this に producer に対応するメッシュが存在すれば削除\n this._removeEntityMeshes( producer );\n\n // this の子孫も同様の処理\n for ( let child of this._children ) {\n if ( child !== null && child._entity_map !== null ) {\n child.removeEntityProducer( producer );\n }\n }\n }\n\n /**\n * @summary Flake ツリーの producer を更新\n *\n * 事前条件:\n * - this._entity_map !== null\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n */\n updateEntityProducer( producer )\n {\n this.removeEntityProducer( producer );\n this.addEntityProducer( producer );\n }\n\n /**\n * @summary 地表断片とレイの交点までの距離を検索\n * 地表断片 this と線分 (ray.position を始点とし、そこから ray.direction 方向に limit 距離未満にある点) との交点の中で、始点から最も近い交点までの距離を返す。
\n * ただし地表断片と線分が交差しないときは limit を返す。
\n * 事前条件: this._globe.status === Status.READY
\n * @param {mapray.Ray} ray ray.position を始点として ray.direction 方向に伸びる半直線\n * @param {number} limit この距離までの交点を検索\n * @return {number} ray.position から交点までの距離、ただし交差しなかったときは limit\n */\n findRayDistance( ray, limit )\n {\n var dem_flake;\n for ( dem_flake = this; dem_flake._dem_state !== DemState.LOADED; dem_flake = dem_flake._parent ) {}\n\n if ( this.z - dem_flake.z === this._globe._ρ ) {\n return this._findQuadRayDistance( ray, limit, dem_flake );\n }\n else if ( this._cullForRayDistance( ray, limit ) ) {\n return limit;\n }\n else {\n var dmin = limit;\n for ( var v = 0; v < 2; ++ v ) {\n for ( var u = 0; u < 2; ++ u ) {\n dmin = this.newChild( u, v ).findRayDistance( ray, dmin );\n }\n }\n return dmin;\n }\n }\n\n /**\n * @summary 自己と子孫を破棄\n */\n dispose()\n {\n var i;\n\n var parent = this._parent;\n if ( parent === null ) {\n // すでに破棄済み\n return;\n }\n\n var globe = this._globe;\n\n // メッシュを破棄\n var meshes = this._meshes;\n while ( meshes.length > 0 ) {\n meshes[0].dispose();\n }\n\n // 子孫 Flake を破棄\n var children = this._children;\n for ( i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n child.dispose();\n }\n }\n\n // 親 Flake から this を削除\n var pchildren = parent._children;\n for ( i = 0; i < 4; ++i ) {\n if ( pchildren[i] === this ) {\n pchildren[i] = null;\n break;\n }\n }\n this._parent = null;\n\n // DEM リクエストの取り消し\n if ( this._dem_state === DemState.REQUESTED ) {\n globe._dem_provider.cancelRequest( this._dem_data );\n --globe._num_dem_requesteds;\n }\n\n // Flake 数を減らす\n globe._num_cache_flakes -= 1;\n }\n\n /**\n * @summary 自己と子孫の Flake リストを取得\n * @return {array}\n * @package\n */\n flattenFlakes()\n {\n var list = [];\n this._flattenFlakes( list );\n return list;\n }\n\n /**\n * @summary 自己と子孫の MeshNode リストを取得\n * @return {array}\n * @package\n */\n flattenMeshes()\n {\n var list = [];\n this._flattenMeshes( list );\n return list;\n }\n\n /**\n * @summary 削減用の Flake 比較\n * @param {mapray.Globe.Flake} other 比較対象\n * @return {number} 比較値\n * @package\n */\n compareForReduce( other )\n {\n // 最近アクセスしたものを優先\n // 同じなら Z レベルが小さい方を優先\n var a = this;\n var b = other;\n var aframe = b._aframe - a._aframe;\n return (aframe !== 0) ? aframe : a.z - b.z;\n }\n\n /**\n * @private\n */\n _flattenFlakes( list )\n {\n list.push( this );\n var children = this._children;\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n child._flattenFlakes( list );\n }\n }\n }\n\n /**\n * @private\n */\n _flattenMeshes( list )\n {\n Array.prototype.push.apply( list, this._meshes );\n var children = this._children;\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n child._flattenMeshes( list );\n }\n }\n }\n\n /**\n * @summary アクセスフレームを更新\n * @package\n */\n touch()\n {\n var globe = this._globe;\n if ( this._aframe !== globe._frame_counter ) {\n this._aframe = globe._frame_counter;\n globe._num_touch_flakes += 1;\n }\n }\n\n /**\n * @summary メッシュノードを取得\n *\n * @param {number} lod 地表詳細レベル (LOD)\n * @param {number} cu 水平球面分割レベル\n * @param {number} cv 垂直球面分割レベル\n *\n * @return {mapray.Globe.MeshNode} メッシュノード\n *\n * @private\n */\n _getMeshNode( lod, cu, cv )\n {\n var dem = this._getMeshDemBinary( lod );\n var dpows = dem.getDivisionPowers( this, lod, cu, cv );\n\n // キャッシュに存在すれば、それを返す\n var meshes = this._meshes;\n var length = meshes.length;\n for ( var i = 0; i < length; ++i ) {\n var item = meshes[i];\n if ( item.match( dem, dpows ) ) {\n return item;\n }\n }\n\n // キャッシュに存在しないので新規に生成\n var node = new MeshNode( this, dem, dpows );\n meshes.unshift( node ); // 検索効率のため先頭に追加\n return node;\n }\n\n /**\n * @summary メッシュ用の DEM バイナリを取得\n * @param {number} lod 地表詳細レベル (LOD)\n * @return {mapray.DemBinary} DEM タイルデータ\n * @private\n */\n _getMeshDemBinary( lod )\n {\n var zDesired = GeoMath.clamp( Math.round( lod + this._globe._dem_zbias ),\n 0, this._dem_zlimit );\n\n var dem = this._findNearestDemTile( zDesired );\n\n // 上のレベルの DEM をリクエスト\n if ( dem.z < zDesired ) {\n var qlevel = dem.getQuadLevel( this.z, this.x, this.y );\n if ( qlevel > 0 ) {\n this._requestAncestorDemTile( Math.min( dem.z + qlevel, zDesired ) );\n }\n }\n\n return dem;\n }\n\n /**\n * @summary 先祖 DEM タイルデータを検索\n * @desc\n * this の (レベルが zlimit またはそれ以下の) 祖先の中で、現在キャッシュに存在する最大レベルの DEM タイルデータを検索する。
\n * @param {number} zlimit 先祖レベルの上限\n * @return {mapray.DemBinary} 先祖 DEM タイルデータ\n */\n _findNearestDemTile( zlimit )\n {\n var flake = this;\n\n // zlimit の地表断片を検索\n var count = this.z - zlimit;\n for ( var i = 0; i < count; ++i ) {\n flake = flake._parent;\n }\n\n // 次の DemBinary を持つ地表断片を検索\n while ( flake._dem_state !== DemState.LOADED ) {\n flake = flake._parent;\n }\n\n // 見つけた地表断片の DemBinary を返す\n return flake._dem_data;\n }\n\n /**\n * @summary 地表断片を包含する DEM タイルデータを要求\n * @desc\n * this を包含または this と一致する、ズームレベル ze の DEM タイルをサーバーに要求する。
\n * ただしすでにキャッシュにその DEM タイルが存在、または REQUESTED 状態のときは要求しない。
\n * FAILED 状態かつ ze > 0 のときは、再帰的に ze - 1 を要求する。
\n * 要求が最大数に達しているときは無視する。
\n * @param {number} ze DEM ズームレベル\n */\n _requestAncestorDemTile( ze )\n {\n var globe = this._globe;\n\n if ( globe._num_dem_requesteds >= globe._max_dem_requesteds ) {\n // 要求が最大数に達しているので受け入れない\n return;\n }\n\n var flake = this;\n\n // zlimit の地表断片を検索\n var count = this.z - ze;\n for ( var i = 0; i < count; ++i ) {\n flake = flake._parent;\n }\n\n while ( true ) {\n var state = flake._dem_state;\n if ( state === DemState.LOADED || state === DemState.REQUESTED ) {\n // 要求する必要がない\n break;\n }\n else if ( state === DemState.FAILED ) {\n // 親でリトライ\n flake = flake._parent;\n continue;\n }\n else {\n // DEM タイルデータを要求\n // assert: state === DemState.NONE\n var provider = globe._dem_provider;\n\n flake._dem_data = provider.requestTile( flake.z, flake.x, flake.y, data => {\n if ( flake._parent === null ) {\n // すでに破棄済みなので無視\n return;\n }\n if ( data ) {\n flake._dem_data = new DemBinary( flake.z, flake.x, flake.y, globe._ρ, data );\n flake._dem_state = DemState.LOADED;\n globe._dem_area_updated.addTileArea( flake );\n }\n else { // データ取得に失敗\n flake._dem_data = null;\n flake._dem_state = DemState.FAILED;\n }\n --globe._num_dem_requesteds;\n } );\n\n flake._dem_state = DemState.REQUESTED;\n ++globe._num_dem_requesteds;\n break;\n }\n }\n }\n\n /**\n * @private\n */\n _isInvisible_0( clip_planes )\n {\n var r = GeoMath.EARTH_RADIUS + this._height_max;\n\n for ( var i = 0; i < clip_planes.length; ++i ) {\n var dist = clip_planes[i][3]; // 平面から GOCS 原点 (地球中心) までの距離\n if ( dist < -r ) {\n // 地球全体がこの平面の裏側にあるので見えない\n return true;\n }\n }\n\n return false; // 見えている可能性がある\n }\n\n /**\n * @private\n */\n _isInvisible_N( clip_planes )\n {\n var xmin = this._gocs_x_min;\n var xmax = this._gocs_x_max;\n var ymin = this._gocs_y_min;\n var ymax = this._gocs_y_max;\n var zmin = this._gocs_z_min;\n var zmax = this._gocs_z_max;\n\n for ( var i = 0; i < clip_planes.length; ++i ) {\n var p = clip_planes[i];\n var px = p[0];\n var py = p[1];\n var pz = p[2];\n var pw = p[3];\n\n // 以下がすべて成り立つとボックス全体は平面の裏側にある\n // px*xmin + py*ymin + pz*zmin + pw < 0\n // px*xmax + py*ymin + pz*zmin + pw < 0\n // px*xmin + py*ymax + pz*zmin + pw < 0\n // px*xmax + py*ymax + pz*zmin + pw < 0\n // px*xmin + py*ymin + pz*zmax + pw < 0\n // px*xmax + py*ymin + pz*zmax + pw < 0\n // px*xmin + py*ymax + pz*zmax + pw < 0\n // px*xmax + py*ymax + pz*zmax + pw < 0\n\n var c0 = px*xmin + py*ymin;\n var c1 = px*xmax + py*ymin;\n var c2 = px*xmin + py*ymax;\n var c3 = px*xmax + py*ymax;\n var c4 = -pz*zmin - pw;\n var c5 = -pz*zmax - pw;\n\n if ( c0 < c4 && c1 < c4 && c2 < c4 && c3 < c4 &&\n c0 < c5 && c1 < c5 && c2 < c5 && c3 < c5 ) {\n // ボックス全体が平面の裏側にあるので見えない\n return true;\n }\n }\n\n return false; // 見えている可能性がある\n }\n\n /**\n * @summary 中間緯度の余弦\n * @return {number}\n * @private\n */\n _getCosφ()\n {\n var z = this.z;\n if ( z > 0 ) {\n var y = this.y;\n var p = Math.pow( 2, 1 - z );\n var y0 = Math.abs( 1 - p * y );\n var y1 = Math.abs( 1 - p * (y + 1) );\n var ey = Math.exp( Math.PI * Math.min( y0, y1 ) );\n return 2 * ey / (ey*ey + 1); // Cos[φ] == Cos[gd[y]] == Sech[y]\n }\n else {\n // z == 0 のときは φ == 0 とする\n return 1; // Cos[0]\n }\n }\n\n /**\n * @summary 標高代表値と境界箱を更新\n * @private\n */\n _estimate()\n {\n if ( this._prev_Za_dem === this ) {\n // 代表値は決定済みなので何もしない\n return;\n }\n\n var zg = this.z;\n var ρ = this._globe._ρ;\n var zr_dem;\n\n if ( zg < ρ ) {\n zr_dem = this._findNearestDemTile( zg );\n if ( zr_dem === this._prev_Zr_dem ) {\n // 前回と代表値が変わらないので何もしない\n return;\n }\n this._prev_Zr_dem = zr_dem;\n this._estimate_low( zr_dem );\n this._dem_zlimit = zg;\n }\n else {\n var za_dem = this._findNearestDemTile( zg - ρ );\n\n if ( za_dem.isLeaf( zg, this.x, this.y ) ) {\n this._estimate_leaf( za_dem );\n }\n else {\n zr_dem = this._findNearestDemTile( za_dem.z + ρ );\n if ( za_dem === this._prev_Za_dem && zr_dem === this._prev_Zr_dem ) {\n // 前回と代表値が変わらないので何もしない\n return;\n }\n this._prev_Za_dem = za_dem;\n this._prev_Zr_dem = zr_dem;\n this._estimate_high( za_dem, zr_dem );\n }\n this._dem_zlimit = za_dem.z + ρ;\n }\n\n // 境界箱の更新\n switch ( zg ) {\n case 0: this._updataBoundingBox_0(); break;\n case 1: this._updataBoundingBox_1(); break;\n default: this._updataBoundingBox_N(); break;\n }\n }\n\n /**\n * @summary 標高代表値を計算 (Zg < ρ)\n * @param {mapray.DemBinary} zr_dem レベルが Zr の DEM\n * @private\n */\n _estimate_low( zr_dem )\n {\n var zg = this.z;\n var xg = this.x;\n var yg = this.y;\n var α = this._calcAlpha();\n\n this._base_height = this._globe._avg_height.sample( zg, xg, yg );\n this._height_min = Math.max( this._base_height + α * Flake.Fm, zr_dem.height_min );\n this._height_max = Math.min( this._base_height + α * Flake.Fp, zr_dem.height_max );\n\n if ( zr_dem.z == zg || zr_dem.isLeaf( zg, xg, yg ) ) {\n // 標高代表値が確定した\n this._prev_Za_dem = this;\n }\n }\n\n /**\n * @summary 標高代表値を計算 (Zg >= ρ && !L(Za))\n * @param {mapray.DemBinary} za_dem レベルが Za の DEM\n * @param {mapray.DemBinary} zr_dem レベルが Zr の DEM\n * @private\n */\n _estimate_high( za_dem, zr_dem )\n {\n var globe = this._globe;\n var zg = this.z;\n var xg = this.x;\n var yg = this.y;\n\n var ze = za_dem.z; // -> za\n var xe = za_dem.x;\n var ye = za_dem.y;\n\n var ρ = globe._ρ;\n var pow = Math.pow( 2, ze - zg );\n var size = 1 << ρ;\n\n var u = Math.floor( size * ((xg + 0.5) * pow - xe) );\n var v = Math.floor( size * ((yg + 0.5) * pow - ye) );\n\n var smin = size * ( xg * pow - xe) - u;\n var smax = size * ((xg + 1) * pow - xe) - u;\n var tmin = size * ( yg * pow - ye) - v;\n var tmax = size * ((yg + 1) * pow - ye) - v;\n\n var heights = za_dem.getHeights( u, v );\n var h00 = heights[0];\n var h10 = heights[1];\n var h01 = heights[2];\n var h11 = heights[3];\n\n var h0 = (h00 * (1 - smin) + h10 * smin) * (1 - tmin) + (h01 * (1 - smin) + h11 * smin) * tmin;\n var h1 = (h00 * (1 - smax) + h10 * smax) * (1 - tmin) + (h01 * (1 - smax) + h11 * smax) * tmin;\n var h2 = (h00 * (1 - smin) + h10 * smin) * (1 - tmax) + (h01 * (1 - smin) + h11 * smin) * tmax;\n var h3 = (h00 * (1 - smax) + h10 * smax) * (1 - tmax) + (h01 * (1 - smax) + h11 * smax) * tmax;\n\n var α = this._calcAlpha();\n\n this._base_height = 0.25 * (h0 + h1 + h2 + h3);\n this._height_min = Math.max( this._base_height + α * Flake.Fm, zr_dem.height_min );\n this._height_max = Math.min( this._base_height + α * Flake.Fp, zr_dem.height_max );\n\n if ( ze < zg - ρ ) {\n // 上のレベルの DEM をリクエスト\n var qlevel = za_dem.getQuadLevel( zg, xg, yg );\n // assert: qlevel > 0\n this._requestAncestorDemTile( Math.min( ze + qlevel, zg - ρ ) );\n }\n else if ( zr_dem.z == zg || zr_dem.isLeaf( zg, xg, yg ) ) {\n // 標高代表値が確定した\n // assert: ze == zg - ρ\n this._prev_Za_dem = this;\n }\n }\n\n /**\n * @summary 標高代表値を計算 (Zg >= ρ && L(Za))\n * @param {mapray.DemBinary} za_dem レベルが Za の DEM\n * @private\n */\n _estimate_leaf( za_dem )\n {\n var zg = this.z;\n var xg = this.x;\n var yg = this.y;\n\n var ze = za_dem.z; // -> za\n var xe = za_dem.x;\n var ye = za_dem.y;\n\n var pow = Math.pow( 2, ze - zg );\n var size = 1 << this._globe._ρ;\n\n var u = Math.floor( size * ((xg + 0.5) * pow - xe) );\n var v = Math.floor( size * ((yg + 0.5) * pow - ye) );\n\n var smin = size * ( xg * pow - xe) - u;\n var smax = size * ((xg + 1) * pow - xe) - u;\n var tmin = size * ( yg * pow - ye) - v;\n var tmax = size * ((yg + 1) * pow - ye) - v;\n\n var heights = za_dem.getHeights( u, v );\n var h00 = heights[0];\n var h10 = heights[1];\n var h01 = heights[2];\n var h11 = heights[3];\n\n // Hi = Di( Za )\n var h0 = (h00 * (1 - smin) + h10 * smin) * (1 - tmin) + (h01 * (1 - smin) + h11 * smin) * tmin;\n var h1 = (h00 * (1 - smax) + h10 * smax) * (1 - tmin) + (h01 * (1 - smax) + h11 * smax) * tmin;\n var h2 = (h00 * (1 - smin) + h10 * smin) * (1 - tmax) + (h01 * (1 - smin) + h11 * smin) * tmax;\n var h3 = (h00 * (1 - smax) + h10 * smax) * (1 - tmax) + (h01 * (1 - smax) + h11 * smax) * tmax;\n\n this._base_height = 0.25 * (h0 + h1 + h2 + h3);\n this._height_min = Math.min( h0, h1, h2, h3 );\n this._height_max = Math.max( h0, h1, h2, h3 );\n\n // 標高代表値が確定した\n this._prev_Za_dem = this;\n }\n\n /**\n * @summary α を計算\n * @desc\n * 中間緯度の標高 0 での緯線の長さを示す値 α を計算する。
\n * @return {number} α\n * @private\n */\n _calcAlpha()\n {\n var pow = Math.pow( 2, 1 - this.z );\n return pow * Flake.πr / Math.cosh( (1 - pow * (this.y + 0.5)) * Math.PI );\n }\n\n /**\n * @summary 境界箱を更新 (Z == 0)\n */\n _updataBoundingBox_0()\n {\n var r = GeoMath.EARTH_RADIUS + this._height_max;\n\n this._gocs_x_min = -r;\n this._gocs_x_max = r;\n\n this._gocs_y_min = -r;\n this._gocs_y_max = r;\n\n this._gocs_z_min = -r;\n this._gocs_z_max = r;\n }\n\n /**\n * @summary 境界箱を更新 (Z == 1)\n */\n _updataBoundingBox_1()\n {\n var r = GeoMath.EARTH_RADIUS + this._height_max;\n var x = this.x;\n var y = this.y;\n\n this._gocs_x_min = -r;\n this._gocs_x_max = r;\n\n this._gocs_y_min = r * (x - 1);\n this._gocs_y_max = r * x;\n\n this._gocs_z_min = -r * y;\n this._gocs_z_max = r * (1 - y);\n }\n\n /**\n * @summary 境界箱を更新 (Z >= 2)\n */\n _updataBoundingBox_N()\n {\n var pi = Math.PI;\n var z = this.z;\n var x = this.x;\n var y = this.y;\n\n // 座標範囲 (単位球メルカトル座標系)\n var msize = Math.pow( 2, 1 - z ) * pi;\n var mx_min = -pi + x * msize;\n var mx_max = -pi + (x + 1) * msize;\n var my_min = pi - (y + 1) * msize;\n var my_max = pi - y * msize;\n\n // 事前計算変数\n var λmin = mx_min;\n var λmax = mx_max;\n var emin = Math.exp( my_min ); // Exp[my_min]\n var emax = Math.exp( my_max ); // Exp[my_max]\n var e2min = emin * emin; // Exp[my_min]^2\n var e2max = emax * emax; // Exp[my_max]^2\n\n // 座標範囲 (地心直交座標系)\n //\n // z >= 2 のとき、λとφの範囲は以下の区間のどれかに入る\n // φ: (-π/2, 0] [0, π/2)\n // λ: [-π, -π/2] [-π/2, 0] [0, π/2] [π/2, π]\n //\n // 区間ごとの関数の変化 (各区間で単調増加または単調減少)\n // Sin[φ]: (-1 → 0] [0 → 1)\n // Cos[φ]: ( 0 → 1] [1 → 0)\n // Sin[λ]: [ 0 → -1] [-1 → 0] [0 → 1] [1 → 0]\n // Cos[λ]: [-1 → 0] [ 0 → 1] [1 → 0] [0 → -1]\n\n var rmin = GeoMath.EARTH_RADIUS + this._height_min;\n var rmax = GeoMath.EARTH_RADIUS + this._height_max;\n var cosφmin = 2 * emin / (e2min + 1);\n var cosφmax = 2 * emax / (e2max + 1);\n\n // gx = r Cos[φ] Cos[λ]\n // gy = r Cos[φ] Sin[λ]\n // gz = r Sin[φ]\n if ( my_min + my_max < 0 ) {\n // φ : (-π/2, 0]\n\n if ( λmin + λmax < -pi ) {\n // λ : [-π, -π/2]\n\n this._gocs_x_min = rmax * cosφmax * Math.cos( λmin );\n this._gocs_x_max = rmin * cosφmin * Math.cos( λmax );\n\n this._gocs_y_min = rmax * cosφmax * Math.sin( λmax );\n this._gocs_y_max = rmin * cosφmin * Math.sin( λmin );\n }\n else if ( λmin + λmax < 0 ) {\n // λ : [-π/2, 0]\n\n this._gocs_x_min = rmin * cosφmin * Math.cos( λmin );\n this._gocs_x_max = rmax * cosφmax * Math.cos( λmax );\n\n this._gocs_y_min = rmax * cosφmax * Math.sin( λmin );\n this._gocs_y_max = rmin * cosφmin * Math.sin( λmax );\n }\n else if ( λmin + λmax < pi ) {\n // λ : [0, π/2]\n\n this._gocs_x_min = rmin * cosφmin * Math.cos( λmax );\n this._gocs_x_max = rmax * cosφmax * Math.cos( λmin );\n\n this._gocs_y_min = rmin * cosφmin * Math.sin( λmin );\n this._gocs_y_max = rmax * cosφmax * Math.sin( λmax );\n }\n else {\n // λ : [π/2, π]\n\n this._gocs_x_min = rmax * cosφmax * Math.cos( λmax );\n this._gocs_x_max = rmin * cosφmin * Math.cos( λmin );\n\n this._gocs_y_min = rmin * cosφmin * Math.sin( λmax );\n this._gocs_y_max = rmax * cosφmax * Math.sin( λmin );\n }\n\n this._gocs_z_min = rmax * (e2min - 1) / (e2min + 1);\n this._gocs_z_max = rmin * (e2max - 1) / (e2max + 1);\n }\n else {\n // φ : [0, π/2)\n\n if ( λmin + λmax < -pi ) {\n // λ : [-π, -π/2]\n\n this._gocs_x_min = rmax * cosφmin * Math.cos( λmin );\n this._gocs_x_max = rmin * cosφmax * Math.cos( λmax );\n\n this._gocs_y_min = rmax * cosφmin * Math.sin( λmax );\n this._gocs_y_max = rmin * cosφmax * Math.sin( λmin );\n }\n else if ( λmin + λmax < 0 ) {\n // λ : [-π/2, 0]\n\n this._gocs_x_min = rmin * cosφmax * Math.cos( λmin );\n this._gocs_x_max = rmax * cosφmin * Math.cos( λmax );\n\n this._gocs_y_min = rmax * cosφmin * Math.sin( λmin );\n this._gocs_y_max = rmin * cosφmax * Math.sin( λmax );\n }\n else if ( λmin + λmax < pi ) {\n // λ : [0, π/2]\n\n this._gocs_x_min = rmin * cosφmax * Math.cos( λmax );\n this._gocs_x_max = rmax * cosφmin * Math.cos( λmin );\n\n this._gocs_y_min = rmin * cosφmax * Math.sin( λmin );\n this._gocs_y_max = rmax * cosφmin * Math.sin( λmax );\n }\n else {\n // λ : [π/2, π]\n\n this._gocs_x_min = rmax * cosφmin * Math.cos( λmax );\n this._gocs_x_max = rmin * cosφmax * Math.cos( λmin );\n\n this._gocs_y_min = rmin * cosφmax * Math.sin( λmax );\n this._gocs_y_max = rmax * cosφmin * Math.sin( λmin );\n }\n\n this._gocs_z_min = rmin * (e2min - 1) / (e2min + 1);\n this._gocs_z_max = rmax * (e2max - 1) / (e2max + 1);\n }\n }\n\n /**\n * サーバーにさらに正確度が高い DEM タイルデータが存在すれば、それをリクエストする。\n * @param {number} xt X 座標 (基底タイル座標系)\n * @param {number} yt Y 座標 (基底タイル座標系)\n * @private\n */\n _requestHighestAccuracy( xt, yt )\n {\n var qlevel = this._dem_data.getQuadLevelDirect( xt, yt );\n if ( qlevel == 0 ) {\n // さらに正確度が高い DEM タイルデータは存在しない\n return;\n }\n\n var flake = this;\n var size = Math.round( Math.pow( 2, this.z + 1 ) );\n var xf = size * xt;\n var yf = size * yt;\n\n for ( var i = 0; i < qlevel; ++i ) {\n var u = GeoMath.clamp( Math.floor( xf ), 0, size - 1 ) % 2;\n var v = GeoMath.clamp( Math.floor( yf ), 0, size - 1 ) % 2;\n flake = flake.newChild( u, v );\n size *= 2;\n xf *= 2;\n yf *= 2;\n }\n\n flake._requestAncestorDemTile( flake.z );\n }\n\n /**\n * @summary 地表断片とレイの交点までの距離を検索\n * 地表断片 this と線分 (ray.position を始点とし、そこから ray.direction 方向に limit 距離未満にある点) との交点までの距離を返す。
\n * ただし地表断片と線分が交差しないときは limit を返す。
\n * @private\n */\n _findQuadRayDistance( ray, limit, dem_flake )\n {\n var pts = this._getQuadPositions( dem_flake, Flake._temp_positions );\n var dist = Flake._findTriRayDistance( ray, limit, pts[0], pts[2], pts[1] );\n return (dist === limit) ? Flake._findTriRayDistance( ray, limit, pts[1], pts[2], pts[3] ) : dist;\n }\n\n /**\n * @summary 三角形とレイの交点までの距離を検索\n * 三角形 p0, p1, p2 と線分 (ray.position を始点とし、そこから ray.direction 方向に limit 距離未満にある点) との交点までの距離を返す。
\n * ただし地表断片と線分が交差しないときは limit を返す。
\n * @private\n */\n static _findTriRayDistance( ray, limit, p0, p1, p2 )\n {\n var v = ray.direction;\n\n // P1 - P0\n var p1_p0 = Flake._temp_ray_1;\n p1_p0[0] = p1[0] - p0[0];\n p1_p0[1] = p1[1] - p0[1];\n p1_p0[2] = p1[2] - p0[2];\n\n // P2 - P0\n var p2_p0 = Flake._temp_ray_2;\n p2_p0[0] = p2[0] - p0[0];\n p2_p0[1] = p2[1] - p0[1];\n p2_p0[2] = p2[2] - p0[2];\n\n // N = (P1 − P0) × (P2 − P0)\n var n = GeoMath.cross3( p1_p0, p2_p0, Flake._temp_ray_3 );\n // N . V\n var nv = GeoMath.dot3( n, v );\n\n if ( nv < 0 ) {\n var q = ray.position;\n\n // P0 - Q\n var p0_q = Flake._temp_ray_4;\n p0_q[0] = p0[0] - q[0];\n p0_q[1] = p0[1] - q[1];\n p0_q[2] = p0[2] - q[2];\n\n // N . (P0 - Q)\n // t = --------------\n // N . V\n var t = GeoMath.dot3( n, p0_q ) / nv;\n if ( t >= 0 && t < limit ) {\n // P = Q + t V\n var p = Flake._temp_ray_5;\n p[0] = q[0] + t * v[0];\n p[1] = q[1] + t * v[1];\n p[2] = q[2] + t * v[2];\n\n // P0 - P\n var p0_p = Flake._temp_ray_6;\n p0_p[0] = p0[0] - p[0];\n p0_p[1] = p0[1] - p[1];\n p0_p[2] = p0[2] - p[2];\n\n // P1 - P\n var p1_p = Flake._temp_ray_7;\n p1_p[0] = p1[0] - p[0];\n p1_p[1] = p1[1] - p[1];\n p1_p[2] = p1[2] - p[2];\n\n // P2 - P\n var p2_p = Flake._temp_ray_8;\n p2_p[0] = p2[0] - p[0];\n p2_p[1] = p2[1] - p[1];\n p2_p[2] = p2[2] - p[2];\n\n // ((P0 - P) × (P1 - P)) . N >= 0\n // ((P1 - P) × (P2 - P)) . N >= 0\n // ((P2 - P) × (P0 - P)) . N >= 0\n if ( GeoMath.dot3( GeoMath.cross3( p0_p, p1_p, Flake._temp_ray_9 ), n ) >= 0 &&\n GeoMath.dot3( GeoMath.cross3( p1_p, p2_p, Flake._temp_ray_10 ), n ) >= 0 &&\n GeoMath.dot3( GeoMath.cross3( p2_p, p0_p, Flake._temp_ray_11 ), n ) >= 0 ) {\n return t;\n }\n }\n }\n\n return limit;\n }\n\n /**\n * @summary 四隅の位置を取得\n * @param {mapray.Globe.Flake} dem_flake DEM の地表断片\n * @param {array} positions 結果の格納先\n * @return {array} positions = [左上, 右上, 左下, 右下]\n * @private\n */\n _getQuadPositions( dem_flake, positions )\n {\n var xg = this.x;\n var yg = this.y;\n var xe = dem_flake.x;\n var ye = dem_flake.y;\n\n var size = 1 << this._globe._ρ;\n var heights = dem_flake._dem_data.getHeights( xg - size * xe, yg - size * ye );\n\n var msize = Math.pow( 2, 1 - this.z ) * Math.PI;\n var mx0 = xg * msize - Math.PI;\n var my0 = Math.PI - yg * msize;\n\n for ( var iv = 0, my = my0; iv < 2; ++iv, my -= msize ) {\n var ey = Math.exp( my );\n var ey2 = ey * ey;\n var sinφ = (ey2 - 1) / (ey2 + 1);\n var cosφ = 2 * ey / (ey2 + 1);\n for ( var iu = 0, mx = mx0; iu < 2; ++iu, mx += msize ) {\n var index = iu + 2*iv;\n var radius = GeoMath.EARTH_RADIUS + heights[index];\n var sinλ = Math.sin( mx );\n var cosλ = Math.cos( mx );\n\n var pos = positions[index];\n pos[0] = radius * cosφ * cosλ;\n pos[1] = radius * cosφ * sinλ;\n pos[2] = radius * sinφ;\n }\n }\n\n return positions;\n }\n\n /**\n * @summary 地表断片とレイの交点までの距離を検索\n * 地表断片 this と線分 (ray.position を始点とし、そこから ray.direction 方向に limit 距離未満にある点) が交差しないときは true, 交差するまたは不明のとき false を返す。\n * @private\n */\n _cullForRayDistance( ray, limit )\n {\n var q = ray.position;\n var qx = q[0];\n var qy = q[1];\n var qz = q[2];\n\n var xmin = this._gocs_x_min;\n var xmax = this._gocs_x_max;\n var ymin = this._gocs_y_min;\n var ymax = this._gocs_y_max;\n var zmin = this._gocs_z_min;\n var zmax = this._gocs_z_max;\n\n if ( xmin <= qx && qx <= xmax &&\n ymin <= qy && qy <= ymax &&\n zmin <= qz && qz <= zmax ) {\n // ray の始点が AABB の表面または内部 -> 交差する可能性がある\n return false;\n }\n\n var v = ray.direction;\n var vx = v[0];\n var vy = v[1];\n var vz = v[2];\n\n var t;\n var px;\n var py;\n var pz;\n\n // yz\n if ( qx < xmin && vx > 0 ) {\n t = (xmin - qx) / vx;\n if ( t < limit ) {\n py = qy + t * vy;\n pz = qz + t * vz;\n if ( ymin <= py && py <= ymax &&\n zmin <= pz && pz <= zmax ) {\n // ray 線分は AABB の xmin 面内で交差\n return false;\n }\n }\n }\n else if ( qx > xmax && vx < 0 ) {\n t = (xmax - qx) / vx;\n if ( t < limit ) {\n py = qy + t * vy;\n pz = qz + t * vz;\n if ( ymin <= py && py <= ymax &&\n zmin <= pz && pz <= zmax ) {\n // ray 線分は AABB の xmax 面内で交差\n return false;\n }\n }\n }\n\n // xz\n if ( qy < ymin && vy > 0 ) {\n t = (ymin - qy) / vy;\n if ( t < limit ) {\n px = qx + t * vx;\n pz = qz + t * vz;\n if ( xmin <= px && px <= xmax &&\n zmin <= pz && pz <= zmax ) {\n // ray 線分は AABB の ymin 面内で交差\n return false;\n }\n }\n }\n else if ( qy > ymax && vy < 0 ) {\n t = (ymax - qy) / vy;\n if ( t < limit ) {\n px = qx + t * vx;\n pz = qz + t * vz;\n if ( xmin <= px && px <= xmax &&\n zmin <= pz && pz <= zmax ) {\n // ray 線分は AABB の ymax 面内で交差\n return false;\n }\n }\n }\n\n // xy\n if ( qz < zmin && vz > 0 ) {\n t = (zmin - qz) / vz;\n if ( t < limit ) {\n px = qx + t * vx;\n py = qy + t * vy;\n if ( xmin <= px && px <= xmax &&\n ymin <= py && py <= ymax ) {\n // ray 線分は AABB の zmin 面内で交差\n return false;\n }\n }\n }\n else if ( qz > zmax && vz < 0 ) {\n t = (zmax - qz) / vz;\n if ( t < limit ) {\n px = qx + t * vx;\n py = qy + t * vy;\n if ( xmin <= px && px <= xmax &&\n ymin <= py && py <= ymax ) {\n // ray 線分は AABB の zmax 面内で交差\n return false;\n }\n }\n }\n\n // ray 線分と AABB は交差しない\n return true;\n }\n\n\n /**\n * @summary エンティティのメッシュを削除\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n *\n * @private\n */\n _removeEntityMeshes( producer )\n {\n for ( let node of this._meshes ) {\n node.removeEntityMesh( producer );\n }\n }\n\n\n /**\n * @summary エンティティ辞書を取得\n *\n * @return {Map.}\n *\n * @private\n */\n _getEntityMap()\n {\n if ( this._entity_map === null ) {\n // 存在しないので新たに生成する\n\n let parent_map = this._parent._getEntityMap(); // 親の辞書\n let entity_map = new Map();\n\n for ( let [producer, isfull] of parent_map ) {\n if ( isfull ) {\n // 親が FULL なので、子も FULL\n entity_map.set( producer, true );\n }\n else {\n switch ( producer.getAreaStatus( this ) ) {\n case Entity.AreaStatus.PARTIAL:\n entity_map.set( producer, false );\n break;\n case Entity.AreaStatus.FULL:\n entity_map.set( producer, true );\n break;\n default: // Entity.AreaStatus.EMPTY\n break;\n }\n }\n }\n\n this._entity_map = entity_map;\n }\n\n return this._entity_map;\n }\n\n}\n\n\n/**\n * @summary 球面分割数の係数\n * @type {number}\n * @constant\n */\nFlake.ε = 0.0625;\n\n/**\n * @summary 標高下限係数\n * @type {number}\n * @constant\n */\nFlake.Fm = -2.0;\n\n/**\n * @summary 標高上限係数\n * @type {number}\n * @constant\n */\nFlake.Fp = 2.0;\n\n\nFlake.πr = Math.PI * GeoMath.EARTH_RADIUS;\n\nFlake._temp_positions = function() {\n var p = [];\n for ( var i = 0; i < 4; ++i ) { p.push( GeoMath.createVector3() ); }\n return p;\n}();\nFlake._temp_ray_1 = GeoMath.createVector3();\nFlake._temp_ray_2 = GeoMath.createVector3();\nFlake._temp_ray_3 = GeoMath.createVector3();\nFlake._temp_ray_4 = GeoMath.createVector3();\nFlake._temp_ray_5 = GeoMath.createVector3();\nFlake._temp_ray_6 = GeoMath.createVector3();\nFlake._temp_ray_7 = GeoMath.createVector3();\nFlake._temp_ray_8 = GeoMath.createVector3();\nFlake._temp_ray_9 = GeoMath.createVector3();\nFlake._temp_ray_10 = GeoMath.createVector3();\nFlake._temp_ray_11 = GeoMath.createVector3();\n\n\n/**\n * @summary 履歴統計\n * @memberof mapray.Globe\n * @private\n */\nclass HistStats {\n\n constructor()\n {\n this._history = [];\n this._max_value = 0;\n this._hsize = 200; // >= 3\n }\n\n /**\n * @summary 最大値を取得\n */\n getMaxValue( value )\n {\n var history = this._history;\n var old_max = this._max_value;\n\n if ( history.length < this._hsize ) {\n // 追加のみ\n if ( value > old_max ) {\n this._max_value = value;\n }\n }\n else {\n // 追加と削除\n if ( value >= old_max ) {\n // 最大値は value に変わる\n this._max_value = value;\n history.shift();\n }\n else if ( history[0] < old_max ) {\n // 最大値は変わらず\n history.shift();\n }\n else {\n // 最大値は変わる可能性がある\n history.shift();\n this._max_value = HistStats._find_max( history );\n }\n }\n\n history.push( value );\n\n return this._max_value;\n }\n\n static\n _find_max( history )\n {\n var max_value = history[0];\n\n var length = history.length;\n for ( var i = 1; i < length; ++i ) {\n var value = history[i];\n if ( value > max_value ) {\n max_value = value;\n }\n }\n\n return max_value;\n }\n\n}\n\n\n/**\n * @summary メッシュ管理ノード\n * @memberof mapray.Globe\n * @private\n */\nclass MeshNode {\n\n /**\n * @summary 初期化\n * @param {mapray.Globe.Flake} flake 所有者\n * @param {mapray.DemBinary} dem DEM バイナリ\n * @param {number[]} dpows 分割指数\n */\n constructor( flake, dem, dpows )\n {\n this._flake = flake;\n this._dem = dem;\n this._dpows = Array.from( dpows );\n this._aframe = -1;\n\n // 地表のメッシュ\n this._base_mesh = new FlakeMesh( flake._globe.glenv, flake, dpows, dem );\n\n // エンティティのメッシュ\n // key: FlakePrimitiveProducer\n // value: Mesh | CACHED_EMPTY_MESH\n this._entity_meshes = new Map();\n\n // メッシュ数をカウントアップ\n flake._globe._num_cache_meshes += 1;\n }\n\n /**\n * @summary FlakeRenderObject インスタンスを取得\n *\n * @return {mapray.FlakeRenderObject}\n */\n getRenderObject()\n {\n let flake = this._flake;\n let fro = new FlakeRenderObject( flake, flake._globe.glenv, this._base_mesh );\n\n // fro にエンティティ毎のデータを追加\n for ( let producer of flake.getEntityProducers() ) {\n // producer に対応するキャッシュされた Mesh\n let mesh = this._getEntityMesh( producer );\n\n if ( mesh === CACHED_EMPTY_MESH ) {\n // 空メッシュとしてキャッシュされている --> fro に追加しない\n continue;\n }\n\n if ( mesh === null ) {\n // メッシュがキャッシュに存在しないので、メッシュを生成してキャッシュする\n mesh = producer.createMesh( flake, this._dpows, this._dem );\n this._setEntityMesh( producer, mesh );\n\n if ( mesh === null ) {\n // 空メッシュとしてキャッシュされた --> fro に追加しない\n continue;\n }\n }\n\n // fro にエンティティを追加\n fro.addEntityData( mesh, producer );\n }\n\n return fro;\n }\n\n /**\n * @summary 一致するか?\n * @param {mapray.DemBinary} dem DEM バイナリ\n * @param {number[]} dpows 分割指数\n * @return {boolean} 一致するとき true, 一致しないとき false\n */\n match( dem, dpows )\n {\n return (this._dem === dem) && (this._dpows[0] === dpows[0]) && (this._dpows[1] === dpows[1]);\n }\n\n /**\n * @summary アクセスフレームを更新\n */\n touch()\n {\n var globe = this._flake._globe;\n if ( this._aframe !== globe._frame_counter ) {\n this._aframe = globe._frame_counter;\n globe._num_touch_meshes += 1;\n }\n }\n\n /**\n * @summary ノードを破棄\n */\n dispose()\n {\n if ( this._base_mesh === null ) {\n // すでに破棄されている\n return;\n }\n\n var flake = this._flake;\n\n // Flake から this ノードを削除\n var meshes = flake._meshes;\n var length = meshes.length;\n for ( var i = 0; i < length; ++i ) {\n if ( meshes[i] === this ) {\n meshes.splice( i, 1 );\n break;\n }\n }\n\n // メッシュを破棄\n this._base_mesh.dispose();\n this._base_mesh = null;\n\n for ( let mesh of this._entity_meshes.values() ) {\n if ( mesh instanceof Mesh ) {\n mesh.dispose();\n }\n }\n\n // メッシュ数をカウントダウン\n flake._globe._num_cache_meshes -= 1;\n }\n\n /**\n * @summary 削減用の MeshNode 比較\n * @param {mapray.Globe.MeshNode} other 比較対象\n * @return {number} 比較値\n * @package\n */\n compareForReduce( other )\n {\n // 最近アクセスしたものを優先\n var a = this;\n var b = other;\n return b._aframe - a._aframe;\n }\n\n\n /**\n * @summary エンティティのメッシュを削除\n *\n * @desc\n * producer に対応するメッシュが存在すれば削除する。
\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n */\n removeEntityMesh( producer )\n {\n this._entity_meshes.delete( producer );\n }\n\n\n /**\n * @summary エンティティのメッシュを取得\n *\n * @desc\n * producer に対応するメッシュを取得する。
\n * ただし存在しないとき null, 空メッシュが設定されているときは CACHED_EMPTY_MESH を返す。
\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n *\n * @return {?(mapray.Mesh|CACHED_EMPTY_MESH)}\n *\n * @private\n */\n _getEntityMesh( producer )\n {\n let mesh = this._entity_meshes.get( producer );\n\n return (mesh !== undefined) ? mesh : null;\n }\n\n\n /**\n * @summary エンティティのメッシュを設定\n *\n * @desc\n * producer に対応するメッシュを設定する。
\n * 空メッシュを設定するときは mesh に null を指定する。
\n *\n * @param {mapray.Entity.FlakePrimitiveProducer} producer\n * @param {?mapray.Mesh} mesh\n *\n * @private\n */\n _setEntityMesh( producer, mesh )\n {\n let value = (mesh !== null) ? mesh : CACHED_EMPTY_MESH;\n\n this._entity_meshes.set( producer, value );\n }\n\n}\n\n\n/**\n * @summary DEM 状態の列挙型\n * @enum {object}\n * @memberof mapray.Globe\n * @constant\n */\nvar DemState = {\n /**\n * DEM タイルが存在しない\n */\n NONE: { id: \"NONE\" },\n\n /**\n * DEM タイルが存在する\n */\n LOADED: { id: \"LOADED\" },\n\n /**\n * DEM タイルをリクエスト中\n */\n REQUESTED: { id: \"REQUESTED\" },\n\n /**\n * DEM タイルのリクエストに失敗\n */\n FAILED: { id: \"FAILED\" }\n};\n\n\n/**\n * @summary キャッシュされた空メッシュを表す\n *\n * @memberof mapray.Globe\n * @constant\n */\nconst CACHED_EMPTY_MESH = { id: \"CACHED_EMPTY_MESH\" };\n\n\nexport default Globe;\n","/**\n * @summary 描画地表断片\n * @memberof mapray\n * @private\n */\nclass RenderFlake {\n\n /**\n * @param {mapray.Globe.Flake} flake 地表断片\n */\n constructor( flake )\n {\n /**\n * @summary 地表断片\n * @member mapray.RenderFlake#flake\n * @type {mapray.Globe.Flake}\n */\n this.flake = flake;\n\n /**\n * @summary 地表詳細レベル (LOD)\n * @member mapray.RenderFlake#lod\n * @type {number}\n */\n\n /**\n * @summary LOD (左下)\n * @member mapray.RenderFlake#lod_00\n * @type {number}\n */\n\n /**\n * @summary LOD (右下)\n * @member mapray.RenderFlake#lod_10\n * @type {number}\n */\n\n /**\n * @summary LOD (左上)\n * @member mapray.RenderFlake#lod_01\n * @type {number}\n */\n\n /**\n * @summary LOD (右上)\n * @member mapray.RenderFlake#lod_11\n * @type {number}\n */\n }\n\n\n /**\n * @summary レンダリングオブジェクトを検索\n *\n * @return {mapray.FlakeRenderObject}\n */\n getRenderObject()\n {\n return this.flake.getRenderObject( this.lod );\n }\n\n}\n\n\nexport default RenderFlake;\n","import GeoMath from \"./GeoMath\";\nimport RenderFlake from \"./RenderFlake\";\n\n\n/**\n * @summary 描画地表断片を収集するツール\n * @memberof mapray.RenderStage\n * @private\n */\nclass FlakeCollector {\n\n /**\n * @param {mapray.RenderStage} stage 所有者である RenderStage\n */\n constructor( stage )\n {\n this._setupViewVectors( stage );\n this._setupClipPlanes( stage );\n\n var viewer = stage._viewer;\n var dem_provider = viewer.dem_provider;\n var tile_texture_cache = viewer.tile_texture_cache;\n\n this._min_image_z = tile_texture_cache.getImageZMin();\n\n var dem_zbias = GeoMath.LOG2PI - dem_provider.getResolutionPower() + 1; // b = log2π - ρ + 1\n this._max_zbias = Math.max( tile_texture_cache.getImageZBias(), dem_zbias );\n\n this._globe = viewer.globe;\n this._rflake_list = [];\n\n // デバッグ統計\n this._debug_stats = viewer.debug_stats;\n if ( this._debug_stats ) {\n this._num_procA_flakes = 0;\n this._num_procB_flakes = 0;\n }\n\n // 事前生成オブジェクト\n this._view_dir_N = GeoMath.createVector3();\n this._view_dir_V = GeoMath.createVector3();\n }\n\n\n /**\n * @private\n */\n _setupViewVectors( stage )\n {\n var view_to_gocs = stage._view_to_gocs;\n var pixel_step = stage._pixel_step;\n\n var view_pos_Q = GeoMath.createVector3();\n var view_dir_wU = GeoMath.createVector3();\n\n // 地表詳細レベル (LOD) 計算用の Q, w*U ベクトルを設定\n view_pos_Q[0] = view_to_gocs[12];\n view_pos_Q[1] = view_to_gocs[13];\n view_pos_Q[2] = view_to_gocs[14];\n\n view_dir_wU[0] = -view_to_gocs[ 8] * pixel_step;\n view_dir_wU[1] = -view_to_gocs[ 9] * pixel_step;\n view_dir_wU[2] = -view_to_gocs[10] * pixel_step;\n\n /**\n * @summary 位置ベクトル Q\n * @member mapray.FlakeCollector#_view_pos_Q\n * @type {mapray.Vector3}\n * @private\n * @see doc/ImageLevelCalculation.txt\n */\n this._view_pos_Q = view_pos_Q;\n\n /**\n * @summary ベクトル w * U\n * @member mapray.FlakeCollector#_view_dir_wU\n * @type {mapray.Vector3}\n * @private\n * @see doc/ImageLevelCalculation.txt\n */\n this._view_dir_wU = view_dir_wU;\n }\n\n\n /**\n * @private\n */\n _setupClipPlanes( stage )\n {\n var view_to_gocs = stage._view_to_gocs;\n var gocs_to_view = stage._gocs_to_view;\n var volume_planes = stage._volume_planes;\n var clip_planes = [];\n\n // 地表遮蔽カリング平面\n var root_flake = stage._viewer._globe.root_flake;\n var rmin = GeoMath.EARTH_RADIUS + root_flake.height_min; // 最小半径\n var rmax = GeoMath.EARTH_RADIUS + root_flake.height_max; // 最大半径\n\n // P (視点位置)\n var px = view_to_gocs[12];\n var py = view_to_gocs[13];\n var pz = view_to_gocs[14];\n\n // q = √[(P.P - rmin^2)(rmax^2 - rmin^2)] - rmin^2\n var p2 = px*px + py*py + pz*pz;\n var rmin2 = rmin*rmin;\n var rmax2 = rmax*rmax;\n var q = Math.sqrt( (p2 - rmin2) * (rmax2 - rmin2) ) - rmin2;\n\n // L = / ‖P‖\n var plane = GeoMath.createVector4();\n var recip = 1 / Math.sqrt( p2 );\n plane[0] = px * recip;\n plane[1] = py * recip;\n plane[2] = pz * recip;\n plane[3] = q * recip;\n clip_planes.push( plane );\n\n // L を基とした遠方距離\n var far_dist = Math.sqrt( p2 + rmax2 + 2*q );\n\n // 視体積平面を取得して、地心直交座標系に変換\n // (直交変換なので x, y, z は正規化されている)\n for ( var i = 0; i < 6; ++i ) {\n var src_plane = volume_planes[i];\n var dst_plane = GeoMath.createVector4();\n\n if ( i == 1 && src_plane[3] > far_dist ) {\n // 遠方平面が必要以上に遠いとき far_dist に置き換える\n src_plane = GeoMath.createVector4( src_plane );\n src_plane[3] = far_dist;\n }\n\n GeoMath.transformPlane_A( gocs_to_view, src_plane, dst_plane );\n\n clip_planes.push( dst_plane );\n }\n\n this._clip_planes = clip_planes;\n }\n\n\n /**\n * @summary 描画地表断片を収集\n * @return {mapray.RenderFlake[]} 収集され描画地表断片の集合\n */\n traverse()\n {\n this._collectFlakes( this._globe.root_flake );\n\n // デバッグ統計\n if ( this._debug_stats ) {\n this._debug_stats.num_procA_flakes = this._num_procA_flakes;\n this._debug_stats.num_procB_flakes = this._num_procB_flakes;\n }\n\n return this._rflake_list;\n }\n\n\n /**\n * @private\n */\n _collectFlakes( flake )\n {\n if ( this._debug_stats !== null ) {\n this._num_procA_flakes += 1;\n }\n\n if ( flake.isInvisible( this._clip_planes ) ) {\n // 地表タイルが見えないので描画しない\n return;\n }\n\n if ( flake.z < this._min_image_z ) {\n // 地表タイルより小さな画像タイルしかない\n this._collectNextLevelFlakes( flake ); // 地表タイルを分割\n return;\n }\n\n if ( this._debug_stats !== null ) {\n this._num_procB_flakes += 1;\n }\n\n // 地表断片の詳細レベルの範囲\n var range = this._getLevelOfDetailRange( flake );\n var zt = range.mid + this._max_zbias; // 最大タイルレベル\n\n if ( range.max - range.min > FlakeCollector.MAX_LOD_INTERVAL || zt > flake.z ) {\n // 地表断片の LOD 幅が閾値より大きい\n // or 最大タイルレベル > 地表断片レベル\n this._collectNextLevelFlakes( flake ); // 地表断片を分割\n return;\n }\n\n // リストに RenderFlake を追加\n this._addRenderFlake( flake, range );\n }\n\n\n /**\n * @private\n */\n _collectNextLevelFlakes( flake )\n {\n for ( var v = 0; v < 2; ++v ) {\n for ( var u = 0; u < 2; ++u ) {\n this._collectFlakes( flake.newChild( u, v ) );\n }\n }\n }\n\n\n /**\n * @summary 地表断片の詳細レベルの範囲を取得\n * @private\n */\n _getLevelOfDetailRange( flake )\n {\n var pi = Math.PI;\n var z = flake.z;\n var x = flake.x;\n var y = flake.y;\n\n // 座標範囲 (単位球メルカトル座標系)\n var msize = Math.pow( 2, 1 - z ) * pi;\n var mx_min = -pi + x * msize;\n var my_min = pi - (y + 1) * msize;\n\n var max_mstep = pi / 32;\n var mcount = Math.ceil( msize / max_mstep );\n var mstep = msize / mcount;\n\n var r = GeoMath.EARTH_RADIUS + flake.base_height;\n var Q = this._view_pos_Q;\n var wU = this._view_dir_wU;\n\n var N = this._view_dir_N;\n var V = this._view_dir_V;\n var dMin = Number.MAX_VALUE;\n var dMax = -Number.MAX_VALUE;\n\n for ( var iy = 0, my = my_min; iy < mcount + 1; ++iy, my += mstep ) {\n var ey = Math.exp( my );\n var ey2 = ey * ey;\n var sinφ = (ey2 - 1) / (ey2 + 1);\n var cosφ = 2 * ey / (ey2 + 1);\n var denom = 1 / (r * cosφ);\n for ( var ix = 0, mx = mx_min; ix < mcount + 1; ++ix, mx += mstep ) {\n var sinλ = Math.sin( mx );\n var cosλ = Math.cos( mx );\n\n // N\n N[0] = cosφ * cosλ;\n N[1] = cosφ * sinλ;\n N[2] = sinφ;\n\n // V = r N - Q\n V[0] = r * N[0] - Q[0];\n V[1] = r * N[1] - Q[1];\n V[2] = r * N[2] - Q[2];\n\n // w U.V\n var wUV = GeoMath.dot3( wU, V );\n if ( wUV <= 0 ) {\n // 頂点が視点の後ろ側\n return { min: -1000, max: 1000, mid: 0 };\n }\n\n // w U.(r N - Q)\n // d = ---------------\n // r Cos[φ]\n var deriv = wUV * denom;\n\n // 最大最小を更新\n dMin = Math.min( dMin, deriv );\n dMax = Math.max( dMax, deriv );\n }\n }\n\n var lodMin = -Math.maprayLog2( dMax ); // Log2[1/dMax]\n var lodMax = -Math.maprayLog2( dMin ); // Log2[1/dMin]\n\n return {\n min: lodMin,\n max: lodMax,\n mid: (lodMin + lodMax) / 2\n };\n }\n\n\n /**\n * @summary 単位球メルカトル座標 x, y の地表詳細レベルを計算\n * @desc\n *
以下の値が設定されていなければならない。
\n * \n * - this._view_pos_Q
\n * - this._view_dir_wU
\n *
\n * @param {number} x X 座標\n * @param {number} y Y 座標\n * @param {number} r GOGS 原点からの距離 (Meters)\n * @return {number} 地表詳細レベル\n * @private\n */\n _calcLOD( x, y, r )\n {\n var sinλ = Math.sin( x );\n var cosλ = Math.cos( x );\n var ey = Math.exp( y );\n var ey2 = ey * ey;\n var sinφ = (ey2 - 1) / (ey2 + 1);\n var cosφ = 2 * ey / (ey2 + 1);\n\n // N\n var N = this._view_dir_N;\n N[0] = cosφ * cosλ;\n N[1] = cosφ * sinλ;\n N[2] = sinφ;\n\n // V = r N - Q\n var V = this._view_dir_V;\n var Q = this._view_pos_Q;\n V[0] = r * N[0] - Q[0];\n V[1] = r * N[1] - Q[1];\n V[2] = r * N[2] - Q[2];\n\n // w U.V\n var wU = this._view_dir_wU;\n var wUV = GeoMath.dot3( wU, V ); // > 0 (表示される Flake 前提なので正数)\n\n // r Cos[φ]\n // 1/d = ---------------\n // w U.(r N - Q)\n var inv_d = r * cosφ / wUV;\n\n // Log2[1/d]\n return Math.maprayLog2( inv_d );\n }\n\n\n /**\n * @summary 四隅の LOD を設定\n * @desc\n * rflake に以下のプロパティを設定する。
\n * \n * - rflake.lod_00
\n * - rflake.lod_10
\n * - rflake.lod_01
\n * - rflake.lod_11
\n *
\n * @private\n */\n _setCornerLODs( rflake )\n {\n var pi = Math.PI;\n var flake = rflake.flake;\n var z = flake.z;\n var x = flake.x;\n var y = flake.y;\n\n // 座標範囲 (単位球メルカトル座標系)\n var msize = Math.pow( 2, 1 - z ) * pi;\n var mx_min = -pi + x * msize;\n var mx_max = -pi + (x + 1) * msize;\n var my_min = pi - (y + 1) * msize;\n var my_max = pi - y * msize;\n\n // GOCS 原点からの距離\n var r = GeoMath.EARTH_RADIUS + flake.base_height;\n\n // 四隅の地表詳細レベル\n rflake.lod_00 = this._calcLOD( mx_min, my_min, r );\n rflake.lod_10 = this._calcLOD( mx_max, my_min, r );\n rflake.lod_01 = this._calcLOD( mx_min, my_max, r );\n rflake.lod_11 = this._calcLOD( mx_max, my_max, r );\n }\n\n\n /**\n * @summary 描画地表断片を追加\n * @private\n */\n _addRenderFlake( flake, range )\n {\n var rflake = new RenderFlake( flake );\n\n rflake.lod = range.mid;\n this._setCornerLODs( rflake );\n\n this._rflake_list.push( rflake );\n }\n\n}\n\n\n/**\n * @summary Flake に対する LOD の許容幅\n * @desc\n * 1つの Flake 全体に対する最小 LOD と最大 LOD の間の最大幅である。
\n * 有効な範囲は 0.0 < MAX_LOD_INTERVAL < 1.0 である。
\n * @type {number}\n * @constant\n */\nFlakeCollector.MAX_LOD_INTERVAL = 0.5;\n\n\nexport default FlakeCollector;\n","'use strict';\nvar $ = require('../internals/export');\nvar IndexedObject = require('../internals/indexed-object');\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar arrayMethodIsStrict = require('../internals/array-method-is-strict');\n\nvar nativeJoin = [].join;\n\nvar ES3_STRINGS = IndexedObject != Object;\nvar STRICT_METHOD = arrayMethodIsStrict('join', ',');\n\n// `Array.prototype.join` method\n// https://tc39.github.io/ecma262/#sec-array.prototype.join\n$({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD }, {\n join: function join(separator) {\n return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);\n }\n});\n","/**\n * @summary WebGL シェーダラッパー\n * @desc\n * 頂点シェーダとフラグメントシェーダのセットである。\n * @memberof mapray\n * @private\n */\nclass Shader {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {string} vs_code 頂点シェーダのソースコード\n * @param {string} fs_code フラグメントシェーダのソースコード\n * @exception {Error} コンパイルエラー\n */\n constructor( glenv, vs_code, fs_code )\n {\n this._glenv = glenv;\n\n try {\n /**\n * @summary 頂点シェーダオブジェクト\n * @member mapray.Shader#vs_object\n * @type {WebGLShader}\n * @readonly\n */\n this.vs_object = this._compile_shader( 'VERTEX_SHADER', vs_code );\n\n /**\n * @summary フラグメントシェーダオブジェクト\n * @member mapray.Shader#fs_object\n * @type {WebGLShader}\n * @readonly\n */\n this.fs_object = this._compile_shader( 'FRAGMENT_SHADER', fs_code );\n }\n catch ( e ) {\n var gl = glenv.context;\n if ( this.vs_object )\n gl.deleteShader( this.vs_object );\n if ( this.fs_object )\n gl.deleteShader( this.fs_object );\n throw e;\n }\n }\n\n\n /**\n * @summary シェーダを破棄\n */\n dispose()\n {\n var gl = this._glenv.context;\n if ( this.vs_object ) {\n gl.deleteShader( this.vs_object );\n this.vs_object = null;\n }\n if ( this.fs_object ) {\n gl.deleteShader( this.fs_object );\n this.fs_object = null;\n }\n }\n\n\n /**\n * @summary シェーダをコンパイル\n * @param {string} type 'VERTEX_SHADER' or 'FRAGMENT_SHADER'\n * @param {string} source ソースコード文字列\n * @return {WebGLShader} コンパイルされたシェーダオブジェクト\n * @exception {Error} コンパイルエラー\n * @private\n */\n _compile_shader( type, source )\n {\n var gl = this._glenv.context;\n var shader = gl.createShader( gl[type] );\n if ( !shader ) {\n throw new Error( type + \" オブジェクトの生成に失敗しました\" );\n }\n try {\n gl.shaderSource( shader, source );\n gl.compileShader( shader );\n if ( !gl.getShaderParameter( shader, gl.COMPILE_STATUS ) ) {\n // コンパイルエラー\n var log = gl.getShaderInfoLog( shader );\n throw new Error( type + \" のコンパイルに失敗: \" + log );\n }\n }\n catch ( e ) {\n gl.deleteShader( shader );\n throw e;\n }\n return shader;\n }\n\n}\n\n\nexport default Shader;\n","import Shader from \"./Shader\";\n\n\n/**\n * @summary マテリアル\n * @memberof mapray\n * @private\n */\nclass Material {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {string} vs_code 頂点シェーダのソースコード\n * @param {string} fs_code フラグメントシェーダのソースコード\n */\n constructor( glenv, vs_code, fs_code )\n {\n var shader = new Shader( glenv, vs_code, fs_code );\n this._gl = glenv.context;\n this._program = this._link_shaders( shader.vs_object, shader.fs_object );\n this._vertex_attribs = this._create_vertex_attribs();\n this._uniform_location = this._create_uniform_location();\n\n shader.dispose();\n }\n\n\n /**\n * @summary シェーダをリンク\n * @param {WebGLShader} vs 頂点シェーダ\n * @param {WebGLShader} fs フラグメントシェーダ\n * @return {WebGLProgram} リンクされたプログラムオブジェクト\n * @exception {Error} リンクエラー\n * @private\n */\n _link_shaders( vs, fs )\n {\n var gl = this._gl;\n var program = gl.createProgram();\n if ( !program ) {\n throw new Error( \"プログラムオブジェクトの生成に失敗しました\" );\n }\n try {\n gl.attachShader( program, vs );\n gl.attachShader( program, fs );\n gl.linkProgram( program );\n if ( !gl.getProgramParameter( program, gl.LINK_STATUS ) ) {\n // リンクエラー\n var log = gl.getProgramInfoLog( program );\n gl.detachShader( program, fs );\n gl.detachShader( program, vs );\n throw new Error( \"シェーダのリンクに失敗: \" + log );\n }\n }\n catch ( e ) {\n gl.deleteProgram( program );\n throw e;\n }\n return program;\n }\n\n\n /**\n * @summary 頂点属性情報を作成\n *\n * @return {array} 頂点属性名前とロケーションの配列\n * @private\n */\n _create_vertex_attribs()\n {\n var gl = this._gl;\n var program = this._program;\n var attribs = [];\n\n var num_items = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n for ( var i = 0; i < num_items; ++i ) {\n var info = gl.getActiveAttrib( program, i );\n var attrib = { name: info.name,\n location: gl.getAttribLocation( program, info.name ) };\n attribs.push( attrib );\n }\n\n return attribs;\n }\n\n\n /**\n * @summary uniform 変数のロケーション辞書を作成\n *\n * @return {object} ロケーション辞書\n * @private\n */\n _create_uniform_location()\n {\n var gl = this._gl;\n var program = this._program;\n var location = {};\n\n // Uniform 変数のロケーション\n var num_items = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n for ( var i = 0; i < num_items; ++i ) {\n var info = gl.getActiveUniform( program, i );\n location[info.name] = gl.getUniformLocation( program, info.name );\n }\n\n return location;\n }\n\n\n /**\n * @summary リソースを破棄\n */\n dispose()\n {\n var gl = this._gl;\n gl.deleteProgram( this._program );\n this._program = null;\n }\n\n\n /**\n * @summary プログラムを束縛\n */\n bindProgram()\n {\n var gl = this._gl;\n gl.useProgram( this._program );\n }\n\n\n /**\n * @summary 真偽値パラメータを設定\n * @param {string} name 変数名\n * @param {boolean} value 真偽値\n */\n setBoolean( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform1i( location, value ? 1 : 0 );\n }\n }\n\n\n /**\n * @summary 整数パラメータを設定\n * @param {string} name 変数名\n * @param {number} value 整数値\n */\n setInteger( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform1i( location, value );\n }\n }\n\n\n /**\n * @summary float パラメータを設定\n * @param {string} name 変数名\n * @param {number} value float 値\n */\n setFloat( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform1f( location, value );\n }\n }\n\n\n /**\n * @summary 2次ベクトルパラメータを設定\n * @param {string} name 変数名\n * @param {mapray.Vector2} value 2次ベクトル\n */\n setVector2( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform2fv( location, value );\n }\n }\n\n\n /**\n * @summary 3次ベクトルパラメータを設定\n * @param {string} name 変数名\n * @param {mapray.Vector3} value 3次ベクトル\n */\n setVector3( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform3fv( location, value );\n }\n }\n\n\n /**\n * @summary 4次ベクトルパラメータを設定\n * @param {string} name 変数名\n * @param {mapray.Vector4} value 4次ベクトル\n */\n setVector4( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniform4fv( location, value );\n }\n }\n\n\n /**\n * @summary 行列パラメータを設定\n * @param {string} name 変数名\n * @param {mapray.Matrix} value 行列\n */\n setMatrix( name, value )\n {\n var location = this._uniform_location[name];\n if ( location ) {\n var gl = this._gl;\n gl.uniformMatrix4fv( location, false, value );\n }\n }\n\n\n /**\n * @summary 頂点属性データを束縛\n *\n * @desc\n * mesh_attribs は頂点属性名から Mesh.AttribData インスタンスを取得する辞書である。
\n *\n * @param {object} mesh_attribs メッシュ側の頂点属性データの辞書\n */\n bindVertexAttribs( mesh_attribs )\n {\n var gl = this._gl;\n var mtl_attribs = this._vertex_attribs; // マテリアル側の頂点属性データ配列\n var num_attribs = mtl_attribs.length;\n\n for ( var i = 0; i < num_attribs; ++i ) {\n var mtl_attrib = mtl_attribs[i];\n var mesh_attrib = mesh_attribs[mtl_attrib.name];\n var location = mtl_attrib.location;\n\n if ( mesh_attrib !== undefined ) {\n // 頂点属性データを束縛\n gl.bindBuffer( gl.ARRAY_BUFFER, mesh_attrib.buffer );\n gl.enableVertexAttribArray( location );\n gl.vertexAttribPointer( location,\n mesh_attrib.num_components,\n mesh_attrib.component_type,\n mesh_attrib.normalized,\n mesh_attrib.byte_stride,\n mesh_attrib.byte_offset );\n }\n else {\n // メッシュ側に必要な頂点属性がないとき\n gl.disableVertexAttribArray( location );\n }\n }\n }\n\n\n /**\n * @summary テクスチャをバインド\n * @desc\n * 注意: 現行テクスチャ (Active Texture) も変更される。
\n * @param {number} unit テクスチャユニット番号\n * @param {WebGLTexture} texture テクスチャオブジェクト\n */\n bindTexture2D( unit, texture )\n {\n var gl = this._gl;\n gl.activeTexture( gl.TEXTURE0 + unit );\n gl.bindTexture( gl.TEXTURE_2D, texture );\n }\n\n}\n\n\nexport default Material;\n","import Material from \"./Material\";\nimport GeoMath from \"./GeoMath\";\n\n\n/**\n * @summary 地表断片マテリアル\n * @memberof mapray.RenderStage\n * @extends mapray.Material\n * @private\n */\nclass FlakeMaterial extends Material {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n * @param {string} vs_code 頂点シェーダのソースコード\n * @param {string} fs_code フラグメントシェーダのソースコード\n */\n constructor( viewer, vs_code, fs_code )\n {\n super( viewer.glenv, vs_code, fs_code );\n\n // シェーダ用の事前生成オブジェクト\n this._flake_to_clip = GeoMath.createMatrixf();\n }\n\n\n /**\n * @summary 描画回数\n * @return {number}\n * @abstract\n */\n numDrawings()\n {\n return 1;\n }\n\n\n /**\n * @summary ワイヤーフレーム表示か?\n * @return {boolean}\n * @abstract\n */\n isWireframe()\n {\n return false;\n }\n\n\n /**\n * @summary 地表断片のパラメータを設定\n *\n * @param {mapray.RenderStage} stage 呼び出し側オブジェクト\n * @param {mapray.RenderFlake} rflake 描画地表断片\n * @param {mapray.FlakeMesh} mesh 地表断片メッシュ\n * @param {number} index 描画インデックス\n * @return {boolean} 描画の有無\n *\n * @abstract\n */\n setFlakeParameter( stage, rflake, mesh, index )\n {\n return false;\n }\n\n\n /**\n * @summary 地表断片の共通パラメータを設定\n *\n * @param {mapray.RenderStage} stage 呼び出し側オブジェクト\n * @param {mapray.FlakeMesh} mesh 地表断片メッシュ\n * @protected\n */\n setCommonParameter( stage, mesh )\n {\n mesh.mul_flake_to_gocs( stage._gocs_to_clip, this._flake_to_clip );\n this.setMatrix( \"u_obj_to_clip\", this._flake_to_clip );\n }\n\n}\n\n\nexport default FlakeMaterial;\n","/**\n * @summary 地図画像プロバイダ\n * @classdesc\n * レンダラーに地図画像を与えるための抽象クラスである。
\n *\n * このインスタンスには状態 ( {@link mapray.ImageProvider.Status} 型) があり、{@link mapray.ImageProvider#status|status()}\n * メソッドにより状態を確認することができる。
\n *\n *
初期状態は READY または NOT_READY でなければならず、状態の変化は NOT_READY から READY または NOT_READY から FAILED しか存在しない。
\n *
READY 以外の状態では {@link mapray.ImageProvider#status|status()} を除くメソッドを呼び出すことはできない。
\n *\n *
初期状態が NOT_READY になる可能性があるプロバイダは、{@link mapray.ImageProvider#status|status()} メソッドをオーバーライドする必要がある。
\n *\n * 以下の抽象メソッドは既定の動作がないので、利用者はこれらのメソッドをオーバーライドした具象クラスを使用しなければならない。
\n * \n * - {@link mapray.ImageProvider#requestTile|requestTile()}
\n * - {@link mapray.ImageProvider#cancelRequest|cancelRequest()}
\n * - {@link mapray.ImageProvider#getImageSize|getImageSize()}
\n * - {@link mapray.ImageProvider#getZoomLevelRange|getZoomLevelRange()}
\n *
\n *\n * @memberof mapray\n * @abstract\n * @protected\n * @see mapray.StandardImageProvider\n * @see mapray.Viewer\n */\nclass ImageProvider {\n\n /**\n * @summary 状態の取得\n * @desc\n * 現在の ImageProvider 状態を返す。
\n * callback を与えたとき、状態が NOT_READY から READY または FAILED に変化したときに callback が呼び出される。\n * NOT_READY 以外の状態で callback 与えても、それは無視されコールバック関数は登録されない。
\n *\n * @param {mapray.ImageProvider.StatusCallback} [callback] 状態変化コールバック関数\n * @return {mapray.ImageProvider.Status} 現在の ImageProvider 状態\n * @abstract\n */\n status( callback )\n {\n return Status.READY;\n }\n\n\n /**\n * @summary 地図タイル画像を要求\n * @desc\n * 座標が (z, x, y) の地図タイル画像を要求する。
\n * 指定したタイル画像の取得が成功または失敗したときに callback が非同期に呼び出されなければならない。
\n * だたし [cancelRequest()]{@link mapray.ImageProvider#cancelRequest} により要求が取り消されたとき、callback は呼び出しても呼び出さなくてもよい。また非同期呼び出しである必要もない。
\n * @param {number} z ズームレベル\n * @param {number} x X タイル座標\n * @param {number} y Y タイル座標\n * @param {mapray.ImageProvider.RequestCallback} callback 要求コールバック関数\n * @return {object} 要求 ID ([cancelRequest()]{@link mapray.ImageProvider#cancelRequest} に与えるオブジェクト)\n * @abstract\n */\n requestTile( z, x, y, callback )\n {\n throw new Error( \"mapray.ImageProvider#requestTile() method has not been overridden.\" );\n }\n\n\n /**\n * @summary 地図タイル画像の要求を取り消す\n * [requestTile()]{@link mapray.ImageProvider#requestTile} による要求を可能であれば取り消す。
\n * @param {object} id 要求 ID ([requestTile()]{@link mapray.ImageProvider#requestTile} から得たオブジェクト)\n * @abstract\n */\n cancelRequest( id )\n {\n throw new Error( \"mapray.ImageProvider#cancelRequest() method has not been overridden.\" );\n }\n\n\n /**\n * @summary 地図タイル画像の寸法を取得\n * @desc\n * サーバーが提供する地図タイル画像の寸法をする。
\n * 地図タイル画像は正方形を前提とし、水平方向の画素数を返す。
\n * 制限: this が同じなら常に同じ値を返さなければならない。
\n * @return {number} 地図タイル画像の画素数\n * @abstract\n */\n getImageSize()\n {\n throw new Error( \"mapray.ImageProvider#getImageSize() method has not been overridden.\" );\n }\n\n\n /**\n * @summary 地図画像ズームレベルの範囲を取得\n * @desc\n * サーバーが提供する地図タイル画像のズームレベルの範囲を取得する。
\n * 制限: this が同じなら常に同じ範囲を返さなければならない。
\n * @return {mapray.ImageProvider.Range} ズームレベルの範囲\n * @abstract\n */\n getZoomLevelRange()\n {\n throw new Error( \"mapray.ImageProvider#getZoomLevelRange() method has not been overridden.\" );\n }\n\n}\n\n\n/**\n * @summary 地図画像ズームレベル範囲\n * @memberof mapray.ImageProvider\n * @see mapray.ImageProvider#getZoomLevelRange\n */\nclass Range {\n\n /**\n * @param {number} min 最小ズームレベル (0 または 0 より大きい整数)\n * @param {number} max 最大ズームレベル (min または min より大きい整数)\n */\n constructor( min, max )\n {\n this._min = min;\n this._max = max;\n }\n\n\n /**\n * @summary 最小ズームレベル\n * @type {number}\n * @readonly\n */\n get min() { return this._min; }\n\n\n /**\n * @summary 最大ズームレベル\n * @type {number}\n * @readonly\n */\n get max() { return this._max; }\n\n}\n\nImageProvider.Range = Range;\n\n\n/**\n * @summary 地図タイル画像要求コールバック関数型\n * @desc\n * 地図タイル画像の取得に成功または失敗したときに呼び出される関数の型である。
\n * この関数は [requestTile()]{@link mapray.ImageProvider#requestTile} の callback 引数に与える。
\n * 画像の取得に成功したときは、image に Image のインスタンス、失敗したときは null を与える。
\n * ただし [cancelRequest()]{@link mapray.ImageProvider#cancelRequest} により要求が取り消されたとき、コールバック関数の呼び出しは無視されるので image は任意の値でよい。
\n * @param {Image} image 地図タイル画像または null\n * @callback RequestCallback\n * @memberof mapray.ImageProvider\n */\n\n\n/**\n * @summary ImageProvider 状態の列挙型\n * @enum {object}\n * @memberof mapray.ImageProvider\n * @constant\n * @see mapray.ImageProvider#status\n */\nvar Status = {\n\n /**\n * 準備中\n */\n NOT_READY: { id: \"NOT_READY\" },\n\n /**\n * 準備完了\n */\n READY: { id: \"READY\" },\n\n /**\n * 失敗状態\n */\n FAILED: { id: \"FAILED\" }\n\n};\n\nImageProvider.Status = Status;\n\n\n/**\n * @summary 状態変化コールバック関数型\n *\n * @param {mapray.ImageProvider.Status} status READY または FAILED (NOT_READEY から遷移した状態)\n * @callback StatusCallback\n * @memberof mapray.ImageProvider\n */\n\n\nexport default ImageProvider;\n","import ImageProvider from \"./ImageProvider\";\n\n\n/**\n * @summary ダミー画像プロバイダ\n *\n * 状態は常に READY、レベル 0 のみの巨大画像、ただし画像は永遠に返さない。\n *\n * @memberof mapray\n * @extends mapray.ImageProvider\n * @private\n */\nclass EmptyImageProvider extends ImageProvider {\n\n /**\n */\n constructor()\n {\n super();\n }\n\n\n /**\n * @override\n */\n requestTile( z, x, y, callback )\n {\n return this;\n }\n\n\n /**\n * @override\n */\n cancelRequest( id )\n {\n }\n\n\n /**\n * @override\n */\n getImageSize()\n {\n return 4096;\n }\n\n\n /**\n * @override\n */\n getZoomLevelRange()\n {\n return new ImageProvider.Range( 0, 0 );\n }\n\n}\n\n\nexport default EmptyImageProvider;\n","/**\n * @summary タイルテクスチャ\n * @memberof mapray\n * @private\n * @see mapray.TileTextureCache\n */\nclass TileTexture {\n\n /**\n * @param {number} z 地図ズームレベル\n * @param {number} x X タイル座標\n * @param {number} y Y タイル座標\n * @param {WebGLTexture} texture テクスチャオブジェクト\n */\n constructor( z, x, y, texture )\n {\n /**\n * @summary 地図ズームレベル\n * @member mapray.TileTexture#z\n * @type {number}\n */\n this.z = z;\n\n /**\n * @summary X タイル座標\n * @member mapray.TileTexture#x\n * @type {number}\n */\n this.x = x;\n\n /**\n * @summary Y タイル座標\n * @member mapray.TileTexture#y\n * @type {number}\n */\n this.y = y;\n\n /**\n * @summary テクスチャオブジェクト\n * @member mapray.TileTexture#texture\n * @type {WebGLTexture}\n */\n this.texture = texture;\n }\n\n\n /**\n * @summary リソースを破棄\n * @param {WebGLRenderingContext} gl WebGL レンダリングコンテキスト\n */\n dispose( gl )\n {\n gl.deleteTexture( this.texture );\n this.texture = null;\n }\n\n}\n\n\nexport default TileTexture;\n","import ImageProvider from \"./ImageProvider\";\nimport EmptyImageProvider from \"./EmptyImageProvider\";\nimport TileTexture from \"./TileTexture\";\nimport GeoMath from \"./GeoMath\";\n\n\n/**\n * @summary タイルテクスチャの管理\n * @memberof mapray\n * @private\n * @see mapray.TileTexture\n */\nclass TileTextureCache {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {mapray.ImageProvider} provider 地図画像プロバイダ\n */\n constructor( glenv, provider )\n {\n this._glenv = glenv;\n this._provider = null;\n this._min_image_z = 0;\n this._max_image_z = 0;\n this._image_zbias = 0;\n\n var status_callback = ( status ) => {\n if ( status === ImageProvider.Status.READY ) {\n // EmptyImageProvider から本来の provider に切り替える\n this._flush();\n this._resetImageProvider( provider );\n }\n else if ( status === ImageProvider.Status.FAILED ) {\n // provider が READY 状態にならなかった\n console.error( \"ImageProvider.Status.FAILED in TileTextureCache\" );\n }\n };\n\n this._resetImageProvider( (provider.status( status_callback ) === ImageProvider.Status.READY) ? provider : new EmptyImageProvider() );\n\n // キャッシュを初期化\n this._croot = new CacheNode();\n\n // キャッシュ制御変数\n this._max_accesses = 0; // 最近のフレームの最大アクセスノード数\n this._frame_counter = 0; // 現行フレーム番号\n\n this._lower_bound = 1.0; // >= 1.0\n this._upper_bound = 1.2; // >= lower_bound\n\n // リクエスト制御変数\n this._num_requesteds = 0; // 現在の REQUESTED 状態のノード数\n this._max_requesteds = 75; // 最大 REQUESTED ノード数\n this._new_requesteds = []; // 新規リクエストのリスト\n\n // WebGL 関連\n var gl = glenv.context;\n\n var aniso_ext = glenv.EXT_texture_filter_anisotropic;\n if ( aniso_ext ) {\n this._aniso_ext = aniso_ext;\n this._max_aniso = gl.getParameter( aniso_ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n }\n this._use_mipmap = false;\n }\n\n\n /**\n * 画像プロバイダを再設定\n *\n * _provider\n * _min_image_z\n * _max_image_z\n * _image_zbias\n *\n * @private\n */\n _resetImageProvider( provider )\n {\n this._provider = provider;\n\n var renge = provider.getZoomLevelRange();\n this._min_image_z = renge.min;\n this._max_image_z = renge.max;\n\n this._image_zbias = Math.maprayLog2( 2 * Math.PI / provider.getImageSize() );\n }\n\n\n /**\n * すべてのリクエストを取り消す\n */\n cancel()\n {\n this._flush();\n }\n\n\n /**\n * キャッシュをフラッシュ\n * @private\n */\n _flush()\n {\n new NodeCanceller( this, this._croot ); // リクエストを取り消す\n this._croot = new CacheNode(); // 取り消したノードは使えないので、単純にすべて捨てる\n this._max_accesses = 0;\n // assert: this._num_requesteds == 0\n }\n\n\n /**\n * LOD からテクスチャの Z レベルを計算するバイアス値を取得\n *\n * @return {number} Log2[2Pi / size]\n */\n getImageZBias()\n {\n return this._image_zbias;\n }\n\n\n /**\n * @return {number} タイルの Z レベルの最小値\n */\n getImageZMin()\n {\n return this._min_image_z;\n }\n\n\n /**\n * @summary リクエスト待ちのタイルの個数を取得\n *\n * @return {number} リクエスト待ちのタイルの個数\n */\n getNumWaitingRequests()\n {\n return this._num_requesteds;\n }\n\n\n /**\n * @summary 先祖タイルテクスチャを検索\n * @desc\n *
[x, y, z] タイルの祖先の中で、現在キャッシュに存在する最大レベルのタイルテクスチャを検索し、hi に設定する。
\n *\n * ただし検索されるタイルのズームレベルが Z とすると、Z <= max( zlimit, this._min_image_z )\n * という条件から検索し、存在しなければ null となる。
\n *\n * hi より低いレベルにタイルが存在すれば、それを lo に設定し、存在しなければ lo に hi と同じタイルを設定する
\n *\n * プロバイダにもっと相応しいテクスチャが存在する可能性があれば、そのテクスチャを要求する。
\n *\n * 前提: z >= this._min_image_z && z >= zlimit
\n *\n * @param {number} z 地図ズームレベル\n * @param {number} x X タイル座標\n * @param {number} y Y タイル座標\n * @param {number} zlimit 先祖レベルの上限\n * @return {mapray.TileTexture[]} 先祖タイルテクスチャ配列 [hi, lo]\n */\n findNearestAncestors( z, x, y, zlimit )\n {\n var depth = 0;\n var d_min = this._min_image_z;\n\n var pow = Math.pow( 2, 1 - z );\n var xf = (x + 0.5) * pow;\n var yf = (y + 0.5) * pow;\n var node = this._croot;\n\n var u;\n var v;\n var index;\n var children;\n var child;\n\n // 最小レベルのノード ---> node, depth\n for ( ; depth < d_min; ++depth ) {\n u = Math.floor( xf ) % 2;\n v = Math.floor( yf ) % 2;\n index = u + 2*v;\n\n children = node.children;\n child = children[index];\n if ( child === null ) {\n child = new CacheNode();\n children[index] = child;\n }\n\n xf *= 2;\n yf *= 2;\n node = child;\n }\n\n var d_max = this._max_image_z;\n var d_lo = GeoMath.clamp( zlimit - 1, d_min, d_max );\n var d_hi = GeoMath.clamp( zlimit, d_min, d_max );\n var tex_lo = null;\n var tex_hi = null;\n\n if ( d_lo < d_hi ) {\n /* assert: (d_min < d_max) && (d_min < zlimit <= d_max) */\n\n for ( ; depth <= d_lo; ++depth ) {\n if ( node.state === NodeState.LOADED ) {\n // 候補テクスチャを更新\n tex_lo = node;\n }\n else if ( node.state === NodeState.NONE ) {\n // 新規リクエスト\n node.state = NodeState.REQUESTED;\n node.req_power = zlimit - depth;\n this._new_requesteds.push( [node, depth, Math.floor( 0.5 * xf ), Math.floor( 0.5 * yf )] );\n }\n else if ( node.state === NodeState.REQUESTED ) {\n // 要求度を更新\n node.updateRequestPower( zlimit - depth );\n }\n\n u = Math.floor( xf ) % 2;\n v = Math.floor( yf ) % 2;\n index = u + 2*v;\n\n children = node.children;\n child = children[index];\n if ( child === null ) {\n child = new CacheNode();\n children[index] = child;\n }\n\n xf *= 2;\n yf *= 2;\n node = child;\n }\n\n tex_hi = tex_lo;\n\n if ( node.state === NodeState.LOADED ) {\n // 候補テクスチャを更新\n tex_hi = node;\n }\n else if ( node.state === NodeState.NONE ) {\n // 新規リクエスト\n node.state = NodeState.REQUESTED;\n node.req_power = zlimit - depth;\n this._new_requesteds.push( [node, depth, Math.floor( 0.5 * xf ), Math.floor( 0.5 * yf )] );\n }\n else if ( node.state === NodeState.REQUESTED ) {\n // 要求度を更新\n node.updateRequestPower( zlimit - depth );\n }\n }\n else { // if d_lo == d_hi\n /* assert: (d_min == d_max) || (zlimit <= d_min) || (zlimit > d_max) */\n\n for ( ;; ++depth ) {\n if ( node.state === NodeState.LOADED ) {\n // 候補テクスチャを更新\n tex_lo = node;\n }\n else if ( node.state === NodeState.NONE ) {\n // 新規リクエスト\n node.state = NodeState.REQUESTED;\n node.req_power = zlimit - depth;\n this._new_requesteds.push( [node, depth, Math.floor( 0.5 * xf ), Math.floor( 0.5 * yf )] );\n }\n else if ( node.state === NodeState.REQUESTED ) {\n // 要求度を更新\n node.updateRequestPower( zlimit - depth );\n }\n\n if ( depth == d_lo ) {\n tex_hi = tex_lo;\n break;\n }\n\n u = Math.floor( xf ) % 2;\n v = Math.floor( yf ) % 2;\n index = u + 2*v;\n\n children = node.children;\n child = children[index];\n if ( child === null ) {\n child = new CacheNode();\n children[index] = child;\n }\n\n xf *= 2;\n yf *= 2;\n node = child;\n }\n\n // assert: tex_hi === tex_lo\n }\n\n node.touch();\n\n var result = TileTextureCache._findNearestAncestors_result;\n result[0] = (tex_hi !== null) ? tex_hi.data : null;\n result[1] = (tex_lo !== null) ? tex_lo.data : null;\n return result;\n }\n\n\n /**\n * @summary フレームの最後の処理\n */\n endFrame()\n {\n this._performNewRequests();\n\n var counter = new NodeCounter( this._croot, this._frame_counter );\n this._max_accesses = Math.max( counter.num_accesses, this._max_accesses );\n\n if ( counter.num_loadeds > this._upper_bound * this._max_accesses ) {\n var num_nodes = Math.floor( this._lower_bound * this._max_accesses );\n this._reduceCache( num_nodes );\n }\n\n ++this._frame_counter;\n }\n\n\n /**\n * @summary 新規リクエストを実行\n * @private\n */\n _performNewRequests()\n {\n // リクエスト数\n var num_requests = Math.min( this._max_requesteds - this._num_requesteds, this._new_requesteds.length );\n\n // 基準に基づき、新規リクエストを前半 (num_requests 個) と後半に分割\n this._new_requesteds.sort( function( a, b ) {\n var anode = a[0];\n var bnode = b[0];\n return bnode.req_power - anode.req_power;\n } );\n\n // リクエストを実行\n var self = this;\n this._new_requesteds.slice( 0, num_requests ).forEach( function( req ) {\n var node = req[0];\n var z = req[1];\n var x = req[2];\n var y = req[3];\n self._requestTileTexture( z, x, y, node );\n } );\n\n // リクエストしなかったノードを空に戻す\n this._new_requesteds.slice( num_requests ).forEach( function( req ) {\n var node = req[0];\n node.state = NodeState.NONE;\n // assert: node.data === null\n } );\n\n // 新規リクエストのリストをクリア\n this._new_requesteds.length = 0;\n }\n\n\n /**\n * @summary タイルテクスチャを要求\n * @param {number} z 地図ズームレベル\n * @param {number} x X タイル座標\n * @param {number} y Y タイル座標\n * @param {mapray.TileTextureCache.CacheNode} node 対象ノード\n * @private\n */\n _requestTileTexture( z, x, y, node )\n {\n node.data = this._provider.requestTile( z, x, y, image => {\n\n if ( node.state !== NodeState.REQUESTED ) {\n // キャンセルされているので無視\n return;\n }\n\n if ( image ) {\n node.data = new TileTexture( z, x, y, this._createTexture( image ) );\n node.state = NodeState.LOADED;\n }\n else {\n node.data = null;\n node.state = NodeState.FAILED;\n }\n --this._num_requesteds;\n\n } );\n\n ++this._num_requesteds;\n }\n\n\n /**\n * @summary テクスチャを生成\n * @desc\n * GL ステートの変更
\n * \n * - TEXTURE_2D_BINDING: null
\n * - UNPACK_FLIP_Y_WEBGL: false
\n *
\n * @param {Image} image 元画像\n * @return {WebGLTexture} 生成されたテクスチャ\n * @private\n */\n _createTexture( image )\n {\n var gl = this._glenv.context;\n var aniso_ext = this._aniso_ext;\n\n var target = gl.TEXTURE_2D;\n var texture = gl.createTexture();\n\n gl.bindTexture( target, texture );\n\n gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );\n gl.texImage2D( target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );\n gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );\n\n if ( this._use_mipmap ) {\n gl.generateMipmap( target );\n }\n\n gl.texParameteri( target, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\n gl.texParameteri( target, gl.TEXTURE_MIN_FILTER, this._use_mipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR );\n gl.texParameteri( target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\n gl.texParameteri( target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\n\n if ( aniso_ext ) {\n gl.texParameterf( gl.TEXTURE_2D, aniso_ext.TEXTURE_MAX_ANISOTROPY_EXT, this._max_aniso );\n }\n\n gl.bindTexture( target, null );\n\n return texture;\n }\n\n\n /**\n * @summary キャッシュを削減\n * @param {number} num_cnodes 目標ノード数\n * @private\n */\n _reduceCache( num_nodes )\n {\n var collector = new NodeCollector( this._croot );\n\n // 基準に基づき、ノードを前半 (num_cnodes 個) と後半に分割\n collector.nodes.sort( function( a, b ) {\n var aframe = b.aframe - a.aframe;\n if ( aframe == 0 ) {\n if ( (a.state === NodeState.LOADED) && (b.state === NodeState.LOADED) ) {\n return a.data.z - b.data.z;\n }\n }\n return aframe;\n } );\n\n\n // 後半のノードを削除\n var gl = this._glenv.context;\n collector.nodes.slice( num_nodes ).forEach( function( node ) {\n if ( node.state === NodeState.LOADED ) {\n node.data.dispose( gl );\n }\n node.state = NodeState.NONE;\n node.data = null;\n } );\n\n // NodeState.NONE の葉ノードを消去\n collector.clean();\n }\n\n}\n\n\n// クラス定数を定義\nTileTextureCache._findNearestAncestors_result = new Array( 2 );\n\n\n/**\n * @summary キャッシュノード\n *\n * @memberof mapray.TileTextureCache\n * @private\n */\nclass CacheNode {\n\n constructor()\n {\n this.children = [null, null, null, null];\n this.state = NodeState.NONE;\n this.data = null; // TileTexture オブジェクト、または取り消しオブジェクト\n this.req_power = -1; // 要求度\n this.aframe = -1; // 最終アクセスフレーム\n }\n\n /**\n * 要求度を更新\n */\n updateRequestPower( req_power )\n {\n if ( req_power > this.req_power ) {\n this.req_power = req_power;\n }\n }\n\n /**\n * このタイルにアクセスしたことにする\n */\n touch()\n {\n this.aframe = true;\n }\n\n}\n\n\n/**\n * @summary ノード数の計上\n *\n * this.num_loadeds: ロードされているタイル数\n * this.num_accesses: ロードされているタイルのうち、アクセスされたタイル数\n *\n * アクセスがあったノードに対して aframe を更新する。\n *\n * @memberof mapray.TileTextureCache\n * @private\n */\nclass NodeCounter {\n\n /**\n * @param {mapray.TileTextureCache.CacheNode} root 最上位ノード\n * @param {number} frame 現在のフレーム\n */\n constructor( root, frame )\n {\n this.num_loadeds = 0;\n this.num_accesses = 0;\n this._frame = frame;\n this._traverse( root );\n }\n\n /**\n * @private\n */\n _traverse( node )\n {\n var children = node.children;\n var isAccessed = (node.aframe === true);\n\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n isAccessed = this._traverse( child ) || isAccessed;\n }\n }\n\n if ( node.state === NodeState.LOADED ) {\n ++this.num_loadeds;\n if ( isAccessed ) {\n ++this.num_accesses;\n }\n }\n\n if ( isAccessed ) {\n // アクセスフレームを更新\n node.aframe = this._frame;\n }\n\n return isAccessed;\n }\n\n}\n\n\n/**\n * @summary ノード収集\n * @desc\n * NodeState.LOADED または NodeState.FAILED のノードを this.nodes に収集する。
\n *\n * @memberof mapray.TileTextureCache\n * @private\n */\nclass NodeCollector {\n\n /**\n * @param {mapray.TileTextureCache.CacheNode} root 最上位ノード\n */\n constructor( root )\n {\n this._root = root;\n this.nodes = [];\n this._traverse( root );\n }\n\n /**\n * @private\n */\n _traverse( node )\n {\n var state = node.state;\n if ( state === NodeState.LOADED || state === NodeState.FAILED ) {\n // LOADED または FAILED なら追加\n this.nodes.push( node );\n }\n\n var children = node.children;\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n this._traverse( child );\n }\n }\n }\n\n /**\n * @summary NodeState.NONE の葉ノードを消去\n */\n clean()\n {\n this._clean_recur( this._root );\n }\n\n /**\n * @return 自己と子孫がすべて NodeState.NONE のとき true, それいがいのとき false\n * @private\n */\n _clean_recur( node )\n {\n var isNodeNone = (node.state === NodeState.NONE);\n\n var children = node.children;\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n var isChildNone = this._clean_recur( child );\n if ( isChildNone === true ) {\n children[i] = null;\n }\n isNodeNone = isChildNone && isNodeNone;\n }\n }\n\n return isNodeNone;\n }\n\n}\n\n\n/**\n * @summary すべてのリクエストを取り消す\n * @memberof mapray.TileTextureCache\n * @private\n */\nclass NodeCanceller {\n\n /**\n * @param {mapray.TileTextureCache} owner 最上位ノード\n * @param {mapray.TileTextureCache.CacheNode} root 最上位ノード\n */\n constructor( owner, root )\n {\n this._owner = owner;\n this._traverse( root );\n }\n\n /**\n * @private\n */\n _traverse( node )\n {\n var children = node.children;\n for ( var i = 0; i < 4; ++i ) {\n var child = children[i];\n if ( child !== null ) {\n this._traverse( child );\n }\n }\n if ( node.state === NodeState.REQUESTED ) {\n var owner = this._owner;\n node.state = NodeState.NONE;\n owner._provider.cancelRequest( node.data );\n --owner._num_requesteds;\n }\n }\n\n}\n\n\n/**\n * @summary ノード状態の列挙型\n * @enum {object}\n * @memberof mapray.TileTextureCache\n * @constant\n */\nvar NodeState = {\n /**\n * タイルが存在しない\n */\n NONE: { id: \"NONE\" },\n\n /**\n * タイルが存在する\n */\n LOADED: { id: \"LOADED\" },\n\n /**\n * タイルをリクエスト中\n */\n REQUESTED: { id: \"REQUESTED\" },\n\n /**\n * タイルのリクエストに失敗\n */\n FAILED: { id: \"FAILED\" }\n};\n\n\nexport default TileTextureCache;\n","import FlakeMaterial from \"./FlakeMaterial\";\nimport wireframe_vs_code from \"./shader/wireframe.vert\";\nimport wireframe_fs_code from \"./shader/wireframe.frag\";\n\n\n/**\n * @summary 地表ワイヤーフレームマテリアル\n * @memberof mapray.RenderStage\n * @extends mapray.RenderStage.FlakeMaterial\n * @private\n */\nclass WireframeMaterial extends FlakeMaterial {\n\n /**\n * @param {mapray.Viewer} viewer 所有者 Viewer\n */\n constructor( viewer )\n {\n super( viewer, wireframe_vs_code, wireframe_fs_code );\n }\n\n\n /**\n * @override\n */\n isWireframe()\n {\n return true;\n }\n\n\n /**\n * @override\n */\n setFlakeParameter( stage, rflake, mesh, index )\n {\n this.setCommonParameter( stage, mesh );\n\n return true;\n }\n\n}\n\n\nexport default WireframeMaterial;\n","import ImageProvider from \"./ImageProvider\";\nimport TileTextureCache from \"./TileTextureCache\";\nimport SurfaceMaterial from \"./SurfaceMaterial\";\nimport WireframeMaterial from \"./WireframeMaterial\";\n\n\n/**\n * @summary 地図レイヤー\n * @classdesc\n * 地図レイヤーを表現するオブジェクトである。
\n *\n * @hideconstructor\n * @memberof mapray\n * @see mapray.LayerCollection\n */\nclass Layer {\n\n /**\n * @param {mapray.LayerCollection} owner 地図レイヤー管理\n * @param {object|mapray.ImageProvider} init 初期化プロパティ\n * @param {mapray.ImageProvider} init.image_provider 画像プロバイダ\n * @param {boolean} [init.visibility] 可視性フラグ\n * @param {number} [init.opacity] 不透明度\n * @param {Layer.LayerType} [init.type] レイヤータイプ\n */\n constructor( owner, init )\n {\n this._owner = owner;\n this._glenv = owner.glenv;\n this._viewer = owner.viewer;\n\n var props = (init instanceof ImageProvider) ? { image_provider: init } : init;\n this._image_provider = props.image_provider;\n this._visibility = props.visibility || true;\n this._opacity = props.opacity || 1.0;\n this._type = props.type === Layer.LayerType.NIGHT ? Layer.LayerType.NIGHT : Layer.LayerType.NORMAL;\n this._material = null;\n\n this._tile_cache = new TileTextureCache( this._glenv, this._image_provider );\n\n const render_cache = this._viewer._render_cache || (this._viewer._render_cache = {});\n if ( this._type === Layer.LayerType.NIGHT ) {\n if ( !render_cache.surface_night_material ) {\n render_cache.surface_night_material = new SurfaceMaterial( this._viewer, { nightMaterial: true } );\n }\n this._material = render_cache.surface_night_material;\n } else {\n if ( !render_cache.surface_material ) {\n render_cache.surface_material = new SurfaceMaterial( this._viewer );\n render_cache.wireframe_material = new WireframeMaterial( this._viewer );\n }\n this._material = render_cache.surface_material;\n }\n\n // プロバイダの状態が変化したら描画レイヤーを更新\n this._image_provider.status( (status) => { owner.dirtyDrawingLayers(); } );\n }\n\n\n /**\n * @summary 画像プロバイダを取得\n * @type {mapray.ImageProvider}\n * @readonly\n */\n get image_provider() { return this._image_provider; }\n\n\n /**\n * @summary 可視性フラグを取得\n * @type {boolean}\n * @readonly\n */\n get visibility() { return this._visibility; }\n\n\n /**\n * @summary 不透明度を取得\n * @type {number}\n * @readonly\n */\n get opacity() { return this._opacity; }\n\n\n /**\n * @summary タイプを取得\n * @type {LayerType}\n * @readonly\n */\n get type() { return this._type; }\n\n\n /**\n * @summary タイルテクスチャキャッシュを取得\n * @type {mapray.TileTextureCache}\n * @readonly\n * @package\n */\n get tile_cache() { return this._tile_cache; }\n\n\n /**\n * @summary 画像プロバイダを設定\n *\n * @param {mapray.ImageProvider} provider 画像プロバイダ\n */\n setImageProvider( provider )\n {\n if ( this._image_provider !== provider ) {\n // プロバイダを変更またはプロバイダの状態が変化したら描画レイヤーを更新\n this._owner.dirtyDrawingLayers();\n provider.status( (status) => { this._owner.dirtyDrawingLayers(); } );\n }\n\n this._image_provider = provider;\n\n // タイルキャッシュを再構築\n this._tile_cache.cancel();\n this._tile_cache = new TileTextureCache( this._glenv, provider );\n }\n\n\n /**\n * @summary 可視性フラグを設定\n *\n * @param {boolean} visibility 可視性フラグ\n */\n setVisibility( visibility )\n {\n if ( this._visibility != visibility ) {\n // レイヤーの可視性が変化したら描画レイヤーを更新\n this._owner.dirtyDrawingLayers();\n }\n\n this._visibility = visibility;\n }\n\n\n /**\n * @summary 不透明度を設定\n *\n * @param {number} opacity 不透明度\n */\n setOpacity( opacity )\n {\n this._opacity = opacity;\n }\n\n\n /**\n * @summary マテリアルを取得\n *\n * @return {mapray.SurfaceMaterial} マテリアル\n * @package\n */\n getMateral()\n {\n return this._material;\n }\n}\n\n\n/**\n * @summary レイヤータイプ\n * @enum {object}\n * @memberof mapray.Layer\n * @constant\n */\nconst LayerType = {\n\n\n /**\n * 通常のレイヤー\n */\n NORMAL: { id: \"NORMAL\" },\n\n\n /**\n * 夜部分のみ描画するレイヤー\n */\n NIGHT: { id: \"NIGHT\" }\n\n};\n\nLayer.LayerType = LayerType;\n\nexport default Layer;\n","import FlakeMaterial from \"./FlakeMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport TileTextureCache from \"./TileTextureCache\";\nimport surface_vs_code from \"./shader/surface.vert\";\nimport surface_fs_code from \"./shader/surface.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\nimport Layer from \"./Layer\";\n\n\n/**\n * @summary 地表面マテリアル\n * @memberof mapray.RenderStage\n * @extends mapray.RenderStage.FlakeMaterial\n * @private\n */\nclass SurfaceMaterial extends FlakeMaterial {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n */\n constructor( viewer, options = {} )\n {\n const preamble = SurfaceMaterial._getPreamble( options );\n\n super( viewer,\n preamble + surface_vs_code,\n preamble + ( options.ridMaterial ? rid_fs_code : surface_fs_code ) );\n\n this.bindProgram();\n this.setInteger( \"u_image_hi\", SurfaceMaterial.TEXUNIT_IMAGE_HI );\n this.setInteger( \"u_image_lo\", SurfaceMaterial.TEXUNIT_IMAGE_LO );\n\n this._viewer = viewer;\n this._tile_texture_cache = viewer.tile_texture_cache;\n this._layers = viewer.layers;\n this._dummy_tile_texture = this._createDummyTileTexture( viewer.glenv );\n this._image_zbias = 0;\n\n this._identity_matrix = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._flake_to_gocs = GeoMath.createMatrixf();\n }\n\n /**\n * @summary シェーダの前文を取得\n *\n * @param {object} options オプション指定\n * @param {boolean} [options.nightMaterial=false] 夜用マテリアルの場合 true\n *\n * @private\n */\n static\n _getPreamble( options )\n {\n const is_night = options.nightMaterial === true;\n\n const lines = [];\n\n // マクロの定義\n if ( is_night ) {\n lines.push( \"#define NIGHTIMAGE\" );\n }\n\n // lines を文字列にして返す\n return lines.join( \"\\n\" ) + \"\\n\\n\";\n }\n\n /**\n * @override\n */\n numDrawings()\n {\n return 1 + this._layers.numDrawingLayers();\n }\n\n\n /**\n * @override\n */\n setFlakeParameter( stage, rflake, mesh, index )\n {\n this.setCommonParameter( stage, mesh );\n\n var param = this._getMaterialParamater( rflake, index );\n\n if ( param !== null ) {\n const layer = this._layers.getDrawingLayer( index - 1 );\n\n this.setVector4( \"u_corner_lod\", param.corner_lod );\n\n this.setVector4( \"u_texcoord_rect_hi\", param.image_hi.texcoord_rect );\n this.setVector4( \"u_texcoord_rect_lo\", param.image_lo.texcoord_rect );\n\n this.setVector2( \"u_image_param\", [param.image_lo.lod,\n (param.image_hi.lod == param.image_lo.lod) ?\n 0 : 1 / (param.image_hi.lod - param.image_lo.lod)] );\n\n this.setFloat( \"u_opacity\", (index == 0) ? 1.0 : layer.opacity );\n\n if ( index > 0 && layer.type === Layer.LayerType.NIGHT ) {\n this.setVector3( \"u_sun_direction\", this._viewer.sun_direction );\n mesh.mul_flake_to_gocs( this._identity_matrix, this._flake_to_gocs );\n this.setMatrix( \"u_obj_to_gocs\", this._flake_to_gocs );\n }\n\n this.bindTexture2D( SurfaceMaterial.TEXUNIT_IMAGE_HI, param.image_hi.texture );\n this.bindTexture2D( SurfaceMaterial.TEXUNIT_IMAGE_LO, param.image_lo.texture );\n\n return true;\n }\n else {\n return false;\n }\n }\n\n\n /**\n * @summary SurfaceMaterial のパラメータを取得\n * @desc\n * \n * オブジェクト構造\n * {\n * // 四隅の地表詳細レベル\n * corner_lod: [lod_00, lod_10, lod_01, lod_11],\n *\n * // 高レベル画像の情報\n * image_hi: { lod: (number), texture: (WebGLTexture), texcoord_rect: [s, t, w, h] },\n *\n * // 低レベル画像の情報\n * image_lo: { lod: (number), texture: (WebGLTexture), texcoord_rect: [s, t, w, h] }\n * }\n *
\n * @private\n */\n _getMaterialParamater( rflake, index )\n {\n var tex_cache = (index == 0) ? this._tile_texture_cache : this._layers.getDrawingLayer( index - 1 ).tile_cache;\n this._image_zbias = tex_cache.getImageZBias();\n\n var flake = rflake.flake;\n var zg = flake.z;\n\n if ( zg < tex_cache.getImageZMin() ) {\n return null;\n }\n\n var x = flake.x;\n var y = flake.y;\n var zi = Math.ceil( rflake.lod + this._image_zbias );\n\n if ( zg < zi ) {\n return null;\n }\n\n var tiles = tex_cache.findNearestAncestors( zg, x, y, zi );\n if ( index >= 1 && tiles[0] === null ) {\n return null;\n }\n\n return {\n corner_lod: [rflake.lod_00, rflake.lod_10, rflake.lod_01, rflake.lod_11],\n image_hi: this._getImageParamater( tiles[0], zg, x, y, zi ),\n image_lo: this._getImageParamater( tiles[1], zg, x, y, zi - 1 )\n };\n }\n\n\n /**\n * @summary 画像パラメータを取得\n * @desc\n * \n * オブジェクト構造\n * {\n * lod: (number),\n * texture: (WebGLTexture),\n * texcoord_rect: [s, t, w, h]\n * }\n *
\n * @private\n */\n _getImageParamater( tile, zg, x, y, zi )\n {\n var pow;\n\n if ( tile !== null ) {\n pow = Math.pow( 2, tile.z - zg );\n return {\n lod: tile.z - this._image_zbias,\n texture: tile.texture,\n texcoord_rect: [x*pow - tile.x, 1 - (y + 1)*pow + tile.y, pow, pow]\n };\n }\n else {\n pow = Math.pow( 2, -zg );\n return {\n lod: -this._image_zbias,\n texture: this._dummy_tile_texture,\n texcoord_rect: [x*pow - Math.floor( pow * (x + 0.5) ), 1 - (y + 1)*pow + Math.floor( pow * (y + 0.5) ), pow, pow]\n };\n }\n }\n\n\n /**\n * @private\n */\n _createDummyTileTexture( glenv )\n {\n var gl = glenv.context;\n var target = gl.TEXTURE_2D;\n var texture = gl.createTexture();\n var pixels = [128, 128, 128, 255];\n\n gl.bindTexture( target, texture );\n gl.texImage2D( target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array( pixels ) );\n gl.bindTexture( target, null );\n\n return texture;\n }\n\n}\n\n\nSurfaceMaterial.TEXUNIT_IMAGE_HI = 0; // 高レベル画像のテクスチャユニット\nSurfaceMaterial.TEXUNIT_IMAGE_LO = 1; // 低レベル画像のテクスチャユニット\n\n\nexport default SurfaceMaterial;\n","'use strict';\nvar $ = require('../internals/export');\nvar $indexOf = require('../internals/array-includes').indexOf;\nvar arrayMethodIsStrict = require('../internals/array-method-is-strict');\nvar arrayMethodUsesToLength = require('../internals/array-method-uses-to-length');\n\nvar nativeIndexOf = [].indexOf;\n\nvar NEGATIVE_ZERO = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;\nvar STRICT_METHOD = arrayMethodIsStrict('indexOf');\nvar USES_TO_LENGTH = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });\n\n// `Array.prototype.indexOf` method\n// https://tc39.github.io/ecma262/#sec-array.prototype.indexof\n$({ target: 'Array', proto: true, forced: NEGATIVE_ZERO || !STRICT_METHOD || !USES_TO_LENGTH }, {\n indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {\n return NEGATIVE_ZERO\n // convert -0 to +0\n ? nativeIndexOf.apply(this, arguments) || 0\n : $indexOf(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\n","'use strict';\nvar $ = require('../internals/export');\nvar $reduce = require('../internals/array-reduce').left;\nvar arrayMethodIsStrict = require('../internals/array-method-is-strict');\nvar arrayMethodUsesToLength = require('../internals/array-method-uses-to-length');\n\nvar STRICT_METHOD = arrayMethodIsStrict('reduce');\nvar USES_TO_LENGTH = arrayMethodUsesToLength('reduce', { 1: 0 });\n\n// `Array.prototype.reduce` method\n// https://tc39.github.io/ecma262/#sec-array.prototype.reduce\n$({ target: 'Array', proto: true, forced: !STRICT_METHOD || !USES_TO_LENGTH }, {\n reduce: function reduce(callbackfn /* , initialValue */) {\n return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\n","var $ = require('../internals/export');\n\n// `Date.now` method\n// https://tc39.github.io/ecma262/#sec-date.now\n$({ target: 'Date', stat: true }, {\n now: function now() {\n return new Date().getTime();\n }\n});\n","var $ = require('../internals/export');\nvar toObject = require('../internals/to-object');\nvar nativeKeys = require('../internals/object-keys');\nvar fails = require('../internals/fails');\n\nvar FAILS_ON_PRIMITIVES = fails(function () { nativeKeys(1); });\n\n// `Object.keys` method\n// https://tc39.github.io/ecma262/#sec-object.keys\n$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, {\n keys: function keys(it) {\n return nativeKeys(toObject(it));\n }\n});\n","'use strict';\nvar redefine = require('../internals/redefine');\nvar anObject = require('../internals/an-object');\nvar fails = require('../internals/fails');\nvar flags = require('../internals/regexp-flags');\n\nvar TO_STRING = 'toString';\nvar RegExpPrototype = RegExp.prototype;\nvar nativeToString = RegExpPrototype[TO_STRING];\n\nvar NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });\n// FF44- RegExp#toString has a wrong name\nvar INCORRECT_NAME = nativeToString.name != TO_STRING;\n\n// `RegExp.prototype.toString` method\n// https://tc39.github.io/ecma262/#sec-regexp.prototype.tostring\nif (NOT_GENERIC || INCORRECT_NAME) {\n redefine(RegExp.prototype, TO_STRING, function toString() {\n var R = anObject(this);\n var p = String(R.source);\n var rf = R.flags;\n var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype) ? flags.call(R) : rf);\n return '/' + p + '/' + f;\n }, { unsafe: true });\n}\n","// このようにする理由は GeoMath.js の最後を参照\nimport { GeoPoint } from \"./GeoMath\";\nexport default GeoPoint;\n","import Material from \"./Material\";\nimport GeoMath from \"./GeoMath\";\nimport PointCloud from \"./PointCloud\";\nimport point_cloud_vs_code from \"./shader/point_cloud.vert\";\nimport point_cloud_fs_code from \"./shader/point_cloud.frag\";\nimport point_cloud_debug_wire_vs_code from \"./shader/point_cloud_debug_wire.vert\";\nimport point_cloud_debug_wire_fs_code from \"./shader/point_cloud_debug_wire.frag\";\nimport point_cloud_debug_face_vs_code from \"./shader/point_cloud_debug_face.vert\";\nimport point_cloud_debug_face_fs_code from \"./shader/point_cloud_debug_face.frag\";\n\n\n\n/**\n * @summary 点群マテリアル\n * @memberof mapray.RenderStage\n * @extends mapray.RenderStage.Material\n * @private\n */\nclass PointCloudMaterial extends Material {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n */\n constructor( viewer, options={} )\n {\n const preamble = PointCloudMaterial._getPreamble( options );\n\n super( viewer.glenv, preamble + point_cloud_vs_code, preamble + point_cloud_fs_code );\n\n this.bindProgram();\n this.setFloat( \"u_point_size\", 10 );\n this.setFloat( \"u_debug\", -1.0 );\n\n this._local_to_clip = GeoMath.createMatrixf();\n }\n\n /**\n * @summary 点の大きさを設定\n * @param {number} val 設定する値\n */\n setPointSize( val ) {\n this.setFloat( \"u_point_size\", val );\n }\n\n /**\n * @summary デバッグ値を設定\n * @param {number} val 設定する値\n */\n setDebug( val ) {\n this.setFloat( \"u_debug\", val );\n }\n\n /**\n * @summary 描画位置を設定\n * @param {number} val 設定する値\n */\n setDebugBoundsParameter( stage, center )\n {\n mul_local_to_gocs( stage._gocs_to_clip, center, this._local_to_clip );\n this.setMatrix( \"u_obj_to_clip\", this._local_to_clip );\n return true;\n }\n\n /**\n * @summary シェーダの前文を取得\n * @private\n */\n static\n _getPreamble( options )\n {\n const lines = [];\n\n const point_shape_type = options.point_shape_type || PointCloud.PointShapeType.CIRCLE;\n\n lines.push( \"#define POINT_SHAPE_TYPE \" + point_shape_type.shader_code );\n\n // lines を文字列にして返す\n return lines.join( \"\\n\" ) + \"\\n\\n\";\n }\n \n\n}\n\n\n\nconst mul_local_to_gocs = ( mat, center, dst ) => {\n const\n m00 = mat[ 0], m01 = mat[ 4], m02 = mat[ 8], m03 = mat[12],\n m10 = mat[ 1], m11 = mat[ 5], m12 = mat[ 9], m13 = mat[13],\n m20 = mat[ 2], m21 = mat[ 6], m22 = mat[10], m23 = mat[14],\n m30 = mat[ 3], m31 = mat[ 7], m32 = mat[11], m33 = mat[15];\n\n const\n t03 = center[0],\n t13 = center[1],\n t23 = center[2];\n\n dst[ 0] = m00;\n dst[ 1] = m10;\n dst[ 2] = m20;\n dst[ 3] = m30;\n\n dst[ 4] = m01;\n dst[ 5] = m11;\n dst[ 6] = m21;\n dst[ 7] = m31;\n\n dst[ 8] = m02;\n dst[ 9] = m12;\n dst[10] = m22;\n dst[11] = m32;\n\n dst[12] = m00*t03 + m01*t13 + m02*t23 + m03;\n dst[13] = m10*t03 + m11*t13 + m12*t23 + m13;\n dst[14] = m20*t03 + m21*t13 + m22*t23 + m23;\n dst[15] = m30*t03 + m31*t13 + m32*t23 + m33;\n\n return dst;\n}\n\n\n\n/**\n * @summary デバッグ用点群マテリアル(ワイヤーフレーム)\n * @memberof mapray.RenderStage\n * @extends mapray.RenderStage.Material\n * @private\n */\nclass PointCloudDebugWireMaterial extends Material {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n */\n constructor( viewer )\n {\n super( viewer.glenv, point_cloud_debug_wire_vs_code, point_cloud_debug_wire_fs_code );\n\n this.bindProgram();\n this._color = GeoMath.createVector3([ 0.0, 0.2, 0.4 ]);\n this.setVector3( \"u_color\", this._color );\n this._local_to_clip = GeoMath.createMatrixf();\n }\n\n setDebugBoundsParameter( stage, center, color )\n {\n mul_local_to_gocs( stage._gocs_to_clip, center, this._local_to_clip );\n this.setMatrix( \"u_obj_to_clip\", this._local_to_clip );\n if ( color ) {\n this._color[0] = color[0];\n this._color[1] = color[1];\n this._color[2] = color[2];\n }\n this.setVector3( \"u_color\", this._color );\n return true;\n }\n}\n\n\n\n/**\n * @summary デバッグ用点群マテリアル(サーフェス)\n * @memberof mapray.RenderStage\n * @extends mapray.RenderStage.Material\n * @private\n */\nclass PointCloudDebugFaceMaterial extends Material {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n */\n constructor( viewer )\n {\n super( viewer.glenv, point_cloud_debug_face_vs_code, point_cloud_debug_face_fs_code );\n\n this.bindProgram();\n this._color = GeoMath.createVector4([ 0.3, 0.9, 1.0, 0.5 ]);\n this.setVector4( \"u_color\", this._color );\n this._local_to_clip = GeoMath.createMatrixf();\n }\n\n setDebugBoundsParameter( stage, center, color )\n {\n mul_local_to_gocs( stage._gocs_to_clip, center, this._local_to_clip );\n this.setMatrix( \"u_obj_to_clip\", this._local_to_clip );\n if ( color ) {\n this._color[0] = color[0];\n this._color[1] = color[1];\n this._color[2] = color[2];\n this._color[3] = color[3] || 0.0;\n }\n this.setVector4( \"u_color\", this._color );\n return true;\n }\n}\n\n\n\nexport default PointCloudMaterial;\nexport { PointCloudDebugWireMaterial, PointCloudDebugFaceMaterial };\n","import Mesh from \"./Mesh\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport PointCloudMaterial, { PointCloudDebugWireMaterial, PointCloudDebugFaceMaterial } from \"./PointCloudMaterial\";\n\n\n\n/**\n * @summary 点群データを表現するクラス\n * @example\n * インスタンスの生成は下記のように行う。\n * const provider = new {@link mapray.RawPointCloudProvider}({\n * resource: {\n * prefix: \"https://...\"\n * }\n * });\n * const point_cloud = viewer.point_cloud_collection.add( provider );\n * point_cloud.setPointShape( {@link mapray.PointCloud.PointShapeType}.GRADIENT_CIRCLE );\n *\n * @see mapray.PointCloudProvider\n * @see mapray.PointCloudCollection\n * @memberof mapray\n */\nclass PointCloud {\n\n /**\n * @param {mapray.Scene} scene 所属するシーン\n * @param {mapray.PointCloudProvider} provider プロバイダ\n */\n constructor( scene, provider )\n {\n this._glenv = scene.glenv;\n this._scene = scene;\n\n this._provider = provider;\n\n this._root = Box.createRoot( this );\n\n // properties\n this._points_per_pixel = 0.7;\n this._point_shape = PointCloud.PointShapeType.CIRCLE;\n this._point_size_type = PointCloud.PointSizeType.FLEXIBLE;\n this._point_size = 1;\n this._point_size_limit = 10;\n\n // hidden properties\n this._dispersion = true;\n this._debug_shader = false;\n\n this._debug_render_box = false;\n this._debug_render_ellipsoid = false;\n this._debug_render_axis = false;\n this._debug_render_section = false;\n\n this._checkMaterials();\n\n PointCloud._instances.push(this);\n }\n\n\n static get PointShapeType() { return PointShapeType; }\n\n\n static get PointSizeType() { return PointSizeType; }\n\n\n /**\n * @summary 初期化\n * mapray.PointCloudBoxCollectorへ追加時に自動的に呼ばれる。\n * @private\n */\n async init() {\n await this._provider.init();\n }\n\n /**\n * @summary 破棄\n * mapray.PointCloudBoxCollectorから削除時に自動的に呼ばれる。\n * @private\n */\n async destroy() {\n if (this._provider) {\n await this._provider.destroy();\n }\n if (this._root) {\n await this._root.dispose(null);\n this._root = null;\n }\n const index = PointCloud._instances.indexOf( this );\n if ( index !== -1 ) {\n PointCloud._instances.splice( index, 1 );\n }\n this._provider = null;\n }\n\n\n /**\n * @summary プロバイダ\n * @type {mapray.PointCloudProvider}\n */\n get provider() { return this._provider; }\n\n\n /**\n * @summary ルートBox\n * @type {Box}\n * @private\n */\n get root() { return this._root };\n\n\n // Properties\n\n /**\n * @summary 点群Box読み込みを行う際の解像度[points/pixel]\n * @return {number}\n */\n getPointsPerPixel() { return this._points_per_pixel; }\n\n /**\n * @summary 点群Box読み込みを行う際の解像度[points/pixel]を設定\n * @param {number} val 設定する値\n */\n setPointsPerPixel( val ) {\n console.assert( val <= 1 );\n this._points_per_pixel = val;\n }\n\n /**\n * @summary 点を描画する際の形状\n * @return {mapray.PointCloud.PointShapeType}\n */\n getPointShape() { return this._point_shape; }\n\n /**\n * @summary 点を描画する際の形状を設定\n * @param {mapray.PointCloud.PointShapeType} val 設定する値\n */\n setPointShape( val ) {\n this._point_shape = val;\n }\n\n /**\n * @summary 点を描画する際のサイズの指定方法\n * @return {mapray.PointCloud.PointSizeType}\n */\n getPointSizeType() { return this._point_size_type; }\n\n /**\n * @summary 点を描画する際のサイズの指定方法を設定\n * @param {mapray.PointCloud.PointSizeType} val 設定する値\n */\n setPointSizeType( val ) {\n this._point_size_type = val;\n }\n\n /**\n * @summary 点を描画する際のサイズ\n * point_size_typeにより単位が異なる\n * @see mapray.PointCloud#getPointSizeType\n * @return {number}\n */\n getPointSize() { return this._point_size; }\n\n /**\n * @summary 点を描画する際のサイズを設定。\n * {@link mapray.PointCloud#setPointSizeType}により指定された値によって解釈される単位が異なる。\n * @param {number} val 設定する値\n */\n setPointSize( val ) {\n console.assert( val > 0 );\n this._point_size = val;\n }\n\n /**\n * @summary 点を描画する際の最大ピクセルサイズ\n * @return {number}\n */\n getPointSizeLimit() { return this._point_size_limit; }\n\n /**\n * @summary 点を描画する際の最大ピクセルサイズを設定\n * @param {number} val 設定する値\n */\n setPointSizeLimit( val ) {\n console.assert( val > 0 );\n this._point_size_limit = val;\n }\n\n\n // hidden properties\n\n /**\n * @private\n */\n getDispersion() { return this._dispersion }\n\n /**\n * @private\n */\n setDispersion( val ) { this._dispersion = val; }\n\n /**\n * @private\n */\n getDebugShader() { return this._debug_shader; }\n\n /**\n * @private\n */\n setDebugShader( val ) { this._debug_shader = val; }\n\n /**\n * @private\n */\n setDebugRenderBox( val ) { this._debug_render_box = val; this._updateDebugMesh(); }\n\n /**\n * @private\n */\n setDebugRenderEllipsoid( val ) { this._debug_render_ellipsoid = val; this._updateDebugMesh(); }\n\n /**\n * @private\n */\n setDebugRenderAxis( val ) { this._debug_render_axis = val; this._updateDebugMesh(); }\n\n /**\n * @private\n */\n setDebugRenderSection( val ) { this._debug_render_section = val; this._updateDebugMesh(); }\n\n /**\n * @private\n */\n _updateDebugMesh() {\n if ( this._root ) {\n this._root._updateDebugMeshes();\n }\n }\n\n /**\n * @summary Traverse結果の統計情報を取得。\n * リクエストキューに登録し、{@link mapray.RenderStage}が処理を完了するのを待つ。\n * @return {Promise}\n * @private\n */\n static async requestTraverseSummary() {\n return new Promise(onSuccess => {\n const notifier = statistics => {\n onSuccess(statistics);\n const index = PointCloud.getTraverseDataRequestQueue().indexOf(notifier);\n if (index !== -1) PointCloud.getTraverseDataRequestQueue().splice(index, 1);\n };\n PointCloud.getTraverseDataRequestQueue().push( notifier );\n });\n }\n\n /**\n * @summary Traverse結果取得用のリクエストキューを取得\n * @return {Array}\n * @private\n */\n static getTraverseDataRequestQueue() {\n return PointCloud._traverseDataRequestQueue || (PointCloud._traverseDataRequestQueue=[]);\n }\n\n /**\n * @summary 指定された level, x, y, z のURLを生成します\n * @param {number} level\n * @param {number} x\n * @param {number} y\n * @param {number} z\n * @return {string}\n * @private\n */\n getURL( level, x, y, z ) {\n return this._urlGenerator( level, x, y, z );\n }\n\n\n /**\n * @private\n */\n _checkMaterials() {\n const viewer = this._scene.viewer;\n const render_cache = viewer._render_cache || (viewer._render_cache = {});\n if ( !render_cache.point_cloud_materials ) {\n render_cache.point_cloud_materials = Object.keys(PointShapeType).reduce((map, key) => {\n const point_shape_type = PointShapeType[key];\n map[point_shape_type.id] = new PointCloudMaterial( viewer, {\n point_shape_type,\n });\n return map;\n }, {});\n }\n if ( !render_cache.point_cloud_debug_wire_material ) {\n render_cache.point_cloud_debug_wire_material = new PointCloudDebugWireMaterial( viewer );\n }\n if ( !render_cache.point_cloud_debug_face_material ) {\n render_cache.point_cloud_debug_face_material = new PointCloudDebugFaceMaterial( viewer );\n }\n }\n\n\n /**\n * @private\n */\n _getMaterial( point_shape ) {\n return this._scene.viewer._render_cache.point_cloud_materials[ point_shape.id ];\n }\n\n\n /**\n * @private\n */\n static setStatisticsHandler( statistics_handler ) {\n if (statistics_handler) {\n PointCloud._statistics = {\n statistics_obj: new Statistics(),\n statistics_handler: statistics_handler,\n };\n }\n \n }\n\n /**\n * @private\n */\n static getStatistics() { return PointCloud._statistics; }\n\n /**\n * @private\n */\n static getStatisticsHandler() { return PointCloud._statistics_handler; }\n}\n\nPointCloud._instances = [];\n\n\n/**\n * @private\n */\nclass Statistics {\n\n constructor() {\n this._now = performance ? () => performance.now() : () => Date.now();\n this.clear();\n };\n\n start() {\n this._start_time = this._now();\n }\n\n doneTraverse() {\n this._done_traverse_time = this._now();\n this.traverse_time += this._done_traverse_time - this._start_time;\n }\n\n done() {\n this._done_time = this._now();\n this.render_time += this._done_time - this._done_traverse_time;\n this.total_time += this._done_time - this._start_time;\n }\n\n clear() {\n this.render_point_count = 0;\n this.total_point_count = 0;\n this.render_boxes = 0;\n this.total_boxes = 0;\n this.loading_boxes = 0;\n this.created_boxes = 0;\n this.disposed_boxes = 0;\n this.total_time = 0.0;\n this.traverse_time = 0.0;\n this.render_time = 0.0;\n }\n}\n\n\n\n\n\n/**\n * @summary 点描画の種類\n * @constant\n * @enum {object}\n * @memberof mapray.PointCloud\n */\nconst PointShapeType = {\n /**\n * 矩形\n */\n RECTANGLE: { id: \"RECTANGLE\", shader_code: 0 },\n\n /**\n * 円\n */\n CIRCLE: { id: \"CIRCLE\", shader_code: 1 },\n\n /**\n * 境界線付きの円\n */\n CIRCLE_WITH_BORDER: { id: \"CIRCLE_WITH_BORDER\", shader_code: 2 },\n\n /**\n * グラデーションで塗り潰した円\n */\n GRADIENT_CIRCLE: { id: \"GRADIENT_CIRCLE\", shader_code: 3 },\n};\n\n\n\n/**\n * @summary 点描画のサイズ指定方法の種類\n * @enum {object}\n * @constant\n * @memberof mapray.PointCloud\n */\nconst PointSizeType = {\n /**\n * setPointSize()により指定された値をピクセルとして解釈する\n */\n PIXEL: { id: \"PIXEL\" },\n\n /**\n * setPointSize()により指定された値をmmとして解釈する\n */\n MILLIMETERS: { id: \"MILLIMETERS\" },\n\n /**\n * setPointSize()により指定された値を参照せず、表示位置に応じて適切なサイズを自動的に指定する。\n */\n FLEXIBLE: { id: \"FLEXIBLE\" },\n};\n\n\n\n/**\n * @summary 点群ツリーを構成するノード。\n * ルート要素(level === 0) は、Box.createRoot()を用いて作成する。\n * @memberof mapray.PointCloud\n * @private\n */\nclass Box {\n\n /**\n * @param {Box|null} parent 親Box(level === 0の場合はnull)\n * @param {number} level レベル\n * @param {number} x x\n * @param {number} y y\n * @param {number} z z\n * @private\n */\n constructor( parent, level, x, y, z )\n {\n /**\n * @summary 親Box\n * @type {Box}\n */\n this._parent = parent;\n\n /**\n * @summary 所属するPointCloud。\n * ルート要素の場合は Box.createRoot() で設定される。\n * @type {mapray.PointCloud}\n */\n this._owner = parent ? parent._owner : null;\n\n /**\n * @summary レベル\n * @type {number}\n */\n this.level = level;\n\n /**\n * @summary x\n * @type {number}\n */\n this.x = x;\n\n /**\n * @summary y\n * @type {number}\n */\n this.y = y;\n\n /**\n * @summary z\n * @type {number}\n */\n this.z = z;\n\n\n /*\n 2次元(X,Y)までを下記に図示する。Z軸についても同様。\n ^ Y\n |\n +-------------+-------------M\n | cell (0, 1) | cell (1, 1) |\n | | | c: gocs_center [GOCS]\n | | | m: gocs_min [GOCS]\n | | | M: gocs_max [GOCS]\n | | |\n +-------------c-------------+\n | cell (0, 0) | cell (1, 0) |\n | | |\n | | |\n | | |\n | | |\n m-------------+-------------+ --> X\n |<--size[m]-->|\n */\n\n /**\n * @summary Box一辺の半分の長さ\n * @type {number}\n * @private\n */\n const size = this.size = (\n level === 0 ? 2147483648: // 2^31\n level < 31 ? 1 << (31-level):\n Math.pow(0.5, level-31)\n );\n\n /**\n * @summary 軸方向に投影した際の面積\n * @type {number}\n * @private\n */\n this.proj_area = 4.0 * this.size * this.size;\n\n /**\n * @summary GOCS座標系でのBoxの中心位置\n * @type {mapray.Vector3}\n * @private\n */\n this.gocs_center = GeoMath.createVector3([\n MIN_INT + (2 * x + 1) * size,\n MIN_INT + (2 * y + 1) * size,\n MIN_INT + (2 * z + 1) * size\n ]);\n\n /**\n * @summary GOCS座標系でのBoxの最小位置\n * @type {mapray.Vector3}\n * @private\n */\n this.gocs_min = GeoMath.createVector3([\n this.gocs_center[0] - size,\n this.gocs_center[1] - size,\n this.gocs_center[2] - size\n ]);\n\n /**\n * @summary GOCS座標系でのBoxの最大位置\n * @type {mapray.Vector3}\n * @private\n */\n this.gocs_max = GeoMath.createVector3([\n this.gocs_center[0] + size,\n this.gocs_center[1] + size,\n this.gocs_center[2] + size\n ]);\n\n /**\n * @type {Box.Status}\n * @private\n */\n this._status = Box.Status.NOT_LOADED;\n\n\n // (this._status === Box.Status.LOADED) において有効な値\n\n /**\n * @summary 子Box、セルに関する情報\n * @type {object}\n * @private\n */\n this._metaInfo = null;\n\n /**\n * @summary 子Box。\n * (u, v, w)
のインデックスは (u | v << 1 | w << 2)
によって算出される。\n * @type {mapray.Box[]}\n * @private\n */\n this._children = [ null, null, null, null, null, null, null, null ];\n\n /**\n * @type {mapray.Vector3}\n * @private\n */\n this.average = null;\n\n /**\n * @type {mapray.Vector3}\n * @private\n */\n this.eigenVector = null;\n\n /**\n * @type {mapray.Vector3}\n * @private\n */\n this.eigenVectorLength = null;\n\n /**\n * @private\n */\n this._vertex_buffer = null;\n\n /**\n * @private\n */\n this._vertex_length = null;\n\n /**\n * @private\n */\n this._vertex_attribs = null;\n\n /**\n * @private\n */\n this.debug1 = null;\n\n\n if ( this._owner ) {\n this._updateDebugMesh();\n }\n }\n\n /**\n * @private\n */\n _updateDebugMeshes() {\n this._updateDebugMesh();\n for (let i=0; i 20 && this.getPointsLength() > 5000 && this.eigenVectorLength[0] < this.size * 0.2 ) { // = 10% = (2 * s) / 10\n this._putSectionShapePoints(vertices, indices, tindices); // Render Cross Section\n }\n }\n if ( this._owner._debug_render_ellipsoid ) {\n this._putVariancePoints(vertices, indices, tindices); // Render Normal Ring\n }\n }\n\n const meshes = [];\n if (indices.length > 0) {\n const mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 }\n ],\n ptype: \"lines\",\n vertices: vertices,\n indices: indices\n };\n meshes.push( new Mesh( this._owner._glenv, mesh_data ) );\n }\n if (tindices.length > 0) {\n const mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 }\n ],\n ptype: \"triangles\",\n vertices: vertices,\n indices: tindices\n };\n meshes.push( new Mesh( this._owner._glenv, mesh_data ) );\n }\n\n this.debugMesh = meshes;\n }\n\n\n /**\n * @private\n */\n _putVariancePoints(vertices, indices, tindices) {\n const offset = vertices.length / 3;\n const [ e1, e2, e3 ] = this.eigenVector;\n const [ e1l, e2l, e3l ] = this.eigenVectorLength;\n const G = 6;\n const N = 12;\n const cache = PointCloud._variance_points_cache || (() => {\n const c = {\n cos_ro: [],\n sin_ro: [],\n cos_th: [],\n sin_th: [],\n };\n for ( let j=0; j<=G; ++j ) {\n const ro = Math.PI * j/G;\n c.cos_ro[j] = Math.cos(ro);\n c.sin_ro[j] = Math.sin(ro);\n }\n for ( let i=0; i<=N; ++i ) {\n const th = 2 * Math.PI * i/N;\n c.cos_th[i] = Math.cos(th);\n c.sin_th[i] = Math.sin(th);\n }\n return PointCloud._variance_points_cache = c;\n })();\n const putPoint = (ro, th, vs) => {\n const cos_ro = cache.cos_ro[ro];\n const sin_ro = cache.sin_ro[ro];\n const cos_th = cache.cos_th[th];\n const sin_th = cache.sin_th[th];\n vs.push(\n this.average[0] + sin_ro * (e2l * cos_th * e2[0] + e3l * sin_th * e3[0]) + e1l * cos_ro * e1[0],\n this.average[1] + sin_ro * (e2l * cos_th * e2[1] + e3l * sin_th * e3[1]) + e1l * cos_ro * e1[1],\n this.average[2] + sin_ro * (e2l * cos_th * e2[2] + e3l * sin_th * e3[2]) + e1l * cos_ro * e1[2]\n );\n }\n for ( let j=0; j<=G; ++j ) {\n for ( let i=0; i<=N; ++i ) {\n putPoint(j, i, vertices);\n }\n }\n for ( let j=0; j . . c c = p + alpha v\n v \\ \n \\ (a - p) ue \n |-------->| \\ alpha = ------------\n alpha v \\ v ue \n */\n const q = []\n for ( let i=0; i<2; ++i ) {\n for ( let j=0; j<2; ++j ) {\n q.push(\n { p: [i>0?s:-s, j>0?s:-s, 0], v: [0, 0, s] },\n { p: [j>0?s:-s, 0, i>0?s:-s], v: [0, s, 0] },\n { p: [ 0, i>0?s:-s, j>0?s:-s], v: [s, 0, 0] }\n )\n }\n }\n\n let n, t;\n for ( let i=0; i a[3] - b[3]);\n for ( let i=0; i 0:\n this._metaInfo.indices[ index ] > this._metaInfo.indices[ index - 1 ]\n )\n );\n }\n\n\n /**\n * @summary Boxに含まれる点の数\n * \n * @return {number}\n */\n getPointsLength() {\n return this._metaInfo ? this._metaInfo.indices[7] : 0;\n }\n\n /**\n * @summary 子Boxの番号を返します。\n * @param {Box} child 子Box\n * @return {number}\n */\n indexOf( child ) {\n return this._children.indexOf( child );\n }\n\n\n /**\n * @summary カリングするか?\n * @param {mapray.Vector4[]} clip_planes クリップ平面配列\n * @return {boolean} 見えないとき true, 見えるまたは不明のとき false\n */\n isInvisible( clip_planes ) {\n if ( this.level === 0 ) return false;\n\n const xmin = this.gocs_min[0];\n const xmax = this.gocs_max[0];\n const ymin = this.gocs_min[1];\n const ymax = this.gocs_max[1];\n const zmin = this.gocs_min[2];\n const zmax = this.gocs_max[2];\n\n for ( let i = 0; i < clip_planes.length; ++i ) {\n const p = clip_planes[i];\n const px = p[0];\n const py = p[1];\n const pz = p[2];\n const pw = p[3];\n\n // 以下がすべて成り立つとボックス全体は平面の裏側にある\n // px*xmin + py*ymin + pz*zmin + pw < 0\n // px*xmax + py*ymin + pz*zmin + pw < 0\n // px*xmin + py*ymax + pz*zmin + pw < 0\n // px*xmax + py*ymax + pz*zmin + pw < 0\n // px*xmin + py*ymin + pz*zmax + pw < 0\n // px*xmax + py*ymin + pz*zmax + pw < 0\n // px*xmin + py*ymax + pz*zmax + pw < 0\n // px*xmax + py*ymax + pz*zmax + pw < 0\n\n const c0 = px*xmin + py*ymin;\n const c1 = px*xmax + py*ymin;\n const c2 = px*xmin + py*ymax;\n const c3 = px*xmax + py*ymax;\n const c4 = -pz*zmin - pw;\n const c5 = -pz*zmax - pw;\n\n if ( c0 < c4 && c1 < c4 && c2 < c4 && c3 < c4 &&\n c0 < c5 && c1 < c5 && c2 < c5 && c3 < c5 ) {\n // ボックス全体が平面の裏側にあるので見えない\n return true;\n }\n }\n\n return false; // 見えている可能性がある\n }\n\n\n /**\n * @summary 点群の読み込み処理\n * \n * @return {Promise}\n */\n load() {\n if ( this._status !== Box.Status.NOT_LOADED ) throw new Error( \"illegal status: \" + this._status.id );\n if ( !this._owner._provider.isReady() ) return;\n this._status = Box.Status.LOADING;\n\n const task = this._owner._provider.load( this.level, this.x, this.y, this.z, true );\n this._loadId = task.id;\n return task.done.then(event => {\n this._metaInfo = {\n children: [],\n indices: event.header.indices,\n };\n\n {\n let childFlags = event.header.childFlags;\n for ( let i=7; i>=0; --i ) {\n this._metaInfo.children[i] = (childFlags & 1) ? {} : null;\n childFlags = childFlags >> 1;\n }\n }\n\n this.average = event.header.average;\n this.eigenVector = event.header.eigenVector;\n this.eigenVectorLength = event.header.eigenVectorLength;\n this.debug1 = event.header.debug1;\n\n const values = event.body;\n console.assert( values.length > 0 );\n console.assert( values.length / 6 === this._metaInfo.indices[7] );\n\n ASSERT: {\n const number_of_points = values.length / 6;\n for ( let i=0; i<8; ++i ) {\n if (this._metaInfo.indices[i] > number_of_points) {\n console.log(\"warning fix indices\");\n this._metaInfo.indices[i] = number_of_points;\n }\n }\n }\n\n const gl = this._owner._glenv.context;\n\n this._vertex_buffer = gl.createBuffer();\n\n this._vertex_length = values.length / 6;\n gl.bindBuffer(gl.ARRAY_BUFFER, this._vertex_buffer);\n gl.bufferData(gl.ARRAY_BUFFER, values, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n /*\n * +------------+------------+----\n * | a_position | a_color | ...\n * +------------+------------+----\n * \n * |<--12 bit-->|<--12 bit-->|\n */\n this._vertex_attribs = {\n \"a_position\": {\n buffer: this._vertex_buffer,\n num_components: 3,\n component_type: gl.FLOAT,\n normalized: false,\n byte_stride: 24,\n byte_offset: 0\n },\n \"a_color\": {\n buffer: this._vertex_buffer,\n num_components: 3,\n component_type: gl.FLOAT,\n normalized: false,\n byte_stride: 24,\n byte_offset: 12\n }\n };\n this._status = Box.Status.LOADED;\n\n this._updateDebugMesh();\n })\n .catch(error => {\n const skip_error = (\n error.message === \"cancel\" ||\n error.message === \"not loading\" ||\n error.message === \"The user aborted a request.\" ||\n error.is_aborted\n );\n if ( !skip_error ) {\n console.log(error);\n this._status = Box.Status.DESTROYED;\n }\n });\n }\n\n\n /**\n * @summary 子Boxを生成。(すでに存在する場合は既存のBoxを返す)\n * LOADED 状態でのみ呼ぶことができる\n * \n * @param {number} index 番号\n * @param {object} [statistics] 統計情報\n * @return {Box}\n */\n newChild( index, statistics ) {\n const [ u, v, w ] = Box.CHILDREN_INDICES[ index ];\n return this.newChildAt( u, v, w, statistics );\n }\n\n\n /**\n * @summary 子Boxを生成。(すでに存在する場合は既存のBoxを返す)\n * LOADED 状態でのみ呼ぶことができる\n * \n * @param {number} u x方向-側は0、+側は1\n * @param {number} v y方向-側は0、+側は1\n * @param {number} w z方向-側は0、+側は1\n * @param {object} [statistics] 統計情報\n * @return {Box}\n */\n newChildAt( u, v, w, statistics ) {\n console.assert( this._status === Box.Status.LOADED );\n const index = u | v << 1 | w << 2;\n const child = this._children[ index ];\n if ( child ) return child;\n\n if ( !this.getChildInfo( index ) ) return null;\n\n if ( statistics ) statistics.created_boxes++;\n return this._children[ index ] = new Box( this,\n this.level + 1,\n this.x << 1 | u,\n this.y << 1 | v,\n this.z << 1 | w\n );\n }\n\n /**\n * @summary 子Boxを取得。\n * 存在しない場合は null を返却する。\n * \n * @param {number} index 番号\n * @return {Box}\n */\n getChild( index ) {\n return this._children[ index ];\n }\n\n /**\n * デバッグメッシュを描画\n * \n * @param {mapray.RenderStage} render_stage レンダリングステージ\n */\n _drawDebugMesh( render_stage ) {\n if ( !this.debugMesh ) return;\n\n const gl = render_stage._glenv.context;\n const color = Box.STATUS_COLOR_TABLE[ this._status.id ];\n\n gl.disable( gl.CULL_FACE );\n for ( let debugMesh of this.debugMesh ) {\n const debug_material = (debugMesh._draw_mode === 1 ?\n this._owner._scene.viewer._render_cache.point_cloud_debug_wire_material:\n this._owner._scene.viewer._render_cache.point_cloud_debug_face_material\n );\n debug_material.bindProgram();\n debug_material.setDebugBoundsParameter( render_stage, this.gocs_center, color );\n debugMesh.draw( debug_material );\n }\n gl.enable( gl.CULL_FACE );\n }\n\n\n /**\n * @summary Boxを描画する。\n * Box全体の描画および、Boxの8分割単位での描画に対応。\n * \n * @param {mapray.RenderStage} render_stage レンダリングステージ\n * @param {number[]|null} target_cells 描画対象の子番号の配列。ただしnullは全体を表す。\n * @param {number[]} points_per_pixels 点の解像度の配列。target_cells同じ順序であり、nullの場合は要素数1となる。\n * @param {object} statistics 統計情報\n */\n draw( render_stage, target_cells, points_per_pixels, statistics )\n {\n if ( this.debugMesh ) {\n this._drawDebugMesh( render_stage );\n }\n\n if ( this._status !== Box.Status.LOADED ) return;\n\n const gl = render_stage._glenv.context;\n\n const point_shape = this._owner._point_shape;\n const point_size_type = this._owner._point_size_type;\n const point_size = this._owner._point_size;\n const point_size_limit = this._owner._point_size_limit;\n const debug_shader = this._owner._debug_shader;\n\n if ( this._status === Box.Status.LOADED ) {\n const material = this._owner._getMaterial( point_shape );\n material.bindProgram();\n material.setDebugBoundsParameter( render_stage, this.gocs_center );\n material.bindVertexAttribs(this._vertex_attribs);\n\n const overlap_scale = 3;\n if ( target_cells === null ) {\n // draw whole points\n const ppp = points_per_pixels[ 0 ];\n material.setPointSize(\n point_size_type === PointCloud.PointSizeType.PIXEL ? point_size:\n point_size_type === PointCloud.PointSizeType.MILLIMETERS ? -0.001 * point_size / render_stage._pixel_step:\n Math.min( point_size_limit, Math.max( 1.0, overlap_scale / ppp ) )\n );\n material.setDebug( debug_shader ? 0.5 / ppp : -1.0 );\n gl.drawArrays( gl.POINTS, 0, this._vertex_length );\n }\n else {\n // draw only target regions\n for ( let i=0; i 0 ? this._metaInfo.indices[childIndex - 1] : 0;\n const length = this._metaInfo.indices[childIndex] - offset;\n if ( length > 0 ) gl.drawArrays( gl.POINTS, offset, length );\n }\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n if ( statistics ) {\n statistics.render_boxes++;\n if ( target_cells && this._metaInfo.indices ) {\n for ( let childIndex of target_cells ) {\n const offset = childIndex > 0 ? this._metaInfo.indices[childIndex-1] : 0;\n const length = this._metaInfo.indices[childIndex] - offset;\n statistics.render_point_count += length;\n }\n }\n else {\n statistics.render_point_count += this._vertex_length;\n }\n }\n }\n }\n\n\n /**\n * @summary 子孫Boxを全て削除する。\n * 全ての状態でこの関数を呼ぶことができ、複数回呼ぶことができる。\n * @param {object} [statistics] 統計情報\n */\n disposeChildren( statistics ) {\n for (let i=0; i (\n text +\n (child ? \"\\n\" + child.toTreeString( indent + \" \" ) : \"\")\n ),\n indent + this.toString()\n );\n }\n\n\n /**\n * ルートBoxを生成します。\n * @param {mapray.PointCloud} owner\n * @return {Box}\n */\n static createRoot( owner ) {\n const box = new Box( null, 0, 0, 0, 0 );\n box._owner = owner;\n return box;\n }\n\n static get Status() {\n return Status;\n }\n}\n\n\nBox.CHILDREN_INDICES = [\n [0, 0, 0],\n [1, 0, 0],\n [0, 1, 0],\n [1, 1, 0],\n [0, 0, 1],\n [1, 0, 1],\n [0, 1, 1],\n [1, 1, 1],\n];\n\n\n\n/**\n * @summary Boxの状態。\n * \n * \n * load() dispose() \n * NOT_LOADED ---------> LOADING -------> LOADED -----------> DESTROYED \n * \\ / \n * `------>-----------------´ \n * error or dispose() \n *
\n * @enum {object}\n * @memberof mapray.PointCloud.Box\n * @constant\n * @see mapray.PointCloud.Box#status\n */\nconst Status = {\n /**\n * 準備中 (初期状態)。\n * load()を呼ぶと LOADING へ遷移し読み込み処理が開始される。\n */\n NOT_LOADED: { id: \"NOT_LOADED\" },\n\n /**\n * 読み込み中。\n * 読み込み処理が終了すると、LOADED か DESTROYED のいずれかに遷移する。\n * 正常に処理が完了すると LOADED 、何らかのエラーが発生した場合は DESTROYED となる。\n * また、LOADING 中に dispose() が呼ばれた場合、即座に DESTROYED に遷移する。\n */\n LOADING: { id: \"LOADING\" },\n\n /**\n * 読み込み完了(描画可能)。\n * dispose()を呼ぶと DESTROYED に遷移する。\n */\n LOADED: { id: \"LOADED\" },\n\n /**\n * 破棄状態\n * 他の状態に遷移することはない。\n */\n DESTROYED: { id: \"DESTROYED\" }\n};\n\n\n\nBox.STATUS_COLOR_TABLE = {};\n{\n Box.STATUS_COLOR_TABLE[Box.Status.LOADED.id] = [0.0, 0.8, 1.0, 0.5];\n Box.STATUS_COLOR_TABLE[Box.Status.DESTROYED.id] = [1.0, 0.0, 0.0];\n Box.STATUS_COLOR_TABLE[Box.Status.LOADING.id] = [1.0, 1.0, 0.0];\n Box.STATUS_COLOR_TABLE[Box.Status.NOT_LOADED.id] = [0.0, 1.0, 0.0];\n};\n\n\nconst MIN_INT = 1 << 31;\n\n\n\n\nexport default PointCloud;\nexport { Box, Statistics };\n","import Primitive from \"./Primitive\";\nimport GeoMath from \"./GeoMath\";\nimport AreaUtil from \"./AreaUtil\";\n\n\n/**\n * @summary Boxをレンダリングするためのオブジェクト\n *\n * @memberof mapray\n * @private\n */\nclass PointCloudBoxRenderObject {\n\n\n /**\n * @param {mapray.PointCloud.Box} box 描画対象\n * @param {number} distance 視点からBoxまでの距離\n * @param {number} parent_points_per_pixel 親Boxの点の細かさ\n */\n constructor( box, distance, parent_points_per_pixel )\n {\n this._box = box;\n this._distance = distance;\n this._target_children = [];\n this._points_per_pixel = [];\n this._parent_points_per_pixel = parent_points_per_pixel;\n }\n\n\n /**\n * @summary 描画対象\n * @type {mapray.PointCloud.Box}\n */\n get box()\n {\n return this._box;\n }\n\n\n /**\n * @summary カメラからの距離\n * @type {number}\n */\n get distance()\n {\n return this._distance;\n }\n\n\n /**\n * @summary 親Boxの点の解像度\n * @type {number}\n */\n get parent_points_per_pixel()\n {\n return this._parent_points_per_pixel;\n }\n\n\n /**\n * @summary 描画対象領域を追加する\n * @param {number} child 領域\n * @param {number} points_per_pixel 描画する点の細かさ\n */\n pushRegion( child, points_per_pixel )\n {\n if ( this._target_children !== null ) {\n const index = this._target_children.indexOf( child );\n if ( index === -1) {\n this._target_children.push( child );\n this._points_per_pixel.push( points_per_pixel );\n }\n else {\n this._target_children[ index ] = child;\n this._points_per_pixel[ index ] = points_per_pixel;\n }\n }\n }\n\n\n /**\n * @summary 描画対象を全領域にする\n * @param {number} points_per_pixel 描画する点の細かさ\n */\n setWholeRegion( points_per_pixel )\n {\n this._target_children = null;\n this._points_per_pixel = [ points_per_pixel ];\n }\n\n\n /**\n * @summary 描画\n * @param {mapray.RenderStage} render_stage レンダリングステージ\n * @param {object} statistics 統計情報\n */\n draw( render_stage, statistics )\n {\n this._box.draw( render_stage, this._target_children, this._points_per_pixel, statistics );\n }\n}\n\n\nexport default PointCloudBoxRenderObject;\n","import GeoMath from \"./GeoMath\";\nimport RenderFlake from \"./RenderFlake\";\nimport PointCloud, { Box } from \"./PointCloud\";\nimport PointCloudBoxRenderObject from \"./PointCloudBoxRenderObject\";\n\n\n\n/**\n * @summary Boxを収集するツール\n * @memberof mapray\n * @private\n */\nclass PointCloudBoxCollector {\n\n /**\n * @param {mapray.RenderStage} stage 所有者である RenderStage\n * @param {number} load_limit 読み込みが必要なBoxリストに保持する要素数の上限\n */\n constructor( stage, load_limit=10 )\n {\n this._setupViewVectors( stage );\n this._setupClipPlanes( stage );\n\n this._point_cloud_collection = stage._point_cloud_collection;\n\n /**\n * 描画するBoxのリスト。\n * @private\n */\n this._render_boxes = [];\n\n /**\n * Box => PointCloudBoxRenderObject の辞書\n * @private\n */\n this._render_boxes_map = new Map();\n\n /**\n * 読み込みが必要なBoxリスト。常に優先度でソート済み。\n * @private\n */\n this._load_boxes = [];\n\n /**\n * 読み込みが必要なBoxリストの要素数の上限\n * @private\n */\n this._load_limit = load_limit;\n }\n\n\n /**\n * @private\n */\n _setupViewVectors( stage )\n {\n const view_to_gocs = stage._view_to_gocs;\n const pixel_step = stage._pixel_step;\n \n const view_pos_Q = GeoMath.createVector3();\n const view_dir_wU = GeoMath.createVector3();\n\n // 地表詳細レベル (LOD) 計算用の Q, w*U ベクトルを設定\n view_pos_Q[0] = view_to_gocs[12];\n view_pos_Q[1] = view_to_gocs[13];\n view_pos_Q[2] = view_to_gocs[14];\n\n view_dir_wU[0] = -view_to_gocs[ 8] * pixel_step;\n view_dir_wU[1] = -view_to_gocs[ 9] * pixel_step;\n view_dir_wU[2] = -view_to_gocs[10] * pixel_step;\n\n /**\n * @summary 位置ベクトル Q\n * @member mapray.FlakeCollector#_view_pos_Q\n * @type {mapray.Vector3}\n * @private\n * @see doc/ImageLevelCalculation.txt\n */\n this._view_pos_Q = view_pos_Q;\n\n /**\n * @summary ベクトル w * U\n * @member mapray.FlakeCollector#_view_dir_wU\n * @type {mapray.Vector3}\n * @private\n * @see doc/ImageLevelCalculation.txt\n */\n this._view_dir_wU = view_dir_wU;\n }\n\n\n /**\n * @private\n */\n _setupClipPlanes( stage )\n {\n const view_to_gocs = stage._view_to_gocs;\n const gocs_to_view = stage._gocs_to_view;\n\n this.volume_planes = stage._volume_planes;\n\n // const volume_planes = stage._volume_planes;\n const clip_planes = [];\n\n // 地表遮蔽カリング平面\n const root_flake = stage._viewer._globe.root_flake;\n const rmin = GeoMath.EARTH_RADIUS + root_flake.height_min; // 最小半径\n const rmax = GeoMath.EARTH_RADIUS + root_flake.height_max; // 最大半径\n\n // P (視点位置)\n const px = view_to_gocs[12];\n const py = view_to_gocs[13];\n const pz = view_to_gocs[14];\n\n // q = √[(P.P - rmin^2)(rmax^2 - rmin^2)] - rmin^2\n const p2 = px*px + py*py + pz*pz;\n const rmin2 = rmin*rmin;\n const rmax2 = rmax*rmax;\n const q = Math.sqrt( (p2 - rmin2) * (rmax2 - rmin2) ) - rmin2;\n\n // L = / ‖P‖\n const plane = GeoMath.createVector4();\n const recip = 1 / Math.sqrt( p2 );\n plane[0] = px * recip;\n plane[1] = py * recip;\n plane[2] = pz * recip;\n plane[3] = q * recip;\n // clip_planes.push( plane );\n\n // L を基とした遠方距離\n const far_dist = Math.sqrt( p2 + rmax2 + 2*q );\n\n // 視体積平面を取得して、地心直交座標系に変換\n // (直交変換なので x, y, z は正規化されている)\n for ( let i = 0; i < 6; ++i ) {\n let src_plane = this.volume_planes[i];\n const dst_plane = GeoMath.createVector4();\n\n if ( i == 1 && src_plane[3] > far_dist ) {\n // 遠方平面が必要以上に遠いとき far_dist に置き換える\n src_plane = GeoMath.createVector4( src_plane );\n src_plane[3] = far_dist;\n }\n\n GeoMath.transformPlane_A( gocs_to_view, src_plane, dst_plane );\n\n clip_planes.push( dst_plane );\n }\n\n this._clip_planes = clip_planes;\n }\n\n\n /**\n * @summary 点群Boxを収集する\n * @param {mapray.PointCloud} point_cloud 点群\n * @param {object} statistics 統計情報\n * @return {mapray.RenderFlake[]} 収集された点群Boxの集合\n */\n traverse( point_cloud, statistics )\n {\n this._points_per_pixel = point_cloud.getPointsPerPixel();\n this._dispersion = point_cloud.getDispersion();\n\n this._statistics = statistics;\n this._updateBox( point_cloud.root, 0 );\n return {\n visible_boxes: this._render_boxes,\n load_boxes: this._load_boxes,\n };\n }\n\n\n /**\n * @private\n */\n _updateBox( box, parent_ppp ) {\n if (this._statistics) {\n this._statistics.total_boxes++;\n if ( box.status === Box.Status.LOADING ) this._statistics.loading_boxes++;\n if ( box.getPointsLength() ) {\n this._statistics.total_point_count += box.getPointsLength();\n }\n }\n\n if ( box.isInvisible( this._clip_planes ) ) {\n box.disposeChildren( this._statistics );\n return;\n }\n\n let box_ppp, lodStatus;\n if (box.is_loaded) {\n box_ppp = this._calcPointsPerPixel( box );\n lodStatus = box_ppp < this._points_per_pixel ? LodStatus.LOAD_NEXT_LEVEL : LodStatus.UNLOAD_NEXT_LEVEL;\n }\n else {\n lodStatus = LodStatus.KEEP_STATUS;\n }\n\n if ( lodStatus === LodStatus.LOAD_NEXT_LEVEL ) { // if more detaild data is required then load nextLevel\n // 子Boxがない領域を描画する\n // [A]\n // |--a1--[B]\n // |--a2--[C]\n // `--a3-- x\n // - Aは、8分割された領域のうち、a1, a2, a3の領域に点が含まれている。\n // - このうちa1, a2については子Box(B, C)を持っており、a3は子Boxを持っていない。\n // - この場合、a3の領域についはAが描画する。(a1, a2の領域については、B, Cにより描画される)\n this._collectNextLevel( box, box_ppp );\n for ( let i=0; i<8; i++ ) {\n if ( box.cellPointsAvailable( i ) && !box.getChild( i ) ) {\n this._pushBox( box, i, box_ppp, parent_ppp );\n }\n }\n return;\n }\n\n if ( lodStatus === LodStatus.UNLOAD_NEXT_LEVEL ) { // if more detaild data is not required then dispose children\n box.disposeChildren( this._statistics );\n }\n\n if ( box.status !== Box.Status.DESTROYED ) {\n // [A]\n // |--a1--[B]\n // |--a2--[C]\n // `--a3-- x\n // - Bが読み込まれている場合はBを描画する。\n // - Bの読み込みが完了するまでは、Aがa1領域を描画する(読み込み中は枠のみ描画される場合があるため、Bも描画する)。\n this._pushBox( box, null, box_ppp, parent_ppp );\n if ( box.status === Box.Status.LOADING || box.status === Box.Status.NOT_LOADED ) {\n if ( box.level > 1 ) {\n this._pushBox( box.parent, box.parent.indexOf(box), parent_ppp, parent_ppp );\n }\n }\n }\n }\n\n\n /**\n * @スクリーン1画素あたりの点の数[points/pixel]を計算する。\n * 例えば、2画素につき1点の間隔で並んでいる場合は0.5を返す。\n * @param {mapray.PointCloud.Box} box Box\n * @private\n */\n _calcPointsPerPixel( box ) {\n const is_plane = box.eigenVectorLength[0] < box.size * 0.8;\n let points_per_pixel;\n\n if ( is_plane && this._dispersion && box.eigenVectorLength[0] > 0 ) {\n const dir = GeoMath.normalize3( this._view_dir_wU, GeoMath.createVector3f() );\n\n const [ ev1, ev2, ev3 ] = box.eigenVector;\n const [ ev1l, ev2l, ev3l ] = box.eigenVectorLength;\n const n = [\n GeoMath.dot3(ev2, dir),\n GeoMath.dot3(ev1, dir),\n GeoMath.dot3(ev3, dir)\n ];\n const s = (n[0] * n[0]) / (ev2l * ev2l) + (n[2] * n[2]) / (ev3l * ev3l);\n const nn = [\n s * ev3l * ev1l / ev2l * n[0],\n s * ev2l * ev3l / ev1l * n[1],\n s * ev1l * ev2l / ev3l * n[2]\n ];\n GeoMath.normalize3(nn, nn);\n const area_calc = (nn[0] * n[0] + nn[1] * n[1] + nn[2] * n[2]) * (\n Math.PI * ev1l * ev2l * ev3l /\n Math.sqrt(nn[0] * nn[0] * ev2l * ev2l + nn[1] * nn[1] * ev1l * ev1l + nn[2] * nn[2] * ev3l * ev3l)\n );\n const area = Math.min(Math.max(area_calc, 0.05 * box.proj_area), box.proj_area);\n points_per_pixel = Math.sqrt( box.getPointsLength() / area );\n }\n else {\n points_per_pixel = 64 / box.size; // (128 / (2*box.size)) = 1セルの大きさ(点の間隔)\n }\n\n const diff = [\n box.gocs_center[0] + box.average[0] - this._view_pos_Q[0],\n box.gocs_center[1] + box.average[1] - this._view_pos_Q[1],\n box.gocs_center[2] + box.average[2] - this._view_pos_Q[2]\n ];\n\n // ω スクリーン上の1画素の一辺の長さを、box.averageの位置に投影した長さ\n const ω = GeoMath.dot3( this._view_dir_wU, diff );\n return ω * points_per_pixel;\n }\n\n\n /**\n * @summary 描画対象を追加\n * \n * @param {mapray.PointCloud.Box} box\n * @param {number|null} targetChild cell領域を指定する場合は数字、全体を描画する場合はnullを指定する\n * @param ppp 描画時の解像度 (points per pixel)\n * @param parent_ppp 親Boxの描画時の解像度 (points per pixel)\n * @private\n */\n _pushBox( box, target_child, ppp, parent_ppp ) {\n let ro = this._render_boxes_map.get( box );\n if ( ro ) {\n if ( target_child === null ) ro.setWholeRegion( ppp );\n else ro.pushRegion( target_child, ppp );\n }\n else {\n const diff = (box.is_loaded ?\n [\n box.gocs_center[0] + box.average[0] - this._view_pos_Q[0],\n box.gocs_center[1] + box.average[1] - this._view_pos_Q[1],\n box.gocs_center[2] + box.average[2] - this._view_pos_Q[2]\n ]:\n [\n box.gocs_center[0] - this._view_pos_Q[0],\n box.gocs_center[1] - this._view_pos_Q[1],\n box.gocs_center[2] - this._view_pos_Q[2]\n ]\n );\n const distance = Math.sqrt(diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]);\n ro = new PointCloudBoxRenderObject( box, distance, parent_ppp );\n if ( target_child === null ) ro.setWholeRegion( ppp );\n else ro.pushRegion( target_child, ppp );\n this._render_boxes.push( ro );\n this._render_boxes_map.set( box, ro );\n\n if ( box.status === Box.Status.NOT_LOADED ) {\n this._pushLoadBox( ro );\n }\n }\n }\n\n\n /**\n * @summary 描画対象を追加\n * \n * @param {mapray.PointCloud.Box} box\n * @param {number|null} targetChild cell領域を指定する場合は数字、全体を描画する場合はnullを指定する\n * @param ppp 描画時の解像度 (points per pixel)\n * @param parent_ppp 親Boxの描画時の解像度 (points per pixel)\n * @private\n */\n _pushLoadBox( ro ) {\n const index = this._binarySearch( this._load_boxes, ro, (ro1, ro2) => ro1.parent_points_per_pixel < ro2.parent_points_per_pixel );\n // this._load_boxes.splice( index, 0, ro );\n if ( index === -1) this._load_boxes.push(ro);\n else this._insert_with_limit( this._load_boxes, index, ro, this._load_limit );\n }\n\n\n /**\n * @private\n */\n _insert_with_limit( list, index, item, limit ) {\n if ( limit === undefined || limit - list.length > 0) {\n list.splice( index, 0, item );\n }\n else {\n // we couldn't increase the size\n if (index === list.length) return;\n for ( let i=list.length-1; i>index; i-- ) {\n list[i] = list[i-1];\n }\n list[index] = item;\n }\n }\n\n\n /**\n * @param Array sorted_list ソート済みリスト\n * \n * @private\n */\n _binarySearch( sorted_list, value, compareFunc ) {\n if ( sorted_list.length === 0 ) return -1;\n if ( compareFunc( value, sorted_list[ 0 ] ) ) return 0;\n if ( compareFunc( sorted_list[sorted_list.length - 1], value ) ) return sorted_list.length;\n if ( sorted_list.length === 1 ) return sorted_list.length;\n return this._binarySearchInner( sorted_list, value, compareFunc, 0, sorted_list.length - 1 );\n }\n\n\n /**\n * @private\n */\n _binarySearchInner( sorted_list, value, compareFunc, min, max ) {\n if ( max - min === 1 ) return max;\n const mid = 0.5 * (min + max) | 0;\n if ( compareFunc( sorted_list[mid], value ) ) min = mid;\n else max = mid;\n return this._binarySearchInner( sorted_list, value, compareFunc, min, max );\n }\n\n\n /**\n * @private\n */\n _collectNextLevel( box, parent_ppp ) {\n let child;\n for ( let i=0; i<8; i++ ) {\n if ( child = box.newChild( i, this._statistics ) ) {\n this._updateBox( child, parent_ppp );\n }\n }\n }\n}\n\n\n\n/**\n * @summary Boxの解像度の状態を表す列挙型\n * @enum {object}\n * @memberof mapray.PointCloud.Box\n * @constant\n * @see mapray.PointCloud.Box#status\n */\nconst LodStatus = {\n /**\n * 目標解像度に達しておらず、次のレベルのBoxを読み込む必要があることを示しす。\n */\n LOAD_NEXT_LEVEL: 1,\n\n /**\n * 読み込み中などの理由で解像度が計算できないため、現状を維持することを示す。\n */\n KEEP_STATUS: 0,\n\n /**\n * 目標解像度に達しており、次のレベルのBoxが必要ないことを示す。\n */\n UNLOAD_NEXT_LEVEL: -1,\n};\n\n\n\nexport default PointCloudBoxCollector;\n","'use strict';\nvar aFunction = require('../internals/a-function');\nvar isObject = require('../internals/is-object');\n\nvar slice = [].slice;\nvar factories = {};\n\nvar construct = function (C, argsLength, args) {\n if (!(argsLength in factories)) {\n for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';\n // eslint-disable-next-line no-new-func\n factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');\n } return factories[argsLength](C, args);\n};\n\n// `Function.prototype.bind` method implementation\n// https://tc39.github.io/ecma262/#sec-function.prototype.bind\nmodule.exports = Function.bind || function bind(that /* , ...args */) {\n var fn = aFunction(this);\n var partArgs = slice.call(arguments, 1);\n var boundFunction = function bound(/* args... */) {\n var args = partArgs.concat(slice.call(arguments));\n return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);\n };\n if (isObject(fn.prototype)) boundFunction.prototype = fn.prototype;\n return boundFunction;\n};\n","var $ = require('../internals/export');\nvar bind = require('../internals/function-bind');\n\n// `Function.prototype.bind` method\n// https://tc39.github.io/ecma262/#sec-function.prototype.bind\n$({ target: 'Function', proto: true }, {\n bind: bind\n});\n","/**\n * @summary フレームバッファ\n *\n * @memberof mapray\n * @private\n */\nclass FrameBuffer {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {number} width 幅\n * @param {number} height 高さ\n * @param {object} options オプション\n * @param {object[]} options.color_containers テクスチャオプションの配列\n * @param {object} [options.depth_containers] 深度テクスチャオプション\n */\n constructor( glenv, width, height, options = {} ) {\n this._glenv = glenv;\n this._width = width;\n this._height = height;\n this._options = options\n const { frame_buffer, color_containers, depth_container } = this._buildBuffers( options );\n this._frame_buffer = frame_buffer;\n this._color_containers = color_containers;\n this._depth_container = depth_container;\n }\n\n /**\n * @summary バッファの生成\n * @private\n */\n _buildBuffers( options ) {\n const ret = {};\n const width = this._width;\n const height = this._height;\n const gl = this._glenv.context;\n\n const frame_buffer = ret.frame_buffer = gl.createFramebuffer();\n gl.bindFramebuffer( gl.FRAMEBUFFER, frame_buffer );\n\n ret.color_containers = options.color_containers.map((color_container, index) => {\n const type = color_container.type || FrameBuffer.ContainerType.RENDER_BUFFER;\n const c_options = color_container.options || {};\n if ( type === FrameBuffer.ContainerType.RENDER_BUFFER ) {\n const buffer = gl.createRenderbuffer();\n gl.bindRenderbuffer( gl.RENDERBUFFER, buffer );\n gl.renderbufferStorage( gl.RENDERBUFFER, c_options.internal_format, width, height );\n gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + index, gl.RENDERBUFFER, buffer );\n return buffer;\n }\n else { // type === FrameBuffer.ContainerType.TEXTURE\n const texture = gl.createTexture();\n gl.bindTexture( gl.TEXTURE_2D, texture );\n gl.texImage2D( gl.TEXTURE_2D, 0, c_options.internal_format, width, height, 0, c_options.format, c_options.type, null );\n gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\n gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );\n gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + index, gl.TEXTURE_2D, texture, 0 );\n return texture;\n }\n });\n\n if ( options.depth_container ) {\n const type = options.depth_container.type || FrameBuffer.ContainerType.RENDER_BUFFER;\n const d_options = options.depth_container.options || {};\n if ( type === FrameBuffer.ContainerType.RENDER_BUFFER ) {\n const buffer = ret.depth_container = gl.createRenderbuffer();\n gl.bindRenderbuffer( gl.RENDERBUFFER, buffer );\n gl.renderbufferStorage( gl.RENDERBUFFER, d_options.internal_format, width, height );\n gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, buffer );\n }\n else { // type === FrameBuffer.ContainerType.TEXTURE\n const depth_container = ret.depth_container = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, depth_container);\n gl.texImage2D(gl.TEXTURE_2D, 0, d_options.internal_format, width, height, 0, d_options.format, d_options.type, null);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.framebufferTexture2D(gl.FRAMEBUFFER, options.depth_container.attach_type, gl.TEXTURE_2D, depth_container, 0);\n }\n }\n\n if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {\n throw new Error(\"ERROR: \" + gl.checkFramebufferStatus(gl.FRAMEBUFFER));\n }\n\n gl.bindTexture(gl.TEXTURE_2D, null);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n return ret;\n }\n\n\n /**\n * @summary リソースを破棄する\n */\n dispose() {\n const gl = this._glenv.context;\n gl.deleteFramebuffer(this._frame_buffer);\n this._frame_buffer = null\n this._color_containers.forEach(container => {\n this._delete_container(container);\n });\n this._color_containers = [];\n this._delete_container( container, this._depth_container );\n this._depth_container = null;\n }\n\n /**\n * @private\n */\n _delete_container( container ) {\n const gl = this._glenv.context;\n if ( container instanceof WebGLTexture ) {\n gl.deleteTexture( container );\n }\n else if ( container instanceof WebGLRenderbuffer ) {\n gl.deleteRenderbuffer( container );\n }\n }\n\n\n /**\n * @summary フレームバッファ\n * @type {WebGLFramebuffer}\n */\n get frame_buffer() {\n return this._frame_buffer;\n }\n\n /**\n * @summary カラーデータを取得(0番目を取得)\n * @type {WebGLTexture|WebGLRenderbuffer}\n */\n get color_container() {\n return this._color_containers[ 0 ];\n }\n\n /**\n * @summary カラーデータを取得\n * @param {number} index\n * @type {WebGLTexture|WebGLRenderbuffer}\n */\n getColorContainer( index ) {\n return this._color_containers[ index ];\n }\n\n /**\n * @summary カラーデータ数\n * @type {number}\n */\n get color_container_length() {\n return this._color_containers.length;\n }\n\n /**\n * @summary 深度データ\n * @type {WebGLTexture|WebGLRenderbuffer}\n */\n get depth_container() {\n return this._depth_container;\n }\n\n /**\n * @summary フレームバッファをバインドする。\n * 呼び出し側がバインド・アンバインドが対応するように使用する。\n */\n bind() {\n if (FrameBuffer.active_frame_buffer) {\n throw new Error(\"Invalid status: already bound\");\n }\n const gl = this._glenv.context;\n gl.bindFramebuffer(gl.FRAMEBUFFER, this._frame_buffer);\n FrameBuffer.active_frame_buffer = this;\n }\n\n /**\n * @summary フレームバッファをアンバインドする。\n * 呼び出し側がバインド・アンバインドが対応するように使用する。\n */\n unbind() {\n if (FrameBuffer.active_frame_buffer !== this) {\n throw new Error(\"Invalid status\");\n }\n const gl = this._glenv.context;\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n FrameBuffer.active_frame_buffer = null;\n }\n}\n\n\nFrameBuffer.active_frame_buffer = null;\n\n\n\n/**\n * @summary 要素型の列挙型\n * @enum {object}\n * @memberof mapray.ContainerType\n * @constant\n */\nconst ContainerType = {\n\n /**\n * Render Buffer\n */\n RENDER_BUFFER: { id: \"RENDER_BUFFER\" },\n\n /**\n * Texture\n */\n TEXTURE: { id: \"TEXTURE\" },\n};\n\n\nFrameBuffer.ContainerType = ContainerType;\n\n\nexport default FrameBuffer;\n","import FrameBuffer from \"./FrameBuffer\";\nimport Material from \"./Material\";\nimport Camera from \"./Camera\";\nimport GeoMath from \"./GeoMath\";\n\nimport depth_vs_code from \"./shader/depth.vert\";\nimport depth_fs_code from \"./shader/depth.frag\";\n\n\n/**\n * @summary マウスピック処理に関連する処理を行う\n *\n * @memberof mapray\n * @private\n */\nclass PickTool {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n */\n constructor( glenv ) {\n this._glenv = glenv;\n const gl = this._glenv.context;\n\n this._camera = new Camera({ width: 1, height: 1 });\n\n this._frame_buffer = new FrameBuffer( this._glenv, 1, 1, {\n color_containers: [{\n type: FrameBuffer.ContainerType.RENDER_BUFFER,\n options: {\n internal_format: gl.RGBA4,\n }\n }],\n depth_container: {\n type: FrameBuffer.ContainerType.TEXTURE,\n attach_type: gl.DEPTH_STENCIL_ATTACHMENT,\n options: {\n internal_format: gl.DEPTH_STENCIL,\n format: gl.DEPTH_STENCIL,\n type: glenv.WEBGL_depth_texture.UNSIGNED_INT_24_8_WEBGL,\n }\n },\n });\n\n this._depth_to_color_frame_buffer = new FrameBuffer( this._glenv, 1, 1, {\n color_containers: [{\n type: FrameBuffer.ContainerType.RENDER_BUFFER,\n options: {\n internal_format: gl.RGBA4,\n }\n }],\n });\n\n this._depth_to_color_materials = [\n new Material( this._glenv, depth_vs_code, define_PASS_BASE_0 + \"\\n\\n\" + depth_fs_code ),\n new Material( this._glenv, depth_vs_code, define_PASS_BASE_1 + \"\\n\\n\" + depth_fs_code ),\n ];\n\n {\n const vertex_buf = gl.createBuffer();\n {\n const vertices = [ -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, +1.0 ];\n gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n }\n\n const texcoord_buf = gl.createBuffer();\n {\n const texcoord = [ 0, 1, 0, 0, 1, 0, 1, 1 ];\n gl.bindBuffer(gl.ARRAY_BUFFER, texcoord_buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoord), gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n }\n\n {\n const indices = [ 0, 1, 2, 0, 2, 3 ];\n this._indices_length = indices.length;\n this._index_buf = gl.createBuffer()\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._index_buf);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n this._vertex_attribs = {\n \"a_position\": {\n buffer: vertex_buf,\n num_components: 2,\n component_type: gl.FLOAT,\n normalized: false,\n byte_stride: 0,\n byte_offset: 0\n },\n \"a_texcoord\": {\n buffer: texcoord_buf,\n num_components: 2,\n component_type: gl.FLOAT,\n normalized: false,\n byte_stride: 0,\n byte_offset: 0\n }\n };\n }\n\n this._rid_value = new Uint8Array( 4 );\n this._depth_value = new Uint8Array( 4 );\n }\n\n /**\n * @summary ピック用カメラを返却する。同じインスタンスが返却される。\n * @param {mapray.Camera} viewer_camera Viewreのカメラ\n */\n pickCamera( viewer_camera ) {\n const cw = viewer_camera.canvas_size.width;\n const ch = viewer_camera.canvas_size.height;\n this._camera.copyViewParameters( viewer_camera );\n const hfov_rad = viewer_camera.fov * GeoMath.DEGREE / 2;\n const hfov_rad2 = Math.atan( Math.sqrt( 2 ) * Math.tan( hfov_rad ) / Math.sqrt( cw*cw + ch*ch ) );\n this._camera.fov = 2 * hfov_rad2 / GeoMath.DEGREE;\n return this._camera;\n }\n\n /**\n * @summary Scene描画処理直前に呼ばれる\n */\n beforeRender() {\n this._frame_buffer.bind();\n }\n\n /**\n * @summary Scene描画処理直後に呼ばれる\n */\n afterRender() {\n this._frame_buffer.unbind();\n }\n\n /**\n * @summary Scene描画処理がキャンセルされたときに呼ばれる\n */\n renderCanceled() {\n this._frame_buffer.unbind();\n }\n\n /**\n * @summary ridを、描画済みテクスチャから読む(1ステップ前の値が返却される)\n * @return {number}\n */\n readRid() {\n const gl = this._glenv.context;\n const startRid = Date.now();\n this._frame_buffer.bind();\n\n let startRidRead, endRidRead;\n\n startRidRead = Date.now();\n\n gl.readPixels( 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, this._rid_value );\n endRidRead = Date.now();\n // 4bit x4 の値が 8bit x4 に格納されている。\n /*\n const rid = Math.round(\n this._rid_value[0] / 17 << 12 |\n this._rid_value[1] / 17 << 8 |\n this._rid_value[2] / 17 << 4 |\n this._rid_value[3] / 17\n );\n */\n const rid = Math.round(\n COEFFICIENTS_RID[ 0 ] * Math.round(this._rid_value[ 0 ] / 17.0) +\n COEFFICIENTS_RID[ 1 ] * Math.round(this._rid_value[ 1 ] / 17.0) +\n COEFFICIENTS_RID[ 2 ] * Math.round(this._rid_value[ 2 ] / 17.0) +\n COEFFICIENTS_RID[ 3 ] * Math.round(this._rid_value[ 3 ] / 17.0)\n );\n\n this._frame_buffer.unbind();\n\n const endRid = Date.now();\n if ( endRid - startRid > 7 ) {\n console.log(\"Render and Read Index: \" + (endRid - startRid) + \"ms gl.readPixels:\" + (endRidRead - startRidRead) + \"ms\");\n }\n\n return rid;\n }\n\n\n\n /**\n * @summary 深度値を、描画済みテクスチャから読み(1ステップ前の値が返却される)、Gocs座標系に変換する\n * @return {mapray.Matrix} view_to_clip View座標系からクリップ座標系への変換マトリックス\n * @return {mapray.Matrix} view_to_gocs View座標系からView座標系への変換マトリックス\n * @return {mapray.Vector3}\n */\n readDepth( view_to_clip, view_to_gocs ) {\n const gl = this._glenv.context;\n\n const startDepth = Date.now();\n let startDepthRead, endDepthRead;\n\n this._depth_to_color_frame_buffer.bind();\n\n let depth_clip = 0;\n gl.viewport( 0, 0, 1, 1 );\n for ( let i=0; i<2; i++ ) {\n const material = this._depth_to_color_materials[ i ];\n material.bindProgram();\n material.bindVertexAttribs(this._vertex_attribs);\n material.setInteger( \"u_sampler\", 0 );\n material.bindTexture2D( 0, this._frame_buffer.depth_container );\n gl.depthFunc( gl.ALWAYS );\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._index_buf);\n gl.drawElements(gl.TRIANGLES, this._indices_length, gl.UNSIGNED_SHORT, 0);\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n startDepthRead = Date.now();\n gl.readPixels( 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, this._depth_value );\n endDepthRead = Date.now();\n\n // 4bit x4 の値が 8bit x4 に格納されている。 => [0.0 〜 1.0]\n /*\n depth_clip += (\n this._depth_value[ 0 ] * Math.pow(2, i==0 ? -3 : -15) / 17.0 +\n this._depth_value[ 1 ] * Math.pow(2, i==0 ? -6 : -18) / 17.0 +\n this._depth_value[ 2 ] * Math.pow(2, i==0 ? -9 : -21) / 17.0 +\n this._depth_value[ 3 ] * Math.pow(2, i==0 ? -12 : -24) / 17.0\n );\n */\n const coef = COEFFICIENTS_DEPTH[ i ];\n for ( let j=0; j<4; j++ ) {\n depth_clip += coef[ j ] * this._depth_value[ j ];\n }\n }\n this._depth_to_color_frame_buffer.unbind();\n\n depth_clip = 2.0 * depth_clip - 1.0; // [0.0 〜 1.0] => [-1.0 〜 1.0]\n\n const vtc = view_to_clip;\n\n /*\n const ctv = GeoMath.inverse(vtc, GeoMath.createMatrix());\n const v = GeoMath.mul( ctv, [ 0, 0, c ] );\n */\n const v = [\n vtc[8] / vtc[0],\n vtc[9] / vtc[5],\n -1.0,\n (depth_clip + vtc[10]) / vtc[14]\n ];\n\n // const point = GeoMath.mul_Av( view_to_gocs, v );\n const m = view_to_gocs;\n const w = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3];\n const point = GeoMath.createVector3([\n ( m[0]*v[0] + m[4]*v[1] + m[ 8]*v[2] + m[12]*v[3] ) / w,\n ( m[1]*v[0] + m[5]*v[1] + m[ 9]*v[2] + m[13]*v[3] ) / w,\n ( m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3] ) / w\n ]);\n\n const endDepth = Date.now();\n if ( endDepth - startDepth > 7 ) {\n console.log(\"Render and Read Depth: \" + (endDepth - startDepth) + \"ms gl.readPixels:\" + (endDepthRead - startDepthRead) + \"ms\");\n }\n\n return point;\n }\n}\n\nconst COEFFICIENTS_RID = [];\nfor ( let i=0; i<4; i++ ) {\n COEFFICIENTS_RID[ i ] = Math.pow(16, 3 - i);\n}\n\nconst COEFFICIENTS_DEPTH = [[],[]];\nfor ( let i=0; i<4; i++ ) {\n COEFFICIENTS_DEPTH[ 0 ][ i ] = Math.pow(2, -3 * (i+1)) / 17.0;\n COEFFICIENTS_DEPTH[ 1 ][ i ] = Math.pow(2, -3 * (i+5)) / 17.0;\n}\n\n\nconst define_PASS_BASE_0 = \"#define PASS_BASE 0\";\nconst define_PASS_BASE_1 = \"#define PASS_BASE 1\";\n\n\nexport default PickTool;\n","import GeoMath from \"./GeoMath\";\nimport Globe from \"./Globe\";\nimport Entity from \"./Entity\";\nimport FlakeCollector from \"./FlakeCollector\";\nimport SurfaceMaterial from \"./SurfaceMaterial\";\nimport WireframeMaterial from \"./WireframeMaterial\";\nimport PointCloudBoxCollector from \"./PointCloudBoxCollector\";\nimport PointCloud from \"./PointCloud\";\nimport Viewer from \"./Viewer\";\nimport PickTool from \"./PickTool\";\n\n\n\n/**\n * @summary 描画対象\n * @enum {object}\n * @memberof mapray.AbstractRenderStage\n * @constant\n * @private\n */\nconst RenderTarget = {\n\n /**\n * 通常のシーン描画\n */\n SCENE: {\n id: \"SCENE\"\n },\n\n /**\n * マウスピックなど、RID取得を目的とした描画\n */\n RID: {\n id: \"RID\"\n }\n};\n\n\n\n\n/**\n * @summary 1フレーム分のレンダリングを実行\n * @desc\n * {@link mapray.Viewer} インスタンスはフレーム毎にこのクラスのインスタンスを生成してレンダリングを実行する。\n *\n * @memberof mapray\n * @private\n */\nclass AbstractRenderStage {\n\n /**\n * @param {mapray.Viewer} viewer 所有者である Viewer\n * @param {mapray.Camera} camera カメラ\n * @param {object} [renderInfo] レンダリング領域\n * @param {number} [renderInfo.sx] レンダリング領域のx位置(ビューポート中央を0, 右方向を正とする)\n * @param {number} [renderInfo.sy] レンダリング領域のy位置(ビューポート中央を0, 上方向を正とする)\n * @param {number} [renderInfo.swidth] レンダリング領域の幅\n * @param {number} [renderInfo.sheight] レンダリング領域の高さ\n */\n constructor( viewer, camera, renderInfo )\n {\n this._viewer = viewer;\n this._glenv = viewer.glenv;\n\n this._width = camera.canvas_size.width;\n this._height = camera.canvas_size.height;\n\n if ( this._width === 0 || this._height === 0 ) {\n // 画素がないのでレンダリングを省略\n this._rendering_cancel = true;\n return;\n }\n\n // _view_to_gocs, _gocs_to_view, _view_to_clip, _gocs_to_clip\n this._setupBasicMatrices( renderInfo, camera );\n\n // カメラ情報\n this._volume_planes = renderInfo.volume_planes; // 視体積の平面ベクトル配列 (視点空間)\n this._pixel_step = renderInfo.pixel_step; // 画素の変化量 (視点空間)\n\n // モデルシーン\n this._scene = viewer.scene;\n\n // リソースキャッシュ\n this._globe = viewer.globe;\n this._tile_texture_cache = viewer.tile_texture_cache;\n this._point_cloud_collection = viewer.point_cloud_collection;\n\n // 地形\n this._flake_material = null;\n this._flake_list = null;\n\n // 半透明化モード\n this._translucent_mode = false;\n\n // フレーム間のオブジェクトキャッシュ\n const render_cache = viewer._render_cache || (viewer._render_cache = {});\n if ( !render_cache.surface_material ) {\n render_cache.surface_material = new SurfaceMaterial( viewer );\n render_cache.wireframe_material = new WireframeMaterial( viewer );\n }\n if ( !render_cache.surface_pick_material ) {\n render_cache.surface_pick_material = new SurfaceMaterial( viewer, { ridMaterial: true } );\n }\n\n // デバッグ統計\n this._debug_stats = viewer.debug_stats;\n }\n\n /**\n * 半透明化モードを取得。エンティティモデルを半透明化して描画する。\n * Sceneがエンティティへ\"半透明化モード\"を伝達するのに用いる。\n * @see mapray.Entity#anchor_mode\n * @return {boolean}\n * @private\n */\n getTranslucentMode() {\n return this._translucent_mode;\n }\n\n /**\n * @summary 半透明化モードを設定。\n * @see getTranslucentMode()\n * @parm {boolean} transparent_mode\n * @private\n */\n setTranslucentMode( translucent_mode ) {\n this._translucent_mode = translucent_mode;\n }\n\n /**\n * @private\n */\n _setupBasicMatrices( renderInfo, camera )\n {\n this._view_to_gocs = camera.view_to_gocs;\n\n this._gocs_to_view = GeoMath.createMatrix();\n GeoMath.inverse_A( this._view_to_gocs, this._gocs_to_view );\n\n this._view_to_clip = renderInfo.view_to_clip;\n\n this._gocs_to_clip = GeoMath.createMatrix();\n GeoMath.mul_PzA( this._view_to_clip, this._gocs_to_view, this._gocs_to_clip );\n }\n\n /**\n * @type {RenderTarget}\n * @abstract\n */\n getRenderTarget() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * @summary Sceneがレンダリングを確定したことを通知\n * pick_objectは、primitiveがpickされたときに返却すべきオブジェクトを指定する。\n * @param {Primitive} primitive\n * @param {mapray.Entity} [pick_object]\n * @abstract\n */\n onPushPrimitive( primitive, pick_object ) {\n }\n\n /**\n * @summary 1フレームのレンダリングを実行\n * @abstract\n */\n render() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * @summary 1フレームのレンダリングを実行\n * @abstract\n * @private\n */\n _render() {\n const gl = this._glenv.context;\n\n // 描画領域全体にビューポートを設定\n gl.viewport( 0, 0, this._width, this._height );\n gl.clearColor( 0.0, 0.0, 0.0, 1.0 );\n gl.depthMask( true );\n gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );\n\n if ( this._rendering_cancel )\n return;\n\n // 地表断片データの収集\n if ( this._globe.status !== Globe.Status.READY ) {\n // まだ基底タイルデータが読み込まれていないので地表をレンダリングできない\n this._rendering_cancel = true;\n return;\n }\n\n gl.enable( gl.CULL_FACE );\n gl.enable( gl.DEPTH_TEST );\n gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); // FB のα値は変えない\n gl.depthFunc( gl.LEQUAL );\n\n const collector = new FlakeCollector( this );\n this._flake_list = collector.traverse();\n\n let vis_ground = this._viewer.getVisibility( Viewer.Category.GROUND );\n let vis_entity = this._viewer.getVisibility( Viewer.Category.ENTITY );\n\n // すべての地表断片を描画\n this._prepare_draw_flake();\n\n for ( let rflake of this._flake_list ) {\n let fro = rflake.getRenderObject();\n if ( vis_ground ) {\n this._draw_flake_base( rflake, fro.getBaseMesh() );\n }\n if ( vis_entity ) {\n this._draw_entities_on_flake( fro );\n }\n }\n\n this._draw_point_cloud();\n\n // モデルシーン描画\n if ( vis_entity ) {\n this._scene.draw( this );\n }\n }\n\n /**\n * @summary 地表断片を描画する前の準備\n *\n * @private\n */\n _prepare_draw_flake()\n {\n // RenderFlake#getRenderObject() の前に必要な処理\n let producers = this._scene.getFlakePrimitiveProducers();\n this._globe.putNextEntityProducers( producers );\n }\n\n\n /**\n * @summary 地表とレイヤーを描画\n *\n * @param {mapray.RenderFlake} rflake 地表断片データ\n * @param {mapray.FlakeMesh} mesh 地表断片メッシュ\n *\n * @private\n */\n _draw_flake_base( rflake, mesh )\n {\n const gl = this._glenv.context;\n let material = this._flake_material;\n\n material.bindProgram();\n\n var num_drawings = material.numDrawings();\n\n // 一番下の不透明地表\n if ( material.setFlakeParameter( this, rflake, mesh, 0 ) ) {\n gl.disable( gl.BLEND );\n gl.depthMask( true );\n mesh.draw( material );\n }\n\n // レイヤーの地表 (半透明の可能性あり)\n for ( let i = 1; i < num_drawings; ++i ) {\n const mat = this._viewer.layers.getDrawingLayer( i - 1 ).getMateral();\n if ( material !== mat ) {\n material = mat;\n material.bindProgram();\n }\n if ( material.setFlakeParameter( this, rflake, mesh, i ) ) {\n if ( this.getRenderTarget() === RenderTarget.SCENE ) {\n gl.enable( gl.BLEND );\n }\n gl.depthMask( false );\n mesh.draw( material );\n }\n }\n\n // 描画地表断頂点数を記録\n var stats = this._debug_stats;\n if ( stats !== null ) {\n stats.num_drawing_flake_vertices += mesh.num_vertices;\n }\n }\n\n\n /**\n * @summary 地表断片上のエンティティを描画\n *\n * @param {mapray.FlakeRenderObject} fro FlakeRenderObject インスタンス\n *\n * @private\n */\n _draw_entities_on_flake( fro )\n {\n let num_entities = fro.num_entities;\n\n if ( num_entities == 0 ) {\n // エンティティなし\n return;\n }\n\n let gl = this._glenv.context;\n\n gl.enable( gl.POLYGON_OFFSET_FILL );\n gl.depthMask( false ); // 地表に張り付いていることが前提なので深度は変更しない\n\n // todo: 仮のポリゴンオフセット\n // 実験で得た適切な値 (Windows, GeForce GT750)\n // Chrome+ANGLE: -8, -8\n // Chrome+EGL: -40, -40\n gl.polygonOffset( -8, -8 );\n\n // 透明色のマテリアルであっても、RID描画時は gl.BLEND を無効にする。\n let setBlend;\n if ( this.getRenderTarget() === RenderTarget.SCENE ) {\n setBlend = (enable) => {\n if (enable) gl.enable( gl.BLEND );\n else gl.disable( gl.BLEND );\n };\n }\n else {\n gl.disable( gl.BLEND );\n setBlend = () => {};\n }\n\n for ( let i = 0; i < num_entities; ++i ) {\n let { primitive, entity } = fro.getEntityPrimitive( i, this );\n\n setBlend( primitive.isTranslucent( this ) );\n\n this.onPushPrimitive( primitive, entity );\n primitive.draw( this );\n }\n\n gl.depthMask( true );\n gl.disable( gl.POLYGON_OFFSET_FILL );\n }\n\n\n _draw_point_cloud()\n {\n }\n}\n\n\n\n/**\n * @summary 1フレーム分のレンダリングを実行\n * @desc\n * {@link mapray.Viewer} インスタンスはフレーム毎にこのクラスのインスタンスを生成してレンダリングを実行する。\n *\n * @memberof mapray\n * @private\n */\nclass RenderStage extends AbstractRenderStage {\n /**\n * @param viewer {mapray.Viewer} 所有者である Viewer\n */\n constructor( viewer )\n {\n super( viewer, viewer.camera, viewer.camera.createRenderInfo() );\n\n // 地表マテリアルの選択\n this._flake_material = (\n viewer.render_mode === Viewer.RenderMode.WIREFRAME ?\n viewer._render_cache.wireframe_material :\n viewer._render_cache.surface_material\n );\n }\n\n\n /**\n * @type {RenderTarget}\n * @override\n */\n getRenderTarget() {\n return RenderTarget.SCENE;\n }\n\n\n /**\n * @summary 1フレームのレンダリングを実行\n * @override\n */\n render()\n {\n this._render();\n if ( this._rendering_cancel ) return;\n\n // 描画地表断片数を記録\n var stats = this._debug_stats;\n if ( stats !== null ) {\n stats.num_drawing_flakes = this._flake_list.length;\n }\n\n // フレーム終了処理\n this._globe.endFrame();\n this._tile_texture_cache.endFrame();\n this._viewer.layers.endFrame();\n }\n\n\n /**\n * @summary 点群を描画\n *\n * @private\n */\n _draw_point_cloud() {\n // const debug_handlers = PointCloud.getDebugHandlers() || {};\n const traverseDataRequestQueue = PointCloud.getTraverseDataRequestQueue();\n const traverseData = traverseDataRequestQueue.length === 0 ? null : [];\n const s = PointCloud.getStatistics() || {};\n // const statistics = ;\n if ( s.statistics_obj ) s.statistics_obj.clear();\n\n for ( let i=0; i 0 ) {\n const pick_object = this._rid_map[ rid ];\n if ( pick_object instanceof Entity ) {\n this._pick_result.entity = pick_object;\n }\n }\n\n this._pick_result.point = pick_tool.readDepth( this._view_to_clip, this._view_to_gocs );\n }\n\n\n /**\n * @type {mapray.Viewer.PickResult}\n */\n get pick_result() {\n return this._pick_result;\n }\n}\n\n\n\nexport default RenderStage;\nexport { PickStage, RenderTarget };\n","/**\n * @summary クレデンシャルモード\n * @desc\n * HTTP リクエストのクレデンシャルモードを表現する型である。
\n * @enum {object}\n * @memberof mapray\n * @constant\n * @see https://developer.mozilla.org/docs/Web/API/Request/credentials\n * @see mapray.StandardDemProvider\n */\nvar CredentialMode = {\n\n /**\n * 決してクッキーを送信しない\n */\n OMIT: { id: \"OMIT\", credentials: \"omit\" },\n\n /**\n * URL が呼び出し元のスクリプトと同一オリジンだった場合のみ、クッキーを送信\n */\n SAME_ORIGIN: { id: \"SAME_ORIGIN\", credentials: \"same-origin\" },\n\n /**\n * クロスオリジンの呼び出しであっても、常にクッキーを送信\n */\n INCLUDE: { id: \"INCLUDE\", credentials: \"include\" }\n\n};\n\n\nexport default CredentialMode;\n","import ImageProvider from \"./ImageProvider\";\nimport CredentialMode from \"./CredentialMode\";\n\n\n/**\n * @summary 標準地図画像プロバイダ\n * @classdesc\n *
汎用的な地図画像プロバイダの実装である。
\n * 構築子の引数に prefix, suffix, size, zmin, zmax を与えた場合、各メソッドの動作は以下のようになる。\n * ここで c1, c2, c3 は opts.coord_order の指定に従った第1、第2、第3の座標である。
\n * \n * requestTile( z, x, y ) -> URL が prefix + c1 + '/' + c2 + '/' + c3 + suffix の画像を要求\n * getImageSize() -> size を返す\n * getZoomLevelRange() -> new ImageProvider.Range( zmin, zmax ) を返す\n *
\n * @memberof mapray\n * @extends mapray.ImageProvider\n */\nclass StandardImageProvider extends ImageProvider {\n\n /**\n * @param {string} prefix URL の先頭文字列\n * @param {string} suffix URL の末尾文字列\n * @param {number} size 地図タイル画像の寸法\n * @param {number} zmin 最小ズームレベル\n * @param {number} zmax 最大ズームレベル\n * @param {object} [opts] オプション集合\n * @param {mapray.StandardImageProvider.CoordOrder} [opts.coord_order=ZXY] URL の座標順序\n * @param {mapray.StandardImageProvider.CoordSystem} [opts.coord_system=UPPER_LEFT] タイル XY 座標系\n * @param {mapray.CredentialMode} [opts.credentials=SAME_ORIGIN] クレデンシャルモード\n */\n constructor( prefix, suffix, size, zmin, zmax, opts )\n {\n super();\n this._prefix = prefix;\n this._suffix = suffix;\n this._size = size;\n this._min_level = zmin;\n this._max_level = zmax;\n\n // タイル座標を並び替える関数\n var orderCoords;\n if ( opts && opts.coord_order ) {\n if ( opts.coord_order === CoordOrder.ZYX ) {\n orderCoords = function( z, x, y ) { return z + \"/\" + y + \"/\" + x; };\n }\n else if ( opts.coord_order === CoordOrder.XYZ ) {\n orderCoords = function( z, x, y ) { return x + \"/\" + y + \"/\" + z; };\n }\n }\n if ( !orderCoords ) {\n // その他の場合は既定値 COORD_ORDER_ZXY を使う\n orderCoords = function( z, x, y ) { return z + \"/\" + x + \"/\" + y; };\n }\n\n // XY 座標を変換する関数\n var convCoords;\n if ( opts && opts.coord_system ) {\n if ( opts.coord_system === CoordSystem.LOWER_LEFT ) {\n convCoords = function( z, x, y ) {\n var size = Math.round( Math.pow( 2, z ) );\n return orderCoords( z, x, size - y - 1 );\n };\n }\n }\n if ( !convCoords ) {\n // その他の場合は既定値 UPPER_LEFT (無変換) を使う\n convCoords = orderCoords;\n }\n\n // 座標部分の URL を取得する関数\n this._coords_part = convCoords;\n\n // crossorigin 属性の値\n this._crossOrigin = \"anonymous\";\n if ( opts && opts.credentials ) {\n if ( opts.credentials === CredentialMode.OMIT ) {\n this._crossOrigin = null;\n }\n else if ( opts.credentials === CredentialMode.INCLUDE ) {\n this._crossOrigin = \"use-credentials\";\n }\n }\n }\n\n\n /**\n * @override\n */\n requestTile( z, x, y, callback )\n {\n var image = new Image();\n\n image.onload = function() { callback( image ); };\n image.onerror = function() { callback( null ); };\n\n if ( this._crossOrigin !== null ) {\n image.crossOrigin = this._crossOrigin;\n }\n\n image.src = this._makeURL( z, x, y );\n\n return image; // 要求 ID (実態は Image)\n }\n\n\n /**\n * @override\n */\n cancelRequest( id )\n {\n // TODO: Image 読み込みの取り消し方法は不明\n }\n\n\n /**\n * @override\n */\n getImageSize()\n {\n return this._size;\n }\n\n\n /**\n * @override\n */\n getZoomLevelRange()\n {\n return new ImageProvider.Range( this._min_level, this._max_level );\n }\n\n\n /**\n * URL を作成\n * @private\n */\n _makeURL( z, x, y )\n {\n return this._prefix + this._coords_part( z, x, y ) + this._suffix;\n }\n\n}\n\n\n/**\n * @summary URL 座標順序の列挙型\n * @desc\n * {@link mapray.StandardImageProvider} の構築子で opts.coord_order パラメータに指定する値の型である。\n * @enum {object}\n * @memberof mapray.StandardImageProvider\n * @constant\n */\nvar CoordOrder = {\n /**\n * 座標順序 Z/X/Y (既定値)\n */\n ZXY: { id: \"ZXY\" },\n\n /**\n * 座標順序 Z/Y/X\n */\n ZYX: { id: \"ZYX\" },\n\n /**\n * 座標順序 Z/X/Y\n */\n XYZ: { id: \"XYZ\" }\n};\n\n\n/**\n * @summary タイル XY 座標系の列挙型\n * @desc\n * {@link mapray.StandardImageProvider} の構築子で opts.coord_system パラメータに指定する値の型である。\n * @enum {object}\n * @memberof mapray.StandardImageProvider\n * @constant\n */\nvar CoordSystem = {\n /**\n * 原点:左上, X軸:右方向, Y軸:下方向 (既定値)\n */\n UPPER_LEFT: { id: \"UPPER_LEFT\" },\n\n /**\n * 原点:左下, X軸:右方向, Y軸:上方向\n */\n LOWER_LEFT: { id: \"LOWER_LEFT\" }\n};\n\n\n// クラス定数の定義\n{\nStandardImageProvider.CoordOrder = CoordOrder;\nStandardImageProvider.CoordSystem = CoordSystem;\n}\n\n\nexport default StandardImageProvider;\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\nvar objectKeys = require('../internals/object-keys');\nvar getOwnPropertySymbolsModule = require('../internals/object-get-own-property-symbols');\nvar propertyIsEnumerableModule = require('../internals/object-property-is-enumerable');\nvar toObject = require('../internals/to-object');\nvar IndexedObject = require('../internals/indexed-object');\n\nvar nativeAssign = Object.assign;\nvar defineProperty = Object.defineProperty;\n\n// `Object.assign` method\n// https://tc39.github.io/ecma262/#sec-object.assign\nmodule.exports = !nativeAssign || fails(function () {\n // should have correct order of operations (Edge bug)\n if (DESCRIPTORS && nativeAssign({ b: 1 }, nativeAssign(defineProperty({}, 'a', {\n enumerable: true,\n get: function () {\n defineProperty(this, 'b', {\n value: 3,\n enumerable: false\n });\n }\n }), { b: 2 })).b !== 1) return true;\n // should work with symbols and should have deterministic property order (V8 bug)\n var A = {};\n var B = {};\n // eslint-disable-next-line no-undef\n var symbol = Symbol();\n var alphabet = 'abcdefghijklmnopqrst';\n A[symbol] = 7;\n alphabet.split('').forEach(function (chr) { B[chr] = chr; });\n return nativeAssign({}, A)[symbol] != 7 || objectKeys(nativeAssign({}, B)).join('') != alphabet;\n}) ? function assign(target, source) { // eslint-disable-line no-unused-vars\n var T = toObject(target);\n var argumentsLength = arguments.length;\n var index = 1;\n var getOwnPropertySymbols = getOwnPropertySymbolsModule.f;\n var propertyIsEnumerable = propertyIsEnumerableModule.f;\n while (argumentsLength > index) {\n var S = IndexedObject(arguments[index++]);\n var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);\n var length = keys.length;\n var j = 0;\n var key;\n while (length > j) {\n key = keys[j++];\n if (!DESCRIPTORS || propertyIsEnumerable.call(S, key)) T[key] = S[key];\n }\n } return T;\n} : nativeAssign;\n","var $ = require('../internals/export');\nvar assign = require('../internals/object-assign');\n\n// `Object.assign` method\n// https://tc39.github.io/ecma262/#sec-object.assign\n$({ target: 'Object', stat: true, forced: Object.assign !== assign }, {\n assign: assign\n});\n","/**\n * @summary DEM データプロバイダ\n * @classdesc\n * レンダラーに DEM データを与えるための抽象クラスである。
\n * 以下の抽象メソッドは既定の動作がないので、利用者はこれらのメソッドをオーバライドした具象クラスを使用しなければならない。
\n * \n * - [requestTile()]{@link mapray.DemProvider#requestTile}
\n * - [cancelRequest()]{@link mapray.DemProvider#cancelRequest}
\n *
\n * [getResolutionPower()]{@link mapray.DemProvider#getResolutionPower} の既定の実装は 8 を返す。DEM タイルの解像度が 256 以外のときはこのメソッドをオーバーロードする必要がある。
\n * @memberof mapray\n * @abstract\n * @protected\n * @see mapray.StandardDemProvider\n * @see mapray.Viewer\n */\nclass DemProvider {\n\n /**\n * @summary DEM タイルデータを要求\n * @desc\n * 座標が (z, x, y) の DEM タイルデータを要求する。
\n * 指定したタイルデータの取得が成功または失敗したときに callback が非同期に呼び出されなければならない。
\n * だたし [cancelRequest()]{@link mapray.DemProvider#cancelRequest} により要求が取り消されたとき、callback は呼び出しても呼び出さなくてもよい。また非同期呼び出しである必要もない。
\n * @param {number} z ズームレベル\n * @param {number} x X タイル座標\n * @param {number} y Y タイル座標\n * @param {mapray.DemProvider.RequestCallback} callback 要求コールバック関数\n * @return {object} 要求 ID ([cancelRequest()]{@link mapray.DemProvider#cancelRequest} に与えるオブジェクト)\n * @abstract\n */\n requestTile( z, x, y, callback )\n {\n throw new Error( \"mapray.DemProvider#requestTile() method has not been overridden.\" );\n }\n\n\n /**\n * @summary DEM タイルデータの要求を取り消す\n * [requestTile()]{@link mapray.DemProvider#requestTile} による要求を可能であれば取り消す。
\n * @param {object} id 要求 ID ([requestTile()]{@link mapray.DemProvider#requestTile} から得たオブジェクト)\n * @abstract\n */\n cancelRequest( id )\n {\n throw new Error( \"mapray.DemProvider#cancelRequest() method has not been overridden.\" );\n }\n\n\n /** \n * @summary 解像度の指数を取得\n * @desc\n * DEM タイルデータ解像度の、2 を底とする対数を取得する。DEM タイルデータの解像度は必ず 2 のべき乗である。
\n * 制限: this が同じなら常に同じ値を返さなければならない。
\n * @return {number} 解像度指数\n * @abstract\n */\n getResolutionPower()\n {\n return 8;\n }\n\n}\n\n\n/**\n * @summary DEM タイルデータ要求コールバック関数型\n * @desc\n * DEM タイルデータの取得に成功または失敗したときに呼び出される関数の型である。
\n * この関数は [requestTile()]{@link mapray.DemProvider#requestTile} の callback 引数に与える。
\n * データの取得に成功したときは、data に ArrayBuffer のインスタンス、失敗したときは null を与える。
\n * ただし [cancelRequest()]{@link mapray.DemProvider#cancelRequest} により要求が取り消されたとき、コールバック関数の呼び出しは無視されるので data は任意の値でよい。
\n * @param {ArrayBuffer} data DEM タイルデータまたは null\n * @callback RequestCallback\n * @memberof mapray.DemProvider\n */\n\n\nexport default DemProvider;\n","import DemProvider from \"./DemProvider\";\nimport CredentialMode from \"./CredentialMode\";\n\n\n/**\n * @summary 標準 DEM プロバイダ\n * @classdesc\n *
汎用的な DEM プロバイダの実装である。
\n * 構築子の引数に prefix を与えた場合、各メソッドの動作は以下のようになる。\n *
\n * requestTile( z, x, y ) -> URL が prefix + z + '/' + x + '/' + y + suffix のデータを要求\n *
\n * @memberof mapray\n * @extends mapray.DemProvider\n */\nclass StandardDemProvider extends DemProvider {\n\n /**\n * @param {string} prefix URL の先頭文字列\n * @param {string} suffix URL の末尾文字列\n * @param {object} [options] オプション集合\n * @param {mapray.CredentialMode} [options.credentials=OMIT] クレデンシャルモード\n * @param {object} [options.headers={}] リクエストに追加するヘッダーの辞書\n */\n constructor( prefix, suffix, options )\n {\n super();\n\n var opts = options || {};\n\n this._prefix = prefix;\n this._suffix = suffix;\n this._credentials = (opts.credentials || CredentialMode.OMIT).credentials;\n this._headers = Object.assign( {}, opts.headers );\n }\n\n\n /**\n * @override\n */\n requestTile( z, x, y, callback )\n {\n var actrl = new AbortController();\n\n fetch( this._makeURL( z, x, y ), { credentials: this._credentials,\n headers: this._headers,\n signal: actrl.signal } )\n .then( response => {\n return response.ok ?\n response.arrayBuffer() : Promise.reject( Error( response.statusText ) );\n } )\n .then( buffer => {\n // データ取得に成功\n callback( buffer );\n } )\n .catch( () => {\n // データ取得に失敗または取り消し\n callback( null );\n } );\n\n return actrl;\n }\n\n\n /**\n * @override\n */\n cancelRequest( id )\n {\n var actrl = id; // 要求 ID を AbortController に変換\n actrl.abort(); // 取り消したので要求を中止\n }\n\n\n /**\n * URL を作成\n * @private\n */\n _makeURL( z, x, y )\n {\n return this._prefix + z + '/' + x + '/' + y + this._suffix;\n }\n\n}\n\n\nexport default StandardDemProvider;\n","import Layer from \"./Layer\";\nimport ImageProvider from \"./ImageProvider\";\n\n\n/**\n * @summary 地図レイヤー管理\n * @classdesc\n * 地図レイヤーを管理するオブジェクトである。
\n * インスタンスは {@link mapray.Viewer#layers} から得ることができる。
\n *\n * @hideconstructor\n * @memberof mapray\n * @see mapray.Layer\n */\nclass LayerCollection {\n\n /**\n * @param {mapray.Viewer} viewer Viewer\n * @param {array} layers 初期化プロパティ配列\n */\n constructor( viewer, layers )\n {\n this._viewer = viewer;\n this._glenv = viewer._glenv;\n this._layers = [];\n this._draw_layers = null;\n\n // 初期レイヤーを追加\n for ( var i = 0; i < layers.length; ++i ) {\n this.add( layers[i] );\n }\n }\n\n\n /**\n * @summary Viewerを取得\n * @type {mapray.Viewer}\n * @readonly\n * @package\n */\n get viewer() { return this._viewer; }\n\n /**\n * @summary WebGL 環境を取得\n * @type {mapray.GLEnv}\n * @readonly\n * @package\n */\n get glenv() { return this._glenv; }\n\n\n /**\n * @summary レイヤー数\n * @type {number}\n * @readonly\n */\n get num_layers() { return this._layers.length; }\n\n\n /**\n * @summary レイヤーを取得\n *\n * @param {number} index レイヤーの場所\n * @return {mapray.Layer} レイヤー\n */\n getLayer( index )\n {\n return this._layers[index];\n }\n\n\n /**\n * @summary すべてのレイヤーを削除\n */\n clear()\n {\n while ( this.num_layers() > 0 ) {\n this.remove( 0 );\n }\n }\n\n\n /**\n * @summary レイヤーを末尾に追加\n *\n * @param {object|mapray.ImageProvider} layer レイヤーのプロパティ\n * @param {mapray.ImageProvider} layer.image_provider 画像プロバイダ\n * @param {boolean} [layer.visibility] 可視性フラグ\n * @param {number} [layer.opacity] 不透明度\n */\n add( layer )\n {\n this.insert( this.num_layers, layer );\n }\n\n\n /**\n * @summary レイヤーを末尾に追加\n *\n * @param {number} index 挿入場所\n * @param {object|mapray.ImageProvider} layer レイヤーのプロパティ\n * @param {mapray.ImageProvider} layer.image_provider 画像プロバイダ\n * @param {boolean} [layer.visibility] 可視性フラグ\n * @param {number} [layer.opacity] 不透明度\n */\n insert( index, layer )\n {\n this._layers.splice( index, 0, new Layer( this, layer ) );\n this.dirtyDrawingLayers();\n }\n\n\n /**\n * @summary 特定のレイヤーを削除\n *\n * @param {number} index 削除場所\n */\n remove( index )\n {\n this._layers.splice( index, 1 );\n this.dirtyDrawingLayers();\n }\n\n\n /**\n * @summary 描画レイヤー数を取得\n *\n * @return {number} 描画レイヤー数\n * @package\n */\n numDrawingLayers()\n {\n if ( this._draw_layers === null ) {\n this._updataDrawingLayers();\n }\n return this._draw_layers.length;\n }\n\n\n /**\n * @summary 描画レイヤーを取得\n *\n * @param {number} index レイヤーの場所\n * @return {mapray.Layer} レイヤー\n * @package\n */\n getDrawingLayer( index )\n {\n if ( this._draw_layers === null ) {\n this._updataDrawingLayers();\n }\n return this._draw_layers[index];\n }\n\n\n /**\n * @summary フレームの最後の処理\n * @package\n */\n endFrame()\n {\n var layers = this._layers;\n\n for ( var i = 0; i < layers.length; ++i ) {\n layers[i].tile_cache.endFrame();\n }\n }\n\n\n /**\n * @summary 取り消し処理\n * @package\n */\n cancel()\n {\n var layers = this._layers;\n\n for ( var i = 0; i < layers.length; ++i ) {\n layers[i].tile_cache.cancel();\n }\n }\n\n\n /**\n * @summary 描画レイヤー配列を無効化\n * @package\n */\n dirtyDrawingLayers()\n {\n this._draw_layers = null;\n }\n\n\n /**\n * @summary 描画レイヤー配列を更新\n * @private\n */\n _updataDrawingLayers()\n {\n var num_layers = this.num_layers;\n\n var draw_layers = [];\n for ( var i = 0; i < num_layers; ++i ) {\n var layer = this._layers[i];\n if ( layer.image_provider.status() === ImageProvider.Status.READY && layer.visibility === true ) {\n draw_layers.push( layer );\n }\n }\n\n this._draw_layers = draw_layers;\n }\n\n}\n\n\nexport default LayerCollection;\n","import PointCloud from \"./PointCloud\";\n\n\n\n/**\n * @summary PointCloudを管理するクラス\n * @see mapray.Viewer#point_cloud_collection\n * \n * @memberof mapray\n */\nclass PointCloudCollection {\n\n /**\n * @param {mapray.Scene} scene 所属するシーン\n */\n constructor( scene ) {\n this._scene = scene;\n this._items = [];\n }\n\n\n /**\n * @summary 点群オブジェクト数\n * @type {number}\n * @readonly\n */\n get length() { return this._items.length; }\n\n\n /**\n * @summary 点群オブジェクトを取得\n *\n * @param {number} index 番号\n * @return {mapray.PointCloud} 点群\n */\n get( index ) {\n return this._items[index];\n }\n\n\n /**\n * @summary 点群オブジェクトを追加\n *\n * @param {PointCloudProvider} item 点群プロバイダ\n * @return {mapray.PointCloud} 点群\n */\n add( item ) {\n return this.insert( this.length, item );\n }\n\n\n /**\n * @summary 点群オブジェクトを指定した位置に追加\n *\n * @param {number} index 番号\n * @param {PointCloudProvider} item 点群プロバイダ\n * @return {mapray.PointCloud} 点群\n */\n insert( index, item )\n {\n const point_cloud = new PointCloud( this._scene, item );\n this._items.splice( index, 0, point_cloud );\n point_cloud.init();\n return point_cloud;\n }\n\n\n /**\n * @summary 指定した位置の点群オブジェクトを削除\n *\n * @param {number} index 番号\n * @return {mapray.PointCloud} 削除された点群\n */\n removeByIndex( index ) {\n const removedItem = this._items.splice( index, 1 )[0];\n removedItem.destroy();\n return removedItem;\n }\n\n\n /**\n * @summary 指定した点群オブジェクトを削除\n *\n * @param {mapray.PointCloud} item 削除する点群\n */\n remove( item ) {\n const index = this._items.indexOf(item);\n if (index === -1) {\n throw new Error(\"Couldn't find item: \" + item);\n }\n this.removeByIndex(index);\n }\n}\n\nexport default PointCloudCollection\n","/**\n * @summary レンダリングコールバック\n * @classdesc\n * レンダリングループでの各箇所で呼び出されるコールバック関数を実装するための抽象クラスである。
\n * サブクラスでは以下のメソッドをオーバーライドすることができる。オーバーライドしないメソッドは何もしない。
\n * \n * - [onStart()]{@link mapray.RenderCallback#onStart}
\n * - [onUpdateFrame()]{@link mapray.RenderCallback#onUpdateFrame}
\n * - [onStop()]{@link mapray.RenderCallback#onStop}
\n *
\n * @memberof mapray\n * @protected\n * @abstract\n * @see mapray.Viewer\n */\nclass RenderCallback {\n\n constructor()\n {\n this._viewer = null;\n this._is_started_ = false;\n }\n\n\n /**\n * View に取り付ける\n * @package\n */\n attach( viewer )\n {\n if ( this._viewer ) {\n throw new Error( \"RenderCallback instance is already attached\" );\n }\n\n this._viewer = viewer;\n this._is_started_ = false;\n }\n\n\n /**\n * View から切り離す\n * すでに onStart() を呼び出してい場合、onStop() を呼び出す。\n * @package\n */\n detach()\n {\n if ( this._is_started_ ) {\n this.onStop();\n this._is_started_ = false;\n }\n this._viewer = null;\n }\n\n\n /**\n * フレーム onUpdateFrame() の呼び出し\n * 取り付けてから最初のフレームのときは onStart() を呼び出す。\n * @package\n */\n onUpdateFrameInner( delta_time )\n {\n if ( !this._is_started_ ) {\n this.onStart();\n this._is_started_ = true;\n }\n this.onUpdateFrame( delta_time );\n }\n\n\n /**\n * @summary 保有者 Viewer\n * @desc\n * この RenderCallback インスタンスが設定されている Viewer インスタンスを示す。
\n * ただし RenderCallback インスタンスがどの Viewer インスタンスにも設定されていない状態では null となる。
\n * @type {?mapray.Viewer}\n * @readonly\n */\n get viewer() { return this._viewer; }\n\n\n /**\n * @summary レンダリングループ開始の処理\n * @abstract\n */\n onStart() {}\n\n\n /**\n * @summary フレームレンダリング前の処理\n * @param {number} delta_time 前フレームからの経過時間 (秒)\n * @abstract\n */\n onUpdateFrame( delta_time ) {}\n\n\n /**\n * @summary レンダリングループ終了の処理\n * @abstract\n */\n onStop() {}\n\n}\n\n\nexport default RenderCallback;\n","import RenderCallback from \"./RenderCallback\";\n\n\n/**\n * @summary 無機能 RenderCallback\n * @desc\n * Viewer に RenderCallback が設定されていないときに使用する内部クラスである。
\n * @memberof mapray\n * @extends mapray.RenderCallback\n * @private\n */\nclass NullRenderCallback extends RenderCallback {\n\n constructor() { super(); }\n\n}\n\n\nexport default NullRenderCallback;\n","import EasyBindingBlock from \"./animation/EasyBindingBlock\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary モデルシーン\n *\n * @classdesc\n * 表示するエンティティを管理するクラスである。
\n * インスタンスは {@link mapray.Viewer#scene} から得ることができる。
\n *\n * @hideconstructor\n * @memberof mapray\n * @see mapray.SceneLoader\n */\nclass Scene {\n\n /**\n * @param {mapray.Viewer} viewer Viewer インスタンス (未構築)\n * @param {mapray.GLEnv} glenv GLEnv インスタンス\n */\n constructor( viewer, glenv )\n {\n this._viewer = viewer;\n this._glenv = glenv;\n this._enode_list = []; // ENode のリスト\n this._loaders = []; // 現在読み込み中の SceneLoader (取り消し用)\n\n // animation.BindingBlock\n this._animation = new EasyBindingBlock();\n this._animation.addDescendantUnbinder( () => { this._unbindDescendantAnimations(); } );\n }\n\n\n /**\n * WebGL レンダリングコンテキスト情報\n * @type {mapray.GLEnv}\n * @readonly\n * @package\n */\n get glenv() { return this._glenv; }\n\n\n /**\n * this を保有する親オブジェクト\n * @type {mapray.Viewer}\n * @readonly\n */\n get viewer() { return this._viewer; }\n\n\n /**\n * @summary アニメーションパラメータ設定\n *\n * @type {mapray.animation.BindingBlock}\n * @readonly\n */\n get animation() { return this._animation; }\n\n\n /**\n * エンティティ数\n * @type {number}\n * @readonly\n */\n get num_entities() { return this._enode_list.length; }\n\n\n /**\n * @summary すべてのエンティティを削除\n */\n clearEntities()\n {\n this._enode_list = [];\n }\n\n\n /**\n * @summary エンティティを末尾に追加\n * @param {mapray.Entity} entity エンティティ\n */\n addEntity( entity )\n {\n if ( entity.scene !== this ) {\n throw new Error( \"invalid entity\" );\n }\n this._enode_list.push( new ENode( entity ) );\n }\n\n\n /**\n * @summary エンティティを削除\n * @param {mapray.Entity} entity エンティティ\n */\n removeEntity( entity )\n {\n var array = this._enode_list;\n\n for ( var i = 0; i < array.length; ++i ) {\n if ( array[i].entity === entity ) {\n array.splice( i, 1 );\n break;\n }\n }\n }\n\n\n /**\n * @summary エンティティを取得\n * @param {number} index インデックス\n * @return {mapray.Entity} エンティティ\n */\n getEntity( index )\n {\n return this._enode_list[index].entity;\n }\n\n\n /**\n * @summary シーンを描画\n * @param {mapray.RenderStage} stage レンダリングステージ\n * @package\n */\n draw( stage )\n {\n this._prepare_entities();\n\n // プリミティブの配列を生成\n var op_prims = []; // 不透明プリミティブ\n var tp_prims = []; // 半透明プリミティブ\n var ac_prims = []; // アンカープリミティブ\n\n for ( let {entity} of this._enode_list ) {\n if ( !entity.visibility ) continue;\n this._add_primitives( stage, entity, op_prims, tp_prims, ac_prims );\n }\n\n // プリミティブ配列を整列してから描画\n this._draw_opaque_primitives( stage, op_prims );\n this._draw_translucent_primitives( stage, tp_prims );\n this._draw_anchor_primitives( stage, ac_prims );\n }\n\n\n /**\n * @summary 描画前のエンティティの準備\n * @private\n */\n _prepare_entities()\n {\n var dem_area_updated = this._viewer.globe.dem_area_updated;\n\n for ( let enode of this._enode_list ) {\n let producer = enode.entity.getPrimitiveProducer();\n\n if ( (producer === null) || !producer.needsElevation() ) {\n // producer が存在しないとき、または\n // producer が標高を必要としないときは何もしない\n continue;\n }\n\n if ( producer.checkToCreateRegions() || enode.regions === null ) {\n // 領域情報が分からない、または領域情報が変化した可能性があるとき\n enode.regions = producer.createRegions();\n if ( enode.regions.length > 0 ) {\n enode.regions.forEach( region => { region.compile(); } );\n producer.onChangeElevation( enode.regions );\n }\n }\n else {\n if ( dem_area_updated.isEmpty() ) {\n // 更新された DEM 領域は存在しない\n // 標高の変化はないので以下の処理を省く\n continue;\n }\n\n var regions = []; // 標高に変化があった領域\n\n enode.regions.forEach( region => {\n if ( region.intersectsWith( dem_area_updated ) ) {\n // 領域の標高に変化があった\n regions.push( region );\n }\n } );\n\n if ( regions.length > 0 ) {\n // 標高が変化した可能性がある領域を通知\n producer.onChangeElevation( regions );\n }\n }\n }\n }\n\n\n /**\n * 視体積に含まれるプリミティブを追加\n * @private\n */\n _add_primitives( stage, entity, op_prims, tp_prims, ac_prims )\n {\n let producer = entity.getPrimitiveProducer();\n if ( producer === null ) return;\n\n for ( let primitive of producer.getPrimitives( stage ) ) {\n if ( primitive.isVisible( stage ) ) {\n let dst_prims = (\n entity.anchor_mode ? ac_prims :\n primitive.isTranslucent( stage ) ? tp_prims :\n op_prims\n );\n stage.onPushPrimitive( primitive, entity );\n dst_prims.push( primitive );\n }\n }\n }\n\n\n /**\n * 不透明プリミティブを整列してから描画\n * @private\n */\n _draw_opaque_primitives( stage, primitives )\n {\n // 不透明プリミティブの整列: 近接 -> 遠方 (Z 降順)\n primitives.sort( function( a, b ) { return b.sort_z - a.sort_z; } );\n\n var gl = this._glenv.context;\n gl.disable( gl.BLEND );\n gl.depthMask( true );\n\n for ( var i = 0; i < primitives.length; ++i ) {\n primitives[i].draw( stage );\n }\n }\n\n\n /**\n * 半透明プリミティブを整列してから描画\n * @private\n */\n _draw_translucent_primitives( stage, primitives )\n {\n // 半透明プリミティブの整列: 遠方 -> 近接 (Z 昇順)\n primitives.sort( function( a, b ) { return a.sort_z - b.sort_z; } );\n\n var gl = this._glenv.context;\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n gl.enable( gl.BLEND );\n }\n else {\n gl.disable( gl.BLEND );\n }\n\n gl.depthMask( false );\n\n for ( var i = 0; i < primitives.length; ++i ) {\n primitives[i].draw( stage );\n }\n\n gl.disable( gl.BLEND );\n gl.depthMask( true );\n }\n\n\n /**\n * @summary アンカープリミティブを整列してから描画。\n * {@link mapray.AbstractRenderStage#getRenderTarget} が {@link mapray.AbstractRenderStage.RenderTarget.SCENE} の場合は、\n * 隠面処理で隠れてえしまう部分は半透明で描画し、それ以外の部分は通常の描画を行う。結果的にアンカーオブジェクトが隠面において重なった場合は色が混ざった表示となる
\n * {@link mapray.AbstractRenderStage#getRenderTarget} が {@link mapray.AbstractRenderStage.RenderTarget.RID} の場合は、\n * 隠面処理で隠れてえしまう部分は強制的に描画し、それ以外の部分は通常の描画を行う。結果的にアンカーオブジェクトが隠面において重なった場合はzソートした順番でRIDが上書きされる
\n * @see {@link mapray.Entity#anchor_mode}\n * @private\n */\n _draw_anchor_primitives( stage, primitives )\n {\n // 不透明プリミティブの整列: 近接 -> 遠方 (Z 降順)\n primitives.sort( function( a, b ) { return b.sort_z - a.sort_z; } );\n\n var gl = this._glenv.context;\n gl.disable( gl.DEPTH_TEST );\n gl.depthMask( false );\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n stage.setTranslucentMode( true );\n gl.enable( gl.BLEND );\n }\n else {\n gl.disable( gl.BLEND );\n }\n\n // 遠方 -> 近接 (Z 昇順)\n for ( var i = primitives.length-1; i >= 0; --i ) {\n primitives[i].draw( stage );\n }\n\n gl.depthMask( true );\n gl.enable( gl.DEPTH_TEST );\n\n stage.setTranslucentMode( false );\n // 近接 -> 遠方 (Z 降順)\n for ( var i = 0; i < primitives.length; ++i ) {\n primitives[i].draw( stage );\n }\n\n gl.disable( gl.BLEND );\n }\n\n\n /**\n * すべての SceneLoader の読み込みを取り消す\n * @package\n */\n cancelLoaders()\n {\n var loaders = this._loaders.concat(); // 複製\n\n for ( var i = 0; i < loaders.length; ++i ) {\n loaders[i].cancel();\n }\n }\n\n\n /**\n * 読み込み中の SceneLoader を登録\n * @param {mapray.SceneLoader} loader 登録するローダー\n * @package\n */\n addLoader( loader )\n {\n this._loaders.push( loader );\n }\n\n\n /**\n * 読み込み中の SceneLoader を削除\n * @param {mapray.SceneLoader} loader 削除するローダー\n * @package\n */\n removeLoader( loader )\n {\n var index = this._loaders.indexOf( loader );\n if ( index >= 0 ) {\n this._loaders.splice( index, 1 );\n }\n }\n\n\n /**\n * @summary FlakePrimitiveProducer の反復可能オブジェクトを取得\n *\n * @return {iterable.}\n *\n * @package\n */\n getFlakePrimitiveProducers()\n {\n let producers = [];\n\n for ( let {entity} of this._enode_list ) {\n if ( !entity.visibility ) continue;\n let prod = entity.getFlakePrimitiveProducer();\n if ( prod !== null ) {\n producers.push( prod );\n }\n }\n\n return producers;\n }\n\n\n /**\n * EasyBindingBlock.DescendantUnbinder 処理\n *\n * @private\n */\n _unbindDescendantAnimations()\n {\n // すべてのエンティティを解除\n for ( let {entity} of this._enode_list ) {\n entity.animation.unbindAllRecursively();\n }\n }\n\n}\n\n\n/**\n * エンティティ管理用ノード\n *\n * @memberof mapray.Scene\n * @private\n */\nclass ENode {\n\n /**\n * @param {mapray.Entity} entity 管理対象のエンティティ\n */\n constructor( entity )\n {\n /**\n * @summary 管理対象のエンティティ\n * @member mapray.Scene.ENode#entity\n * @type {mapray.Entity}\n * @readonly\n */\n this.entity = entity;\n\n this.regions = null;\n }\n\n}\n\n\nexport default Scene;\n","var requireObjectCoercible = require('../internals/require-object-coercible');\n\nvar quot = /\"/g;\n\n// B.2.3.2.1 CreateHTML(string, tag, attribute, value)\n// https://tc39.github.io/ecma262/#sec-createhtml\nmodule.exports = function (string, tag, attribute, value) {\n var S = String(requireObjectCoercible(string));\n var p1 = '<' + tag;\n if (attribute !== '') p1 += ' ' + attribute + '=\"' + String(value).replace(quot, '"') + '\"';\n return p1 + '>' + S + '' + tag + '>';\n};\n","var fails = require('../internals/fails');\n\n// check the existence of a method, lowercase\n// of a tag and escaping quotes in arguments\nmodule.exports = function (METHOD_NAME) {\n return fails(function () {\n var test = ''[METHOD_NAME]('\"');\n return test !== test.toLowerCase() || test.split('\"').length > 3;\n });\n};\n","'use strict';\nvar $ = require('../internals/export');\nvar createHTML = require('../internals/create-html');\nvar forcedStringHTMLMethod = require('../internals/string-html-forced');\n\n// `String.prototype.link` method\n// https://tc39.github.io/ecma262/#sec-string.prototype.link\n$({ target: 'String', proto: true, forced: forcedStringHTMLMethod('link') }, {\n link: function link(url) {\n return createHTML(this, 'a', 'href', url);\n }\n});\n","var $ = require('../internals/export');\nvar lastIndexOf = require('../internals/array-last-index-of');\n\n// `Array.prototype.lastIndexOf` method\n// https://tc39.github.io/ecma262/#sec-array.prototype.lastindexof\n$({ target: 'Array', proto: true, forced: lastIndexOf !== [].lastIndexOf }, {\n lastIndexOf: lastIndexOf\n});\n","'use strict';\nvar $ = require('../internals/export');\nvar getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor').f;\nvar toLength = require('../internals/to-length');\nvar notARegExp = require('../internals/not-a-regexp');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\nvar correctIsRegExpLogic = require('../internals/correct-is-regexp-logic');\nvar IS_PURE = require('../internals/is-pure');\n\nvar nativeStartsWith = ''.startsWith;\nvar min = Math.min;\n\nvar CORRECT_IS_REGEXP_LOGIC = correctIsRegExpLogic('startsWith');\n// https://github.com/zloirock/core-js/pull/702\nvar MDN_POLYFILL_BUG = !IS_PURE && !CORRECT_IS_REGEXP_LOGIC && !!function () {\n var descriptor = getOwnPropertyDescriptor(String.prototype, 'startsWith');\n return descriptor && !descriptor.writable;\n}();\n\n// `String.prototype.startsWith` method\n// https://tc39.github.io/ecma262/#sec-string.prototype.startswith\n$({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {\n startsWith: function startsWith(searchString /* , position = 0 */) {\n var that = String(requireObjectCoercible(this));\n notARegExp(searchString);\n var index = toLength(min(arguments.length > 1 ? arguments[1] : undefined, that.length));\n var search = String(searchString);\n return nativeStartsWith\n ? nativeStartsWith.call(that, search, index)\n : that.slice(index, index + search.length) === search;\n }\n});\n","/**\n * @private\n */\nclass HTTP {\n\n static async get( url, query, option={} )\n {\n return await this.fetch( HTTP.METHOD.GET, url, query, null, option );\n }\n\n static async post( url, query, body, option={} )\n {\n return await this.fetch( HTTP.METHOD.POST, url, query, body, option );\n }\n\n static async patch( url, query, body, option={} )\n {\n return await this.fetch( HTTP.METHOD.PATCH, url, query, body, option );\n }\n\n static async put( url, query, body, option={} )\n {\n return await this.fetch( HTTP.METHOD.PUT, url, query, body, option );\n }\n\n static async delete( url, query, option={} )\n {\n return await this.fetch( HTTP.METHOD.DELETE, url, query, null, option );\n }\n\n /**\n * @summary call fetch\n *\n * \n * query = {\n * key1: value1,\n * key2: value2,\n * };\n * URL: url?key1=value1&key2=value2\n *
\n * window.fetch();\n *\n * @private\n * @param {string} method\n * @param {string} url\n * @param {object} query\n * @param {object|string} body\n * @param {object} [option] second argument of window.fetch(url, [init]).\n */\n static async fetch( method, url, query, body, option={} )\n {\n const queryText = !query ? \"\" : \"?\" + Object.keys( query ).map( k => k + \"=\" + query[k] ).join(\"&\");\n option.method = method;\n if (body) option.body = typeof(body) === \"object\" ? JSON.stringify(body) : body;\n\n let response;\n try {\n response = await fetch( url + queryText, option );\n }\n catch( error ) {\n throw new FetchError( \"Failed to fetch\", url, null, error );\n }\n if ( !response.ok ) {\n throw new FetchError( \"Failed to fetch: \" + response.statusText, url, response );\n }\n return response;\n }\n\n static isJson( mimeType ) {\n return (\n mimeType.startsWith( \"application/json\" ) ||\n mimeType === \"model/gltf+json\"\n );\n }\n}\n\nHTTP.METHOD = {\n GET: \"GET\",\n POST: \"POST\",\n PATCH: \"PATCH\",\n PUT: \"PUT\",\n DELETE: \"DELETE\",\n};\n\nHTTP.CONTENT_TYPE = \"Content-Type\";\n\nHTTP.RESPONSE_STATUS = {\n NO_CONTENT: 204\n};\n\n\n/**\n * @private\n */\nclass FetchError extends Error {\n constructor( message, url, response, cause )\n {\n super( message + \" \" + url );\n if ( Error.captureStackTrace ) {\n Error.captureStackTrace( this, FetchError );\n }\n this.name = \"FetchError\";\n this.url = url;\n this.response = response;\n this.cause = cause;\n let is_aborted = false;\n if ( cause ) {\n is_aborted = cause.message === \"The user aborted a request.\";\n this.stack += \"\\nCaused-By: \" + ( cause.stack || cause );\n }\n this.is_aborted = is_aborted;\n }\n}\n\nexport { FetchError };\nexport default HTTP;\n","var DESCRIPTORS = require('../internals/descriptors');\nvar global = require('../internals/global');\nvar isForced = require('../internals/is-forced');\nvar inheritIfRequired = require('../internals/inherit-if-required');\nvar defineProperty = require('../internals/object-define-property').f;\nvar getOwnPropertyNames = require('../internals/object-get-own-property-names').f;\nvar isRegExp = require('../internals/is-regexp');\nvar getFlags = require('../internals/regexp-flags');\nvar stickyHelpers = require('../internals/regexp-sticky-helpers');\nvar redefine = require('../internals/redefine');\nvar fails = require('../internals/fails');\nvar setInternalState = require('../internals/internal-state').set;\nvar setSpecies = require('../internals/set-species');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar MATCH = wellKnownSymbol('match');\nvar NativeRegExp = global.RegExp;\nvar RegExpPrototype = NativeRegExp.prototype;\nvar re1 = /a/g;\nvar re2 = /a/g;\n\n// \"new\" should create a new object, old webkit bug\nvar CORRECT_NEW = new NativeRegExp(re1) !== re1;\n\nvar UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y;\n\nvar FORCED = DESCRIPTORS && isForced('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y || fails(function () {\n re2[MATCH] = false;\n // RegExp constructor can alter flags and IsRegExp works correct with @@match\n return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';\n})));\n\n// `RegExp` constructor\n// https://tc39.github.io/ecma262/#sec-regexp-constructor\nif (FORCED) {\n var RegExpWrapper = function RegExp(pattern, flags) {\n var thisIsRegExp = this instanceof RegExpWrapper;\n var patternIsRegExp = isRegExp(pattern);\n var flagsAreUndefined = flags === undefined;\n var sticky;\n\n if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {\n return pattern;\n }\n\n if (CORRECT_NEW) {\n if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;\n } else if (pattern instanceof RegExpWrapper) {\n if (flagsAreUndefined) flags = getFlags.call(pattern);\n pattern = pattern.source;\n }\n\n if (UNSUPPORTED_Y) {\n sticky = !!flags && flags.indexOf('y') > -1;\n if (sticky) flags = flags.replace(/y/g, '');\n }\n\n var result = inheritIfRequired(\n CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),\n thisIsRegExp ? this : RegExpPrototype,\n RegExpWrapper\n );\n\n if (UNSUPPORTED_Y && sticky) setInternalState(result, { sticky: sticky });\n\n return result;\n };\n var proxy = function (key) {\n key in RegExpWrapper || defineProperty(RegExpWrapper, key, {\n configurable: true,\n get: function () { return NativeRegExp[key]; },\n set: function (it) { NativeRegExp[key] = it; }\n });\n };\n var keys = getOwnPropertyNames(NativeRegExp);\n var index = 0;\n while (keys.length > index) proxy(keys[index++]);\n RegExpPrototype.constructor = RegExpWrapper;\n RegExpWrapper.prototype = RegExpPrototype;\n redefine(global, 'RegExp', RegExpWrapper);\n}\n\n// https://tc39.github.io/ecma262/#sec-get-regexp-@@species\nsetSpecies('RegExp');\n","var fails = require('../internals/fails');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\nvar IS_PURE = require('../internals/is-pure');\n\nvar ITERATOR = wellKnownSymbol('iterator');\n\nmodule.exports = !fails(function () {\n var url = new URL('b?a=1&b=2&c=3', 'http://a');\n var searchParams = url.searchParams;\n var result = '';\n url.pathname = 'c%20d';\n searchParams.forEach(function (value, key) {\n searchParams['delete']('b');\n result += key + value;\n });\n return (IS_PURE && !url.toJSON)\n || !searchParams.sort\n || url.href !== 'http://a/c%20d?a=1&c=3'\n || searchParams.get('c') !== '3'\n || String(new URLSearchParams('?a=1')) !== 'a=1'\n || !searchParams[ITERATOR]\n // throws in Edge\n || new URL('https://a@b').username !== 'a'\n || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'\n // not punycoded in Edge\n || new URL('http://тест').host !== 'xn--e1aybc'\n // not escaped in Chrome 62-\n || new URL('http://a#б').hash !== '#%D0%B1'\n // fails in Chrome 66-\n || result !== 'a1c3'\n // throws in Safari\n || new URL('http://x', undefined).host !== 'x';\n});\n","'use strict';\n// based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js\nvar maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1\nvar base = 36;\nvar tMin = 1;\nvar tMax = 26;\nvar skew = 38;\nvar damp = 700;\nvar initialBias = 72;\nvar initialN = 128; // 0x80\nvar delimiter = '-'; // '\\x2D'\nvar regexNonASCII = /[^\\0-\\u007E]/; // non-ASCII chars\nvar regexSeparators = /[.\\u3002\\uFF0E\\uFF61]/g; // RFC 3490 separators\nvar OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';\nvar baseMinusTMin = base - tMin;\nvar floor = Math.floor;\nvar stringFromCharCode = String.fromCharCode;\n\n/**\n * Creates an array containing the numeric code points of each Unicode\n * character in the string. While JavaScript uses UCS-2 internally,\n * this function will convert a pair of surrogate halves (each of which\n * UCS-2 exposes as separate characters) into a single code point,\n * matching UTF-16.\n */\nvar ucs2decode = function (string) {\n var output = [];\n var counter = 0;\n var length = string.length;\n while (counter < length) {\n var value = string.charCodeAt(counter++);\n if (value >= 0xD800 && value <= 0xDBFF && counter < length) {\n // It's a high surrogate, and there is a next character.\n var extra = string.charCodeAt(counter++);\n if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.\n output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);\n } else {\n // It's an unmatched surrogate; only append this code unit, in case the\n // next code unit is the high surrogate of a surrogate pair.\n output.push(value);\n counter--;\n }\n } else {\n output.push(value);\n }\n }\n return output;\n};\n\n/**\n * Converts a digit/integer into a basic code point.\n */\nvar digitToBasic = function (digit) {\n // 0..25 map to ASCII a..z or A..Z\n // 26..35 map to ASCII 0..9\n return digit + 22 + 75 * (digit < 26);\n};\n\n/**\n * Bias adaptation function as per section 3.4 of RFC 3492.\n * https://tools.ietf.org/html/rfc3492#section-3.4\n */\nvar adapt = function (delta, numPoints, firstTime) {\n var k = 0;\n delta = firstTime ? floor(delta / damp) : delta >> 1;\n delta += floor(delta / numPoints);\n for (; delta > baseMinusTMin * tMax >> 1; k += base) {\n delta = floor(delta / baseMinusTMin);\n }\n return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));\n};\n\n/**\n * Converts a string of Unicode symbols (e.g. a domain name label) to a\n * Punycode string of ASCII-only symbols.\n */\n// eslint-disable-next-line max-statements\nvar encode = function (input) {\n var output = [];\n\n // Convert the input in UCS-2 to an array of Unicode code points.\n input = ucs2decode(input);\n\n // Cache the length.\n var inputLength = input.length;\n\n // Initialize the state.\n var n = initialN;\n var delta = 0;\n var bias = initialBias;\n var i, currentValue;\n\n // Handle the basic code points.\n for (i = 0; i < input.length; i++) {\n currentValue = input[i];\n if (currentValue < 0x80) {\n output.push(stringFromCharCode(currentValue));\n }\n }\n\n var basicLength = output.length; // number of basic code points.\n var handledCPCount = basicLength; // number of code points that have been handled;\n\n // Finish the basic string with a delimiter unless it's empty.\n if (basicLength) {\n output.push(delimiter);\n }\n\n // Main encoding loop:\n while (handledCPCount < inputLength) {\n // All non-basic code points < n have been handled already. Find the next larger one:\n var m = maxInt;\n for (i = 0; i < input.length; i++) {\n currentValue = input[i];\n if (currentValue >= n && currentValue < m) {\n m = currentValue;\n }\n }\n\n // Increase `delta` enough to advance the decoder's state to , but guard against overflow.\n var handledCPCountPlusOne = handledCPCount + 1;\n if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {\n throw RangeError(OVERFLOW_ERROR);\n }\n\n delta += (m - n) * handledCPCountPlusOne;\n n = m;\n\n for (i = 0; i < input.length; i++) {\n currentValue = input[i];\n if (currentValue < n && ++delta > maxInt) {\n throw RangeError(OVERFLOW_ERROR);\n }\n if (currentValue == n) {\n // Represent delta as a generalized variable-length integer.\n var q = delta;\n for (var k = base; /* no condition */; k += base) {\n var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);\n if (q < t) break;\n var qMinusT = q - t;\n var baseMinusT = base - t;\n output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));\n q = floor(qMinusT / baseMinusT);\n }\n\n output.push(stringFromCharCode(digitToBasic(q)));\n bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);\n delta = 0;\n ++handledCPCount;\n }\n }\n\n ++delta;\n ++n;\n }\n return output.join('');\n};\n\nmodule.exports = function (input) {\n var encoded = [];\n var labels = input.toLowerCase().replace(regexSeparators, '\\u002E').split('.');\n var i, label;\n for (i = 0; i < labels.length; i++) {\n label = labels[i];\n encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);\n }\n return encoded.join('.');\n};\n","var anObject = require('../internals/an-object');\nvar getIteratorMethod = require('../internals/get-iterator-method');\n\nmodule.exports = function (it) {\n var iteratorMethod = getIteratorMethod(it);\n if (typeof iteratorMethod != 'function') {\n throw TypeError(String(it) + ' is not iterable');\n } return anObject(iteratorMethod.call(it));\n};\n","'use strict';\n// TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`\nrequire('../modules/es.array.iterator');\nvar $ = require('../internals/export');\nvar getBuiltIn = require('../internals/get-built-in');\nvar USE_NATIVE_URL = require('../internals/native-url');\nvar redefine = require('../internals/redefine');\nvar redefineAll = require('../internals/redefine-all');\nvar setToStringTag = require('../internals/set-to-string-tag');\nvar createIteratorConstructor = require('../internals/create-iterator-constructor');\nvar InternalStateModule = require('../internals/internal-state');\nvar anInstance = require('../internals/an-instance');\nvar hasOwn = require('../internals/has');\nvar bind = require('../internals/function-bind-context');\nvar classof = require('../internals/classof');\nvar anObject = require('../internals/an-object');\nvar isObject = require('../internals/is-object');\nvar create = require('../internals/object-create');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\nvar getIterator = require('../internals/get-iterator');\nvar getIteratorMethod = require('../internals/get-iterator-method');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar $fetch = getBuiltIn('fetch');\nvar Headers = getBuiltIn('Headers');\nvar ITERATOR = wellKnownSymbol('iterator');\nvar URL_SEARCH_PARAMS = 'URLSearchParams';\nvar URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';\nvar setInternalState = InternalStateModule.set;\nvar getInternalParamsState = InternalStateModule.getterFor(URL_SEARCH_PARAMS);\nvar getInternalIteratorState = InternalStateModule.getterFor(URL_SEARCH_PARAMS_ITERATOR);\n\nvar plus = /\\+/g;\nvar sequences = Array(4);\n\nvar percentSequence = function (bytes) {\n return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\\\da-f]{2}){' + bytes + '})', 'gi'));\n};\n\nvar percentDecode = function (sequence) {\n try {\n return decodeURIComponent(sequence);\n } catch (error) {\n return sequence;\n }\n};\n\nvar deserialize = function (it) {\n var result = it.replace(plus, ' ');\n var bytes = 4;\n try {\n return decodeURIComponent(result);\n } catch (error) {\n while (bytes) {\n result = result.replace(percentSequence(bytes--), percentDecode);\n }\n return result;\n }\n};\n\nvar find = /[!'()~]|%20/g;\n\nvar replace = {\n '!': '%21',\n \"'\": '%27',\n '(': '%28',\n ')': '%29',\n '~': '%7E',\n '%20': '+'\n};\n\nvar replacer = function (match) {\n return replace[match];\n};\n\nvar serialize = function (it) {\n return encodeURIComponent(it).replace(find, replacer);\n};\n\nvar parseSearchParams = function (result, query) {\n if (query) {\n var attributes = query.split('&');\n var index = 0;\n var attribute, entry;\n while (index < attributes.length) {\n attribute = attributes[index++];\n if (attribute.length) {\n entry = attribute.split('=');\n result.push({\n key: deserialize(entry.shift()),\n value: deserialize(entry.join('='))\n });\n }\n }\n }\n};\n\nvar updateSearchParams = function (query) {\n this.entries.length = 0;\n parseSearchParams(this.entries, query);\n};\n\nvar validateArgumentsLength = function (passed, required) {\n if (passed < required) throw TypeError('Not enough arguments');\n};\n\nvar URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {\n setInternalState(this, {\n type: URL_SEARCH_PARAMS_ITERATOR,\n iterator: getIterator(getInternalParamsState(params).entries),\n kind: kind\n });\n}, 'Iterator', function next() {\n var state = getInternalIteratorState(this);\n var kind = state.kind;\n var step = state.iterator.next();\n var entry = step.value;\n if (!step.done) {\n step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];\n } return step;\n});\n\n// `URLSearchParams` constructor\n// https://url.spec.whatwg.org/#interface-urlsearchparams\nvar URLSearchParamsConstructor = function URLSearchParams(/* init */) {\n anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);\n var init = arguments.length > 0 ? arguments[0] : undefined;\n var that = this;\n var entries = [];\n var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;\n\n setInternalState(that, {\n type: URL_SEARCH_PARAMS,\n entries: entries,\n updateURL: function () { /* empty */ },\n updateSearchParams: updateSearchParams\n });\n\n if (init !== undefined) {\n if (isObject(init)) {\n iteratorMethod = getIteratorMethod(init);\n if (typeof iteratorMethod === 'function') {\n iterator = iteratorMethod.call(init);\n next = iterator.next;\n while (!(step = next.call(iterator)).done) {\n entryIterator = getIterator(anObject(step.value));\n entryNext = entryIterator.next;\n if (\n (first = entryNext.call(entryIterator)).done ||\n (second = entryNext.call(entryIterator)).done ||\n !entryNext.call(entryIterator).done\n ) throw TypeError('Expected sequence with length 2');\n entries.push({ key: first.value + '', value: second.value + '' });\n }\n } else for (key in init) if (hasOwn(init, key)) entries.push({ key: key, value: init[key] + '' });\n } else {\n parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');\n }\n }\n};\n\nvar URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;\n\nredefineAll(URLSearchParamsPrototype, {\n // `URLSearchParams.prototype.appent` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-append\n append: function append(name, value) {\n validateArgumentsLength(arguments.length, 2);\n var state = getInternalParamsState(this);\n state.entries.push({ key: name + '', value: value + '' });\n state.updateURL();\n },\n // `URLSearchParams.prototype.delete` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-delete\n 'delete': function (name) {\n validateArgumentsLength(arguments.length, 1);\n var state = getInternalParamsState(this);\n var entries = state.entries;\n var key = name + '';\n var index = 0;\n while (index < entries.length) {\n if (entries[index].key === key) entries.splice(index, 1);\n else index++;\n }\n state.updateURL();\n },\n // `URLSearchParams.prototype.get` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-get\n get: function get(name) {\n validateArgumentsLength(arguments.length, 1);\n var entries = getInternalParamsState(this).entries;\n var key = name + '';\n var index = 0;\n for (; index < entries.length; index++) {\n if (entries[index].key === key) return entries[index].value;\n }\n return null;\n },\n // `URLSearchParams.prototype.getAll` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-getall\n getAll: function getAll(name) {\n validateArgumentsLength(arguments.length, 1);\n var entries = getInternalParamsState(this).entries;\n var key = name + '';\n var result = [];\n var index = 0;\n for (; index < entries.length; index++) {\n if (entries[index].key === key) result.push(entries[index].value);\n }\n return result;\n },\n // `URLSearchParams.prototype.has` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-has\n has: function has(name) {\n validateArgumentsLength(arguments.length, 1);\n var entries = getInternalParamsState(this).entries;\n var key = name + '';\n var index = 0;\n while (index < entries.length) {\n if (entries[index++].key === key) return true;\n }\n return false;\n },\n // `URLSearchParams.prototype.set` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-set\n set: function set(name, value) {\n validateArgumentsLength(arguments.length, 1);\n var state = getInternalParamsState(this);\n var entries = state.entries;\n var found = false;\n var key = name + '';\n var val = value + '';\n var index = 0;\n var entry;\n for (; index < entries.length; index++) {\n entry = entries[index];\n if (entry.key === key) {\n if (found) entries.splice(index--, 1);\n else {\n found = true;\n entry.value = val;\n }\n }\n }\n if (!found) entries.push({ key: key, value: val });\n state.updateURL();\n },\n // `URLSearchParams.prototype.sort` method\n // https://url.spec.whatwg.org/#dom-urlsearchparams-sort\n sort: function sort() {\n var state = getInternalParamsState(this);\n var entries = state.entries;\n // Array#sort is not stable in some engines\n var slice = entries.slice();\n var entry, entriesIndex, sliceIndex;\n entries.length = 0;\n for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {\n entry = slice[sliceIndex];\n for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {\n if (entries[entriesIndex].key > entry.key) {\n entries.splice(entriesIndex, 0, entry);\n break;\n }\n }\n if (entriesIndex === sliceIndex) entries.push(entry);\n }\n state.updateURL();\n },\n // `URLSearchParams.prototype.forEach` method\n forEach: function forEach(callback /* , thisArg */) {\n var entries = getInternalParamsState(this).entries;\n var boundFunction = bind(callback, arguments.length > 1 ? arguments[1] : undefined, 3);\n var index = 0;\n var entry;\n while (index < entries.length) {\n entry = entries[index++];\n boundFunction(entry.value, entry.key, this);\n }\n },\n // `URLSearchParams.prototype.keys` method\n keys: function keys() {\n return new URLSearchParamsIterator(this, 'keys');\n },\n // `URLSearchParams.prototype.values` method\n values: function values() {\n return new URLSearchParamsIterator(this, 'values');\n },\n // `URLSearchParams.prototype.entries` method\n entries: function entries() {\n return new URLSearchParamsIterator(this, 'entries');\n }\n}, { enumerable: true });\n\n// `URLSearchParams.prototype[@@iterator]` method\nredefine(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries);\n\n// `URLSearchParams.prototype.toString` method\n// https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior\nredefine(URLSearchParamsPrototype, 'toString', function toString() {\n var entries = getInternalParamsState(this).entries;\n var result = [];\n var index = 0;\n var entry;\n while (index < entries.length) {\n entry = entries[index++];\n result.push(serialize(entry.key) + '=' + serialize(entry.value));\n } return result.join('&');\n}, { enumerable: true });\n\nsetToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);\n\n$({ global: true, forced: !USE_NATIVE_URL }, {\n URLSearchParams: URLSearchParamsConstructor\n});\n\n// Wrap `fetch` for correct work with polyfilled `URLSearchParams`\n// https://github.com/zloirock/core-js/issues/674\nif (!USE_NATIVE_URL && typeof $fetch == 'function' && typeof Headers == 'function') {\n $({ global: true, enumerable: true, forced: true }, {\n fetch: function fetch(input /* , init */) {\n var args = [input];\n var init, body, headers;\n if (arguments.length > 1) {\n init = arguments[1];\n if (isObject(init)) {\n body = init.body;\n if (classof(body) === URL_SEARCH_PARAMS) {\n headers = init.headers ? new Headers(init.headers) : new Headers();\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');\n }\n init = create(init, {\n body: createPropertyDescriptor(0, String(body)),\n headers: createPropertyDescriptor(0, headers)\n });\n }\n }\n args.push(init);\n } return $fetch.apply(this, args);\n }\n });\n}\n\nmodule.exports = {\n URLSearchParams: URLSearchParamsConstructor,\n getState: getInternalParamsState\n};\n","'use strict';\n// TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`\nrequire('../modules/es.string.iterator');\nvar $ = require('../internals/export');\nvar DESCRIPTORS = require('../internals/descriptors');\nvar USE_NATIVE_URL = require('../internals/native-url');\nvar global = require('../internals/global');\nvar defineProperties = require('../internals/object-define-properties');\nvar redefine = require('../internals/redefine');\nvar anInstance = require('../internals/an-instance');\nvar has = require('../internals/has');\nvar assign = require('../internals/object-assign');\nvar arrayFrom = require('../internals/array-from');\nvar codeAt = require('../internals/string-multibyte').codeAt;\nvar toASCII = require('../internals/string-punycode-to-ascii');\nvar setToStringTag = require('../internals/set-to-string-tag');\nvar URLSearchParamsModule = require('../modules/web.url-search-params');\nvar InternalStateModule = require('../internals/internal-state');\n\nvar NativeURL = global.URL;\nvar URLSearchParams = URLSearchParamsModule.URLSearchParams;\nvar getInternalSearchParamsState = URLSearchParamsModule.getState;\nvar setInternalState = InternalStateModule.set;\nvar getInternalURLState = InternalStateModule.getterFor('URL');\nvar floor = Math.floor;\nvar pow = Math.pow;\n\nvar INVALID_AUTHORITY = 'Invalid authority';\nvar INVALID_SCHEME = 'Invalid scheme';\nvar INVALID_HOST = 'Invalid host';\nvar INVALID_PORT = 'Invalid port';\n\nvar ALPHA = /[A-Za-z]/;\nvar ALPHANUMERIC = /[\\d+\\-.A-Za-z]/;\nvar DIGIT = /\\d/;\nvar HEX_START = /^(0x|0X)/;\nvar OCT = /^[0-7]+$/;\nvar DEC = /^\\d+$/;\nvar HEX = /^[\\dA-Fa-f]+$/;\n// eslint-disable-next-line no-control-regex\nvar FORBIDDEN_HOST_CODE_POINT = /[\\u0000\\u0009\\u000A\\u000D #%/:?@[\\\\]]/;\n// eslint-disable-next-line no-control-regex\nvar FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\\u0000\\u0009\\u000A\\u000D #/:?@[\\\\]]/;\n// eslint-disable-next-line no-control-regex\nvar LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\\u0000-\\u001F ]+|[\\u0000-\\u001F ]+$/g;\n// eslint-disable-next-line no-control-regex\nvar TAB_AND_NEW_LINE = /[\\u0009\\u000A\\u000D]/g;\nvar EOF;\n\nvar parseHost = function (url, input) {\n var result, codePoints, index;\n if (input.charAt(0) == '[') {\n if (input.charAt(input.length - 1) != ']') return INVALID_HOST;\n result = parseIPv6(input.slice(1, -1));\n if (!result) return INVALID_HOST;\n url.host = result;\n // opaque host\n } else if (!isSpecial(url)) {\n if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;\n result = '';\n codePoints = arrayFrom(input);\n for (index = 0; index < codePoints.length; index++) {\n result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);\n }\n url.host = result;\n } else {\n input = toASCII(input);\n if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;\n result = parseIPv4(input);\n if (result === null) return INVALID_HOST;\n url.host = result;\n }\n};\n\nvar parseIPv4 = function (input) {\n var parts = input.split('.');\n var partsLength, numbers, index, part, radix, number, ipv4;\n if (parts.length && parts[parts.length - 1] == '') {\n parts.pop();\n }\n partsLength = parts.length;\n if (partsLength > 4) return input;\n numbers = [];\n for (index = 0; index < partsLength; index++) {\n part = parts[index];\n if (part == '') return input;\n radix = 10;\n if (part.length > 1 && part.charAt(0) == '0') {\n radix = HEX_START.test(part) ? 16 : 8;\n part = part.slice(radix == 8 ? 1 : 2);\n }\n if (part === '') {\n number = 0;\n } else {\n if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;\n number = parseInt(part, radix);\n }\n numbers.push(number);\n }\n for (index = 0; index < partsLength; index++) {\n number = numbers[index];\n if (index == partsLength - 1) {\n if (number >= pow(256, 5 - partsLength)) return null;\n } else if (number > 255) return null;\n }\n ipv4 = numbers.pop();\n for (index = 0; index < numbers.length; index++) {\n ipv4 += numbers[index] * pow(256, 3 - index);\n }\n return ipv4;\n};\n\n// eslint-disable-next-line max-statements\nvar parseIPv6 = function (input) {\n var address = [0, 0, 0, 0, 0, 0, 0, 0];\n var pieceIndex = 0;\n var compress = null;\n var pointer = 0;\n var value, length, numbersSeen, ipv4Piece, number, swaps, swap;\n\n var char = function () {\n return input.charAt(pointer);\n };\n\n if (char() == ':') {\n if (input.charAt(1) != ':') return;\n pointer += 2;\n pieceIndex++;\n compress = pieceIndex;\n }\n while (char()) {\n if (pieceIndex == 8) return;\n if (char() == ':') {\n if (compress !== null) return;\n pointer++;\n pieceIndex++;\n compress = pieceIndex;\n continue;\n }\n value = length = 0;\n while (length < 4 && HEX.test(char())) {\n value = value * 16 + parseInt(char(), 16);\n pointer++;\n length++;\n }\n if (char() == '.') {\n if (length == 0) return;\n pointer -= length;\n if (pieceIndex > 6) return;\n numbersSeen = 0;\n while (char()) {\n ipv4Piece = null;\n if (numbersSeen > 0) {\n if (char() == '.' && numbersSeen < 4) pointer++;\n else return;\n }\n if (!DIGIT.test(char())) return;\n while (DIGIT.test(char())) {\n number = parseInt(char(), 10);\n if (ipv4Piece === null) ipv4Piece = number;\n else if (ipv4Piece == 0) return;\n else ipv4Piece = ipv4Piece * 10 + number;\n if (ipv4Piece > 255) return;\n pointer++;\n }\n address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;\n numbersSeen++;\n if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;\n }\n if (numbersSeen != 4) return;\n break;\n } else if (char() == ':') {\n pointer++;\n if (!char()) return;\n } else if (char()) return;\n address[pieceIndex++] = value;\n }\n if (compress !== null) {\n swaps = pieceIndex - compress;\n pieceIndex = 7;\n while (pieceIndex != 0 && swaps > 0) {\n swap = address[pieceIndex];\n address[pieceIndex--] = address[compress + swaps - 1];\n address[compress + --swaps] = swap;\n }\n } else if (pieceIndex != 8) return;\n return address;\n};\n\nvar findLongestZeroSequence = function (ipv6) {\n var maxIndex = null;\n var maxLength = 1;\n var currStart = null;\n var currLength = 0;\n var index = 0;\n for (; index < 8; index++) {\n if (ipv6[index] !== 0) {\n if (currLength > maxLength) {\n maxIndex = currStart;\n maxLength = currLength;\n }\n currStart = null;\n currLength = 0;\n } else {\n if (currStart === null) currStart = index;\n ++currLength;\n }\n }\n if (currLength > maxLength) {\n maxIndex = currStart;\n maxLength = currLength;\n }\n return maxIndex;\n};\n\nvar serializeHost = function (host) {\n var result, index, compress, ignore0;\n // ipv4\n if (typeof host == 'number') {\n result = [];\n for (index = 0; index < 4; index++) {\n result.unshift(host % 256);\n host = floor(host / 256);\n } return result.join('.');\n // ipv6\n } else if (typeof host == 'object') {\n result = '';\n compress = findLongestZeroSequence(host);\n for (index = 0; index < 8; index++) {\n if (ignore0 && host[index] === 0) continue;\n if (ignore0) ignore0 = false;\n if (compress === index) {\n result += index ? ':' : '::';\n ignore0 = true;\n } else {\n result += host[index].toString(16);\n if (index < 7) result += ':';\n }\n }\n return '[' + result + ']';\n } return host;\n};\n\nvar C0ControlPercentEncodeSet = {};\nvar fragmentPercentEncodeSet = assign({}, C0ControlPercentEncodeSet, {\n ' ': 1, '\"': 1, '<': 1, '>': 1, '`': 1\n});\nvar pathPercentEncodeSet = assign({}, fragmentPercentEncodeSet, {\n '#': 1, '?': 1, '{': 1, '}': 1\n});\nvar userinfoPercentEncodeSet = assign({}, pathPercentEncodeSet, {\n '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\\\': 1, ']': 1, '^': 1, '|': 1\n});\n\nvar percentEncode = function (char, set) {\n var code = codeAt(char, 0);\n return code > 0x20 && code < 0x7F && !has(set, char) ? char : encodeURIComponent(char);\n};\n\nvar specialSchemes = {\n ftp: 21,\n file: null,\n http: 80,\n https: 443,\n ws: 80,\n wss: 443\n};\n\nvar isSpecial = function (url) {\n return has(specialSchemes, url.scheme);\n};\n\nvar includesCredentials = function (url) {\n return url.username != '' || url.password != '';\n};\n\nvar cannotHaveUsernamePasswordPort = function (url) {\n return !url.host || url.cannotBeABaseURL || url.scheme == 'file';\n};\n\nvar isWindowsDriveLetter = function (string, normalized) {\n var second;\n return string.length == 2 && ALPHA.test(string.charAt(0))\n && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));\n};\n\nvar startsWithWindowsDriveLetter = function (string) {\n var third;\n return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (\n string.length == 2 ||\n ((third = string.charAt(2)) === '/' || third === '\\\\' || third === '?' || third === '#')\n );\n};\n\nvar shortenURLsPath = function (url) {\n var path = url.path;\n var pathSize = path.length;\n if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {\n path.pop();\n }\n};\n\nvar isSingleDot = function (segment) {\n return segment === '.' || segment.toLowerCase() === '%2e';\n};\n\nvar isDoubleDot = function (segment) {\n segment = segment.toLowerCase();\n return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';\n};\n\n// States:\nvar SCHEME_START = {};\nvar SCHEME = {};\nvar NO_SCHEME = {};\nvar SPECIAL_RELATIVE_OR_AUTHORITY = {};\nvar PATH_OR_AUTHORITY = {};\nvar RELATIVE = {};\nvar RELATIVE_SLASH = {};\nvar SPECIAL_AUTHORITY_SLASHES = {};\nvar SPECIAL_AUTHORITY_IGNORE_SLASHES = {};\nvar AUTHORITY = {};\nvar HOST = {};\nvar HOSTNAME = {};\nvar PORT = {};\nvar FILE = {};\nvar FILE_SLASH = {};\nvar FILE_HOST = {};\nvar PATH_START = {};\nvar PATH = {};\nvar CANNOT_BE_A_BASE_URL_PATH = {};\nvar QUERY = {};\nvar FRAGMENT = {};\n\n// eslint-disable-next-line max-statements\nvar parseURL = function (url, input, stateOverride, base) {\n var state = stateOverride || SCHEME_START;\n var pointer = 0;\n var buffer = '';\n var seenAt = false;\n var seenBracket = false;\n var seenPasswordToken = false;\n var codePoints, char, bufferCodePoints, failure;\n\n if (!stateOverride) {\n url.scheme = '';\n url.username = '';\n url.password = '';\n url.host = null;\n url.port = null;\n url.path = [];\n url.query = null;\n url.fragment = null;\n url.cannotBeABaseURL = false;\n input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');\n }\n\n input = input.replace(TAB_AND_NEW_LINE, '');\n\n codePoints = arrayFrom(input);\n\n while (pointer <= codePoints.length) {\n char = codePoints[pointer];\n switch (state) {\n case SCHEME_START:\n if (char && ALPHA.test(char)) {\n buffer += char.toLowerCase();\n state = SCHEME;\n } else if (!stateOverride) {\n state = NO_SCHEME;\n continue;\n } else return INVALID_SCHEME;\n break;\n\n case SCHEME:\n if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {\n buffer += char.toLowerCase();\n } else if (char == ':') {\n if (stateOverride && (\n (isSpecial(url) != has(specialSchemes, buffer)) ||\n (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||\n (url.scheme == 'file' && !url.host)\n )) return;\n url.scheme = buffer;\n if (stateOverride) {\n if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;\n return;\n }\n buffer = '';\n if (url.scheme == 'file') {\n state = FILE;\n } else if (isSpecial(url) && base && base.scheme == url.scheme) {\n state = SPECIAL_RELATIVE_OR_AUTHORITY;\n } else if (isSpecial(url)) {\n state = SPECIAL_AUTHORITY_SLASHES;\n } else if (codePoints[pointer + 1] == '/') {\n state = PATH_OR_AUTHORITY;\n pointer++;\n } else {\n url.cannotBeABaseURL = true;\n url.path.push('');\n state = CANNOT_BE_A_BASE_URL_PATH;\n }\n } else if (!stateOverride) {\n buffer = '';\n state = NO_SCHEME;\n pointer = 0;\n continue;\n } else return INVALID_SCHEME;\n break;\n\n case NO_SCHEME:\n if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;\n if (base.cannotBeABaseURL && char == '#') {\n url.scheme = base.scheme;\n url.path = base.path.slice();\n url.query = base.query;\n url.fragment = '';\n url.cannotBeABaseURL = true;\n state = FRAGMENT;\n break;\n }\n state = base.scheme == 'file' ? FILE : RELATIVE;\n continue;\n\n case SPECIAL_RELATIVE_OR_AUTHORITY:\n if (char == '/' && codePoints[pointer + 1] == '/') {\n state = SPECIAL_AUTHORITY_IGNORE_SLASHES;\n pointer++;\n } else {\n state = RELATIVE;\n continue;\n } break;\n\n case PATH_OR_AUTHORITY:\n if (char == '/') {\n state = AUTHORITY;\n break;\n } else {\n state = PATH;\n continue;\n }\n\n case RELATIVE:\n url.scheme = base.scheme;\n if (char == EOF) {\n url.username = base.username;\n url.password = base.password;\n url.host = base.host;\n url.port = base.port;\n url.path = base.path.slice();\n url.query = base.query;\n } else if (char == '/' || (char == '\\\\' && isSpecial(url))) {\n state = RELATIVE_SLASH;\n } else if (char == '?') {\n url.username = base.username;\n url.password = base.password;\n url.host = base.host;\n url.port = base.port;\n url.path = base.path.slice();\n url.query = '';\n state = QUERY;\n } else if (char == '#') {\n url.username = base.username;\n url.password = base.password;\n url.host = base.host;\n url.port = base.port;\n url.path = base.path.slice();\n url.query = base.query;\n url.fragment = '';\n state = FRAGMENT;\n } else {\n url.username = base.username;\n url.password = base.password;\n url.host = base.host;\n url.port = base.port;\n url.path = base.path.slice();\n url.path.pop();\n state = PATH;\n continue;\n } break;\n\n case RELATIVE_SLASH:\n if (isSpecial(url) && (char == '/' || char == '\\\\')) {\n state = SPECIAL_AUTHORITY_IGNORE_SLASHES;\n } else if (char == '/') {\n state = AUTHORITY;\n } else {\n url.username = base.username;\n url.password = base.password;\n url.host = base.host;\n url.port = base.port;\n state = PATH;\n continue;\n } break;\n\n case SPECIAL_AUTHORITY_SLASHES:\n state = SPECIAL_AUTHORITY_IGNORE_SLASHES;\n if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;\n pointer++;\n break;\n\n case SPECIAL_AUTHORITY_IGNORE_SLASHES:\n if (char != '/' && char != '\\\\') {\n state = AUTHORITY;\n continue;\n } break;\n\n case AUTHORITY:\n if (char == '@') {\n if (seenAt) buffer = '%40' + buffer;\n seenAt = true;\n bufferCodePoints = arrayFrom(buffer);\n for (var i = 0; i < bufferCodePoints.length; i++) {\n var codePoint = bufferCodePoints[i];\n if (codePoint == ':' && !seenPasswordToken) {\n seenPasswordToken = true;\n continue;\n }\n var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);\n if (seenPasswordToken) url.password += encodedCodePoints;\n else url.username += encodedCodePoints;\n }\n buffer = '';\n } else if (\n char == EOF || char == '/' || char == '?' || char == '#' ||\n (char == '\\\\' && isSpecial(url))\n ) {\n if (seenAt && buffer == '') return INVALID_AUTHORITY;\n pointer -= arrayFrom(buffer).length + 1;\n buffer = '';\n state = HOST;\n } else buffer += char;\n break;\n\n case HOST:\n case HOSTNAME:\n if (stateOverride && url.scheme == 'file') {\n state = FILE_HOST;\n continue;\n } else if (char == ':' && !seenBracket) {\n if (buffer == '') return INVALID_HOST;\n failure = parseHost(url, buffer);\n if (failure) return failure;\n buffer = '';\n state = PORT;\n if (stateOverride == HOSTNAME) return;\n } else if (\n char == EOF || char == '/' || char == '?' || char == '#' ||\n (char == '\\\\' && isSpecial(url))\n ) {\n if (isSpecial(url) && buffer == '') return INVALID_HOST;\n if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;\n failure = parseHost(url, buffer);\n if (failure) return failure;\n buffer = '';\n state = PATH_START;\n if (stateOverride) return;\n continue;\n } else {\n if (char == '[') seenBracket = true;\n else if (char == ']') seenBracket = false;\n buffer += char;\n } break;\n\n case PORT:\n if (DIGIT.test(char)) {\n buffer += char;\n } else if (\n char == EOF || char == '/' || char == '?' || char == '#' ||\n (char == '\\\\' && isSpecial(url)) ||\n stateOverride\n ) {\n if (buffer != '') {\n var port = parseInt(buffer, 10);\n if (port > 0xFFFF) return INVALID_PORT;\n url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;\n buffer = '';\n }\n if (stateOverride) return;\n state = PATH_START;\n continue;\n } else return INVALID_PORT;\n break;\n\n case FILE:\n url.scheme = 'file';\n if (char == '/' || char == '\\\\') state = FILE_SLASH;\n else if (base && base.scheme == 'file') {\n if (char == EOF) {\n url.host = base.host;\n url.path = base.path.slice();\n url.query = base.query;\n } else if (char == '?') {\n url.host = base.host;\n url.path = base.path.slice();\n url.query = '';\n state = QUERY;\n } else if (char == '#') {\n url.host = base.host;\n url.path = base.path.slice();\n url.query = base.query;\n url.fragment = '';\n state = FRAGMENT;\n } else {\n if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {\n url.host = base.host;\n url.path = base.path.slice();\n shortenURLsPath(url);\n }\n state = PATH;\n continue;\n }\n } else {\n state = PATH;\n continue;\n } break;\n\n case FILE_SLASH:\n if (char == '/' || char == '\\\\') {\n state = FILE_HOST;\n break;\n }\n if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {\n if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);\n else url.host = base.host;\n }\n state = PATH;\n continue;\n\n case FILE_HOST:\n if (char == EOF || char == '/' || char == '\\\\' || char == '?' || char == '#') {\n if (!stateOverride && isWindowsDriveLetter(buffer)) {\n state = PATH;\n } else if (buffer == '') {\n url.host = '';\n if (stateOverride) return;\n state = PATH_START;\n } else {\n failure = parseHost(url, buffer);\n if (failure) return failure;\n if (url.host == 'localhost') url.host = '';\n if (stateOverride) return;\n buffer = '';\n state = PATH_START;\n } continue;\n } else buffer += char;\n break;\n\n case PATH_START:\n if (isSpecial(url)) {\n state = PATH;\n if (char != '/' && char != '\\\\') continue;\n } else if (!stateOverride && char == '?') {\n url.query = '';\n state = QUERY;\n } else if (!stateOverride && char == '#') {\n url.fragment = '';\n state = FRAGMENT;\n } else if (char != EOF) {\n state = PATH;\n if (char != '/') continue;\n } break;\n\n case PATH:\n if (\n char == EOF || char == '/' ||\n (char == '\\\\' && isSpecial(url)) ||\n (!stateOverride && (char == '?' || char == '#'))\n ) {\n if (isDoubleDot(buffer)) {\n shortenURLsPath(url);\n if (char != '/' && !(char == '\\\\' && isSpecial(url))) {\n url.path.push('');\n }\n } else if (isSingleDot(buffer)) {\n if (char != '/' && !(char == '\\\\' && isSpecial(url))) {\n url.path.push('');\n }\n } else {\n if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {\n if (url.host) url.host = '';\n buffer = buffer.charAt(0) + ':'; // normalize windows drive letter\n }\n url.path.push(buffer);\n }\n buffer = '';\n if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {\n while (url.path.length > 1 && url.path[0] === '') {\n url.path.shift();\n }\n }\n if (char == '?') {\n url.query = '';\n state = QUERY;\n } else if (char == '#') {\n url.fragment = '';\n state = FRAGMENT;\n }\n } else {\n buffer += percentEncode(char, pathPercentEncodeSet);\n } break;\n\n case CANNOT_BE_A_BASE_URL_PATH:\n if (char == '?') {\n url.query = '';\n state = QUERY;\n } else if (char == '#') {\n url.fragment = '';\n state = FRAGMENT;\n } else if (char != EOF) {\n url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);\n } break;\n\n case QUERY:\n if (!stateOverride && char == '#') {\n url.fragment = '';\n state = FRAGMENT;\n } else if (char != EOF) {\n if (char == \"'\" && isSpecial(url)) url.query += '%27';\n else if (char == '#') url.query += '%23';\n else url.query += percentEncode(char, C0ControlPercentEncodeSet);\n } break;\n\n case FRAGMENT:\n if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);\n break;\n }\n\n pointer++;\n }\n};\n\n// `URL` constructor\n// https://url.spec.whatwg.org/#url-class\nvar URLConstructor = function URL(url /* , base */) {\n var that = anInstance(this, URLConstructor, 'URL');\n var base = arguments.length > 1 ? arguments[1] : undefined;\n var urlString = String(url);\n var state = setInternalState(that, { type: 'URL' });\n var baseState, failure;\n if (base !== undefined) {\n if (base instanceof URLConstructor) baseState = getInternalURLState(base);\n else {\n failure = parseURL(baseState = {}, String(base));\n if (failure) throw TypeError(failure);\n }\n }\n failure = parseURL(state, urlString, null, baseState);\n if (failure) throw TypeError(failure);\n var searchParams = state.searchParams = new URLSearchParams();\n var searchParamsState = getInternalSearchParamsState(searchParams);\n searchParamsState.updateSearchParams(state.query);\n searchParamsState.updateURL = function () {\n state.query = String(searchParams) || null;\n };\n if (!DESCRIPTORS) {\n that.href = serializeURL.call(that);\n that.origin = getOrigin.call(that);\n that.protocol = getProtocol.call(that);\n that.username = getUsername.call(that);\n that.password = getPassword.call(that);\n that.host = getHost.call(that);\n that.hostname = getHostname.call(that);\n that.port = getPort.call(that);\n that.pathname = getPathname.call(that);\n that.search = getSearch.call(that);\n that.searchParams = getSearchParams.call(that);\n that.hash = getHash.call(that);\n }\n};\n\nvar URLPrototype = URLConstructor.prototype;\n\nvar serializeURL = function () {\n var url = getInternalURLState(this);\n var scheme = url.scheme;\n var username = url.username;\n var password = url.password;\n var host = url.host;\n var port = url.port;\n var path = url.path;\n var query = url.query;\n var fragment = url.fragment;\n var output = scheme + ':';\n if (host !== null) {\n output += '//';\n if (includesCredentials(url)) {\n output += username + (password ? ':' + password : '') + '@';\n }\n output += serializeHost(host);\n if (port !== null) output += ':' + port;\n } else if (scheme == 'file') output += '//';\n output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';\n if (query !== null) output += '?' + query;\n if (fragment !== null) output += '#' + fragment;\n return output;\n};\n\nvar getOrigin = function () {\n var url = getInternalURLState(this);\n var scheme = url.scheme;\n var port = url.port;\n if (scheme == 'blob') try {\n return new URL(scheme.path[0]).origin;\n } catch (error) {\n return 'null';\n }\n if (scheme == 'file' || !isSpecial(url)) return 'null';\n return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');\n};\n\nvar getProtocol = function () {\n return getInternalURLState(this).scheme + ':';\n};\n\nvar getUsername = function () {\n return getInternalURLState(this).username;\n};\n\nvar getPassword = function () {\n return getInternalURLState(this).password;\n};\n\nvar getHost = function () {\n var url = getInternalURLState(this);\n var host = url.host;\n var port = url.port;\n return host === null ? ''\n : port === null ? serializeHost(host)\n : serializeHost(host) + ':' + port;\n};\n\nvar getHostname = function () {\n var host = getInternalURLState(this).host;\n return host === null ? '' : serializeHost(host);\n};\n\nvar getPort = function () {\n var port = getInternalURLState(this).port;\n return port === null ? '' : String(port);\n};\n\nvar getPathname = function () {\n var url = getInternalURLState(this);\n var path = url.path;\n return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';\n};\n\nvar getSearch = function () {\n var query = getInternalURLState(this).query;\n return query ? '?' + query : '';\n};\n\nvar getSearchParams = function () {\n return getInternalURLState(this).searchParams;\n};\n\nvar getHash = function () {\n var fragment = getInternalURLState(this).fragment;\n return fragment ? '#' + fragment : '';\n};\n\nvar accessorDescriptor = function (getter, setter) {\n return { get: getter, set: setter, configurable: true, enumerable: true };\n};\n\nif (DESCRIPTORS) {\n defineProperties(URLPrototype, {\n // `URL.prototype.href` accessors pair\n // https://url.spec.whatwg.org/#dom-url-href\n href: accessorDescriptor(serializeURL, function (href) {\n var url = getInternalURLState(this);\n var urlString = String(href);\n var failure = parseURL(url, urlString);\n if (failure) throw TypeError(failure);\n getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);\n }),\n // `URL.prototype.origin` getter\n // https://url.spec.whatwg.org/#dom-url-origin\n origin: accessorDescriptor(getOrigin),\n // `URL.prototype.protocol` accessors pair\n // https://url.spec.whatwg.org/#dom-url-protocol\n protocol: accessorDescriptor(getProtocol, function (protocol) {\n var url = getInternalURLState(this);\n parseURL(url, String(protocol) + ':', SCHEME_START);\n }),\n // `URL.prototype.username` accessors pair\n // https://url.spec.whatwg.org/#dom-url-username\n username: accessorDescriptor(getUsername, function (username) {\n var url = getInternalURLState(this);\n var codePoints = arrayFrom(String(username));\n if (cannotHaveUsernamePasswordPort(url)) return;\n url.username = '';\n for (var i = 0; i < codePoints.length; i++) {\n url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);\n }\n }),\n // `URL.prototype.password` accessors pair\n // https://url.spec.whatwg.org/#dom-url-password\n password: accessorDescriptor(getPassword, function (password) {\n var url = getInternalURLState(this);\n var codePoints = arrayFrom(String(password));\n if (cannotHaveUsernamePasswordPort(url)) return;\n url.password = '';\n for (var i = 0; i < codePoints.length; i++) {\n url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);\n }\n }),\n // `URL.prototype.host` accessors pair\n // https://url.spec.whatwg.org/#dom-url-host\n host: accessorDescriptor(getHost, function (host) {\n var url = getInternalURLState(this);\n if (url.cannotBeABaseURL) return;\n parseURL(url, String(host), HOST);\n }),\n // `URL.prototype.hostname` accessors pair\n // https://url.spec.whatwg.org/#dom-url-hostname\n hostname: accessorDescriptor(getHostname, function (hostname) {\n var url = getInternalURLState(this);\n if (url.cannotBeABaseURL) return;\n parseURL(url, String(hostname), HOSTNAME);\n }),\n // `URL.prototype.port` accessors pair\n // https://url.spec.whatwg.org/#dom-url-port\n port: accessorDescriptor(getPort, function (port) {\n var url = getInternalURLState(this);\n if (cannotHaveUsernamePasswordPort(url)) return;\n port = String(port);\n if (port == '') url.port = null;\n else parseURL(url, port, PORT);\n }),\n // `URL.prototype.pathname` accessors pair\n // https://url.spec.whatwg.org/#dom-url-pathname\n pathname: accessorDescriptor(getPathname, function (pathname) {\n var url = getInternalURLState(this);\n if (url.cannotBeABaseURL) return;\n url.path = [];\n parseURL(url, pathname + '', PATH_START);\n }),\n // `URL.prototype.search` accessors pair\n // https://url.spec.whatwg.org/#dom-url-search\n search: accessorDescriptor(getSearch, function (search) {\n var url = getInternalURLState(this);\n search = String(search);\n if (search == '') {\n url.query = null;\n } else {\n if ('?' == search.charAt(0)) search = search.slice(1);\n url.query = '';\n parseURL(url, search, QUERY);\n }\n getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);\n }),\n // `URL.prototype.searchParams` getter\n // https://url.spec.whatwg.org/#dom-url-searchparams\n searchParams: accessorDescriptor(getSearchParams),\n // `URL.prototype.hash` accessors pair\n // https://url.spec.whatwg.org/#dom-url-hash\n hash: accessorDescriptor(getHash, function (hash) {\n var url = getInternalURLState(this);\n hash = String(hash);\n if (hash == '') {\n url.fragment = null;\n return;\n }\n if ('#' == hash.charAt(0)) hash = hash.slice(1);\n url.fragment = '';\n parseURL(url, hash, FRAGMENT);\n })\n });\n}\n\n// `URL.prototype.toJSON` method\n// https://url.spec.whatwg.org/#dom-url-tojson\nredefine(URLPrototype, 'toJSON', function toJSON() {\n return serializeURL.call(this);\n}, { enumerable: true });\n\n// `URL.prototype.toString` method\n// https://url.spec.whatwg.org/#URL-stringification-behavior\nredefine(URLPrototype, 'toString', function toString() {\n return serializeURL.call(this);\n}, { enumerable: true });\n\nif (NativeURL) {\n var nativeCreateObjectURL = NativeURL.createObjectURL;\n var nativeRevokeObjectURL = NativeURL.revokeObjectURL;\n // `URL.createObjectURL` method\n // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL\n // eslint-disable-next-line no-unused-vars\n if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {\n return nativeCreateObjectURL.apply(NativeURL, arguments);\n });\n // `URL.revokeObjectURL` method\n // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL\n // eslint-disable-next-line no-unused-vars\n if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {\n return nativeRevokeObjectURL.apply(NativeURL, arguments);\n });\n}\n\nsetToStringTag(URLConstructor, 'URL');\n\n$({ global: true, forced: !USE_NATIVE_URL, sham: !DESCRIPTORS }, {\n URL: URLConstructor\n});\n","import CredentialMode from \"../CredentialMode\";\n\nconst DATA_URL_PATTERN = new RegExp(\"^data:\");\nconst ABSOLUTE_URL_PATTERN = new RegExp(\"^https?://\");\n\n\n/**\n * @summary Utility Class for DOM\n * @private\n * @memberof mapray\n */\nclass Dom {\n\n /**\n * @param {number} width\n * @param {number} height\n * @return {CanvasRenderingContext2D}\n */\n static createCanvasContext( width, height )\n {\n var canvas = document.createElement( \"canvas\" );\n canvas.width = width;\n canvas.height = height;\n return canvas.getContext( \"2d\" );\n }\n\n /**\n * @summary 画像を読み込みます。\n * @param {string|Blob} src\n * @Param {object} options\n * @param {mapray.CredentialMode} [options.credentials=mapray.CredentialMode.SAME_ORIGIN]\n */\n static async loadImage( src, options={} )\n {\n return await new Promise( (resolve, reject) => {\n const image = new Image();\n image.onload = event => resolve( event.target );\n image.onerror = event => reject( new Error(\"Failed to load image\") );\n if ( options.credentials !== CredentialMode.OMIT ) {\n image.crossOrigin = (\n options.credentials === CredentialMode.INCLUDE ? \"use-credentials\" : \"anonymous\"\n );\n }\n image.src = src instanceof Blob ? URL.createObjectURL( src ) : src;\n } );\n }\n\n /**\n * @summary 画像が読み込まれるまで待ちます。\n * @param {HTMLImageElement} image\n */\n static async waitForLoad( image )\n {\n if ( !image.src ) throw new Error( \"src was not set\" );\n if ( image.complete ) return image;\n\n return await new Promise( (resolve, reject) => {\n const prevOnLoad = image.onload;\n const prevOnError = image.onerror;\n image.onload = event => {\n if ( prevOnLoad ) prevOnLoad( event );\n resolve( event.target );\n };\n image.onerror = event => {\n if ( prevOnError ) prevOnError( event );\n reject( new Error(\"Failed to load image\") );\n };\n } );\n }\n\n static resolveUrl( baseUrl, url ) {\n if ( DATA_URL_PATTERN.test( url ) || ABSOLUTE_URL_PATTERN.test( url ) ) {\n // url がデータ url または絶対 url のときは\n // そのまま url をリクエスト\n return url;\n }\n else {\n // それ以外のときは url を相対 url と解釈し\n // 基底 url と結合した url をリクエスト\n return baseUrl + url;\n }\n }\n\n}\n\n\nDom.SYSTEM_FONT_FAMILY = \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'\";\n\n\nexport default Dom;\n","import HTTP from \"./HTTP\";\nimport Dom from \"./util/Dom\";\nimport CredentialMode from \"./CredentialMode\";\n\n\n\n/**\n * @classdesc リソースクラス\n * URLやDB、クラウドサービス等、各種リソースへのアクセスを同一インターフェースで提供することを目的とした抽象クラスです。\n * 基本機能:\n * ・コンストラクタ等によりあらかじめURLやデータの位置を示すプロパティを設定\n * ・load()によりリソースを読み込む\n * ・loadSubResource()によりサブリソースを読み込む\n *\n * @memberof mapray\n */\nclass Resource {\n\n /**\n * リソースを読み込みます。\n */\n async load( options={} ) {\n throw new Error( \"Not Implemented\" );\n }\n\n /**\n * @summary リソースの読み込みをキャンセルできる場合はキャンセルします。\n */\n cancel() {\n }\n\n /**\n * @summary サブリソースをサポートするかを返します。\n * @return {boolean}\n */\n loadSubResourceSupported() {\n return false;\n }\n\n /**\n * @summary サブリソースを読み込みます。\n * @param {string} url URL\n * @param {object} options\n * @param {mapray.Resource.ResourceType} [options.type] 返却するタイプを指定します。\n * @return {object} options.type に応じた型で返却されます。\n */\n async loadSubResource( url, options={} ) {\n throw new Error( \"Not Supported\" );\n }\n\n /**\n * @summary 関連リソースをサポートするかを返します。\n * @return {boolean}\n */\n resolveResourceSupported() {\n return false;\n }\n\n /**\n * @summary 関連リソースを読み込みます。\n * @return {boolean}\n */\n async resolveResource( url ) {\n throw new Error( \"Not Supported\" );\n }\n\n /**\n * @summary リソース型\n */\n static get ResourceType() { return ResourceType; }\n}\n\n\n\n/**\n * @summary リソースの種類\n * @enum {object}\n * @memberof mapray.ResourceType\n * @constant\n */\nconst ResourceType = {\n\n /**\n * JSON\n */\n JSON: { id: \"JSON\" },\n\n /**\n * バイナリ(ArrayBuffer)\n */\n BINARY: { id: \"BINARY\" },\n\n /**\n * 画像(Image)\n */\n IMAGE: { id: \"IMAGE\" }\n\n};\n\n\n\n/**\n * @classdesc URLリソースです。\n */\nclass URLResource extends Resource {\n\n /**\n * @param {string} url\n * @param {object} [options]\n * @param {mapray.Resource.ResourceType} [options.type]\n * @param {mapray.Transform} [options.transform]\n */\n constructor( url, options={} ) {\n super();\n this._url = url;\n const index = url.lastIndexOf( '/' );\n if ( index === -1 ) throw new Error( \"invalid url\" );\n this._base_url = this._url.substr( 0, index + 1 );\n this._type = options.type || \"json\";\n this._transform = options.transform || defaultTransformCallback;\n this._abort_ctrl = new AbortController();\n }\n\n /**\n * @summary リソースのurl\n * @type {string}\n */\n get url() {\n return this._url;\n }\n\n /**\n * @summary このリソースを読み込みます。\n * @param {object} [options]\n */\n async load( options={} ) {\n return await this._loadURLResource( this._url, options.type || this._type, options );\n }\n\n /**\n * @summary リソースの読み込みをキャンセルします。\n */\n cancel() {\n this._abort_ctrl.abort();\n }\n\n /**\n * @summary このクラスでのデフォルト実装では、trueを返却します。\n * @return {boolean}\n */\n loadSubResourceSupported() {\n return true;\n }\n\n /**\n * @summary サブリソースを読み込みます。\n * @param {string} subUrl URL\n * @param {object} options\n * @param {mapray.Resource.ResourceType} [options.type] 返却するタイプを指定します。\n * @return {object} options.type に応じた型で返却されます。\n */\n async loadSubResource( subUrl, options={} ) {\n return await this._loadURLResource( Dom.resolveUrl( this._base_url, subUrl ), options.type, options );\n }\n\n /**\n * @summary 関連リソースをサポートするかを返します。\n * @return {boolean}\n */\n resolveResourceSupported() {\n return true;\n }\n\n /**\n * @summary 関連リソースを読み込みます。\n * @param {string} url \n * @return {Resource}\n */\n resolveResource( sub_url ) {\n const url = Dom.resolveUrl( this._base_url, sub_url );\n return new URLResource( url, {\n transform: this._transform\n });\n }\n\n\n /**\n * @param {string} url\n * @param {mapray.Resource.ResourceType} [type]\n * @private\n */\n async _loadURLResource( url, type, options={} ) {\n const tr = this._transform( url, type );\n if ( type === ResourceType.IMAGE ) {\n return await Dom.loadImage( tr.url, tr );\n }\n const http_option = this._make_fetch_params( tr ) || {};\n if ( options.signal ) http_option.signal = options.signal;\n\n const response = await HTTP.get( tr.url, null, http_option );\n if ( !response.ok ) throw new Error( response.statusText );\n\n return (\n type === ResourceType.JSON ? await response.json():\n type === ResourceType.BINARY ? await response.arrayBuffer():\n response\n );\n }\n\n /**\n * fetch() の init 引数に与えるオブジェクトを生成\n * @private\n */\n _make_fetch_params( tr ) {\n var init = {\n signal: this._abort_ctrl.signal,\n credentials: (tr.credentials || CredentialMode.OMIT).credentials\n };\n\n if ( tr.headers ) {\n init.headers = tr.headers;\n }\n\n return init;\n }\n}\n\n\n\nfunction defaultTransformCallback( url, type ) {\n return { url: url };\n}\n\n\n\nexport { URLResource, ResourceType };\nexport default Resource;\n","import Resource, { URLResource } from \"./Resource\";\n\n\n/**\n * @summary ローダークラス\n * @memberof mapray\n */\nclass Loader {\n\n /**\n * @param {mapray.Scene} scene 読み込み先のシーン\n * @param {mapray.Resource} resource \n * @param {object} [options={}]\n * @param {object} [options.onLoad] 全ての読み込み完了時に呼ばれる\n * @param {mapray.Loader.EntityCallback} [options.onEntity] エンティティが読み込まれるたびに呼ばれる\n */\n constructor( scene, resource, options={} ) {\n this._scene = scene;\n if (!(resource instanceof Resource)) {\n throw new Error(\"Unsupported Resource Type: \" + resource);\n }\n this._resource = resource;\n this._status = Loader.Status.NOT_LOADED;\n this._onLoad = options.onLoad || defaultOnLoadCallback;\n this._onEntity = options.onEntity || defaultOnEntityCallback;\n }\n\n\n /**\n * @summary 読み込み先のシーン\n * @type {mapray.Scene}\n * @readonly\n */\n get scene() { return this._scene; }\n\n\n /**\n * @summary シーンリソース\n * @type {string}\n * @readonly\n */\n get resource() { return this._resource; }\n\n\n /**\n * ローダー読み込みの状態\n * @readonly\n */\n get status() { return this._status; }\n\n\n /**\n * @private\n */\n _setStatus( status ) {\n this._status = status;\n }\n\n\n /**\n * @summary 読み込みを実行します。\n * @returns {Promise}\n */\n load()\n {\n if ( this.status !== Loader.Status.NOT_LOADED ) {\n return Promise.reject( new Error( \"Illegal Status: \" + this.status ) );\n }\n\n return (\n Promise.resolve()\n .then( () => {\n this._setStatus( Loader.Status.LOADING );\n this.scene.addLoader( this );\n return this._load();\n } )\n .catch( error => {\n // JSON データの取得に失敗 (キャンセルによる失敗の可能性あり)\n console.log( error );\n this._scene.removeLoader( this );\n this._onLoad( this, false );\n if ( this._status !== Loader.Status.CANCELED ) {\n this._setStatus( Loader.Status.ABORTED );\n }\n throw error;\n } )\n .then( value => {\n this._scene.removeLoader( this );\n if ( this._status === Loader.Status.CANCELED ) {\n this._onLoad( this, false );\n throw new Error( \"canceled\" );\n }\n else {\n this._setStatus( Loader.Status.LOADED );\n this._onLoad( this, true );\n return value;\n }\n } )\n );\n }\n\n\n /**\n * @summary 読み込み処理の実態。継承クラスによって実装される。\n * @private\n */\n _load() {\n throw new Error( \"_load() is not implemented in \" + this.constructor.name );\n }\n\n\n /**\n * @summary 読み込みの取り消し\n * @desc\n * 終了コールバック関数は isSuccess == false で呼び出される。
\n */\n cancel()\n {\n if ( this._status === Loader.Status.LOADING || this._status === Loader.Status.LOADED ) {\n this._setStatus( Loader.Status.CANCELED );\n this._resource.cancel();\n this._cancel();\n // this._scene.removeLoader( this );\n // this._onLoad( this, false );\n }\n }\n\n\n /**\n * @summary キャンセル時に行う処理。継承クラスによって実装される。\n * @private\n */\n _cancel()\n {\n }\n\n\n /**\n * 取り消し状態のとき例外を投げる\n * @private\n */\n _check_cancel()\n {\n if ( this.status === Loader.Status.CANCELED ) {\n throw new Error( \"canceled\" );\n }\n }\n}\n\n\n/**\n * @summary Entity読み込みコールバック\n * @callback EntityCallback\n * @desc\n * 読み込み処理の中でEntityが生成される際に呼ばれる。\n * 一度の読み込み(load()呼び出し)において複数のエンティティが生成される場合は、エンティティが生成されるたびに呼ばれる。\n * この関数をLoaderに指定する場合は、callback処理の中でEntityをsceneへ追加する必要がある。\n * geojsonのように、要素ごとにプロパティを含められるような場合は、propにより値にアクセスする。\n *
\n *\n * @param {mapray.Loader} loader Loader\n * @param {mapray.Entity} entity 読み込まれたEntity\n * @param {object} prop エンティティ生成の元となるオブジェクト\n *\n * @example\n * const loader = new mapray.SceneLoader( viewer.scene, resource, {\n * onEntity: ( loader, entity, prop ) => {\n * entity.setScale( [ 2, 2, 2 ] );\n * loader.scene.addEntity( entity );\n * }\n * } );\n * loader.load();\n * \n * @memberof mapray.Loader\n */\n\n\n\nLoader.Status = {\n NOT_LOADED : \"Not Loaded\",\n LOADING : \"Loading\",\n LOADED : \"Loaded\",\n CANCELED : \"Canceled\",\n ERROR : \"ERROR\"\n};\n\nfunction defaultOnLoadCallback( loader, isSuccess )\n{\n}\n\nfunction defaultOnEntityCallback( loader, entity )\n{\n loader.scene.addEntity( entity );\n}\n\n\n/**\n * @summary リソース要求変換関数\n * @callback TransformCallback\n * @desc\n * リソースのリクエスト時に URL などを変換する関数の型である。
\n *\n * @param {string} url 変換前のリソース URL\n * @param {mapray.Resource.ResourceType} type リソースの種類\n * @return {mapray.Loader.TransformResult} 変換結果を表すオブジェクト\n *\n * @example\n * function( url, type ) {\n * return {\n * url: url,\n * credentials: mapray.CredentialMode.SAME_ORIGIN,\n * headers: {\n * 'Header-Name': 'Header-Value'\n * }\n * };\n * }\n *\n * @memberof mapray.Loader\n */\n\n\n/**\n * @summary リソース要求変換関数の変換結果\n * @typedef {object} TransformResult\n * @desc\n * 関数型 {@link mapray.Loader.TransformCallback} の戻り値のオブジェクト構造である。
\n * @property {string} url 変換後のリソース URL\n * @property {mapray.CredentialMode} [credentials=SAME_ORIGIN] クレデンシャルモード\n * @property {object} [headers={}] リクエストに追加するヘッダーの辞書 (キーがヘッダー名、値がヘッダー値)\n * @memberof mapray.Loader\n */\n\n\nexport default Loader;\n","// このようにする理由は GeoMath.js の最後を参照\nimport { Orientation } from \"./GeoMath\";\nexport default Orientation;\n","/**\n * @summary glTF オブジェクトの共通データ\n *\n * @classdesc\n * 多くの glTF オブジェクトに共通に存在する、次のプロパティの値を取得する。
\n *
\n * - name\n * - extensions\n * - extras\n *
\n *\n * @memberof mapray.gltf\n * @private\n */\nclass CommonData {\n\n /**\n * @param {object} json JSON オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n */\n constructor( json, ctx )\n {\n // specification/2.0/schema/glTFChildOfRootProperty.schema.json\n\n this._name = json.name || null;\n this._extensions = ctx.extractUsedExtensions( json.extensions || {} );\n this._extras = json.extras || {};\n }\n\n\n /**\n * @summary オブジェクト名を取得\n *\n * @return {?string} オブジェクト名\n */\n getName()\n {\n return this._name;\n }\n\n\n /**\n * @summary 拡張機能固有オブジェクトを取得\n *\n * @param {string} id 拡張機能の識別子\n *\n * @return {?object} 拡張機能固有オブジェクト\n */\n getExtensions( id )\n {\n const extension = this._extensions[id];\n\n return (extension !== undefined) ? extension : null;\n }\n\n\n /**\n * @summary アプリケーション固有データを取得\n *\n * @param {string} id アプリケーション固有データの識別子\n *\n * @return {?object} アプリケーション固有データ\n */\n getExtras( id )\n {\n const extra = this._extras[id];\n\n return (extra !== undefined) ? extra : null;\n }\n\n}\n\n\nexport default CommonData;\n","import CommonData from \"./CommonData\";\n\n\n/**\n * 読み込んだ glTF データの内容\n *\n * @memberof mapray.gltf\n * @private\n */\nclass Content {\n\n /**\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {mapray.gltf.Scene[]} scenes シーンの配列\n * @param {number} default_scene_index 既定シーンの索引 (既定シーンがないときは -1)\n */\n constructor( ctx, scenes, default_scene_index )\n {\n this._commonData = new CommonData( ctx.gjson, ctx );\n\n this._scenes = scenes;\n this._default_scene_index = default_scene_index;\n }\n\n\n /**\n * glTF オブジェクトの共通データ\n *\n * @type {mapray.gltf.CommonData}\n * @readonly\n */\n get commonData() { return this._commonData; }\n\n\n /**\n * @summary シーンの配列\n *\n * @type {mapray.gltf.Scene[]}\n * @readonly\n */\n get scenes()\n {\n return this._scenes;\n }\n\n\n /**\n * @summary 既定シーンの索引\n *\n * 既定シーンの索引を返す。ただし既定シーンがないときは -1 を返す。
\n *\n * @type {number}\n * @readonly\n */\n get default_scene_index()\n {\n return this._default_scene_index;\n }\n\n}\n\n\nexport default Content;\n","/**\n * @summary モデルテクスチャ\n * @memberof mapray\n * @package\n */\nclass Texture {\n\n /**\n * オプション mag_filter, min_filter, wrap_s, wrap_t は WebGL の定数と同じ値を指定する。\n * これらのうち、指定がなかったオプションは usage オプションにより決定される。
\n *\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {?(HTMLImageElement|HTMLCanvasElement)} image 元画像 (usage=COLOR のときは null)\n * @param {object} [options] オプション集合\n * @param {mapray.Texture.Usage} [options.usage=GENERAL] テクスチャ用途\n * @param {number} [options.mag_filter] 拡大フィルタ (NEAREST | LINEAR)\n * @param {number} [options.min_filter] 縮小フィルタ (NEAREST | LINEAR | NEAREST_MIPMAP_NEAREST |\n * LINEAR_MIPMAP_NEAREST | NEAREST_MIPMAP_LINEAR | LINEAR_MIPMAP_LINEAR)\n * @param {number} [options.wrap_s] S Wrap (CLAMP_TO_EDGE | MIRRORED_REPEAT | REPEAT)\n * @param {number} [options.wrap_t] T Wrap (CLAMP_TO_EDGE | MIRRORED_REPEAT | REPEAT)\n * @param {boolean} [options.flip_y=true] 画像読み込み時に上下を反転するか?\n * @param {mapray.Vector4} [options.color=[1,1,1,1]] usage=COLOR のときの色指定\n */\n constructor( glenv, image, options )\n {\n var opts = options || {};\n this._glenv = glenv;\n this._handle = this._createTexture( image, opts );\n }\n\n\n /**\n * @summary テクスチャのハンドル\n * @type {WebGLTexture}\n * @readonly\n */\n get handle() { return this._handle; }\n\n\n /**\n * @summary リソースを破棄\n */\n dispose()\n {\n var gl = this._glenv.context;\n gl.deleteTexture( this._handle );\n this._handle = null;\n }\n\n\n /**\n * WebGL テクスチャオブジェクトを生成\n *\n * @param {?(HTMLImageElement|HTMLCanvasElement)} image 元画像\n * @param {object} opts オプション集合\n * @return {WebGLTexture} WebGL テクスチャオブジェクト\n * @private\n */\n _createTexture( image, opts )\n {\n var gl = this._glenv.context;\n var target = gl.TEXTURE_2D;\n var texture = gl.createTexture();\n var params = Texture._getParameters( gl, opts );\n\n gl.bindTexture( target, texture );\n\n var flip_y = (opts.flip_y !== undefined) ? opts.flip_y : true;\n gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, flip_y );\n\n if ( opts.usage === Texture.Usage.COLOR ) {\n // 均一色テクスチャー\n gl.texImage2D( target, 0, params.format, 1, 1, 0, params.format, params.type,\n Texture._getColorArray( opts ) );\n }\n else {\n // 画像テクスチャー\n gl.texImage2D( target, 0, params.format, params.format, params.type, image );\n }\n\n if ( flip_y ) {\n gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );\n }\n\n if ( Texture._generateMipmapQ( gl, params ) ) {\n gl.generateMipmap( target );\n }\n\n gl.texParameteri( target, gl.TEXTURE_MAG_FILTER, params.mag_filter );\n gl.texParameteri( target, gl.TEXTURE_MIN_FILTER, params.min_filter );\n gl.texParameteri( target, gl.TEXTURE_WRAP_S, params.wrap_s );\n gl.texParameteri( target, gl.TEXTURE_WRAP_T, params.wrap_t );\n\n gl.bindTexture( target, null );\n\n return texture;\n }\n\n\n /**\n * テクスチャの生成パラメータを取得\n *\n * @param {WebGLRenderingContext} gl WebGL コンテキスト\n * @param {object} opts オプション集合\n * @return {object} 生成パラメータ\n * @private\n */\n static\n _getParameters( gl, opts )\n {\n var params = {\n format: gl.RGBA,\n type: gl.UNSIGNED_BYTE,\n mag_filter: gl.LINEAR,\n min_filter: gl.LINEAR_MIPMAP_LINEAR,\n wrap_s: gl.REPEAT,\n wrap_t: gl.REPEAT\n };\n\n if ( opts.usage === Texture.Usage.SIMPLETEXT ) {\n params.format = gl.ALPHA;\n params.min_filter = gl.LINEAR;\n params.wrap_s = gl.CLAMP_TO_EDGE;\n params.wrap_t = gl.CLAMP_TO_EDGE;\n } \n else if ( opts.usage === Texture.Usage.TEXT) {\n params.min_filter = gl.LINEAR;\n params.wrap_s = gl.CLAMP_TO_EDGE;\n params.wrap_t = gl.CLAMP_TO_EDGE;\n } \n else if ( opts.usage === Texture.Usage.COLOR ) {\n params.mag_filter = gl.NEAREST;\n params.min_filter = gl.NEAREST;\n }\n else if ( opts.usage === Texture.Usage.ICON ) {\n params.min_filter = gl.LINEAR;\n params.wrap_s = gl.CLAMP_TO_EDGE;\n params.wrap_t = gl.CLAMP_TO_EDGE;\n }\n\n // オプション指定による上書き\n if (opts.mag_filter !== undefined) {\n params.mag_filter = opts.mag_filter;\n }\n if (opts.min_filter !== undefined) {\n params.min_filter = opts.min_filter;\n }\n if (opts.wrap_s !== undefined) {\n params.wrap_s = opts.wrap_s;\n }\n if (opts.wrap_t !== undefined) {\n params.wrap_t = opts.wrap_t;\n }\n\n return params;\n }\n\n\n /**\n * テクスチャの生成パラメータを取得\n *\n * @param {object} opts オプション集合\n * @return {Uint8Array} 均一色用の画像データ\n * @private\n */\n static\n _getColorArray( opts )\n {\n var color = opts.color || [1, 1, 1, 1];\n var pixels = color.map( value => Math.round( 255*value ) );\n return new Uint8Array( pixels );\n }\n\n\n /**\n * ミップマップを生成するか?\n *\n * @param {WebGLRenderingContext} gl WebGL コンテキスト\n * @param {object} params 生成パラメータ\n * @return {boolean} ミップマップを生成するとき true, それ以外のとき false\n * @private\n */\n static\n _generateMipmapQ( gl, params )\n {\n var filter = params.min_filter;\n return (filter == gl.NEAREST_MIPMAP_NEAREST)\n || (filter == gl.LINEAR_MIPMAP_NEAREST)\n || (filter == gl.NEAREST_MIPMAP_LINEAR)\n || (filter == gl.LINEAR_MIPMAP_LINEAR);\n }\n\n}\n\n\n/**\n * @summary テクスチャの用途\n * @desc\n * {@link mapray.Texture} の構築子で opts.usage パラメータに指定する値の型である。\n * @enum {object}\n * @memberof mapray.Texture\n * @constant\n */\nvar Usage = {\n\n /**\n * 一般用途 (既定値)\n */\n GENERAL: { id: \"GENERAL\" },\n\n /**\n * 均一色\n */\n COLOR: { id: \"COLOR\" },\n\n /**\n * テキスト表示\n */\n TEXT: { id: \"TEXT\" },\n\n /**\n * シンプルテキスト表示\n */\n SIMPLETEXT: { id: \"SIMPLETEXT\" },\n\n /**\n * アイコン\n */\n ICON: { id: \"ICON\" }\n\n};\n\n\n// クラス定数の定義\n{\n Texture.Usage = Usage;\n}\n\n\nexport default Texture;\n","import Material from \"./Material\";\nimport GeoMath from \"./GeoMath\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary エンティティ・マテリアル\n * @classdesc\n * このクラスは、mapray.RenderStage.getRenderTarget()の値により異なる動作をする。\n * \n * - \n * mapray.RenderStage.RenderTarget.SCENEの場合は、通常通り描画を行う。\n * setParametersは、描画に必要な全てのパラメータを設定します。\n *
- mapray.RenderStage.RenderTarget.RIDの場合は、\n * setParametersは、RID描画に必要なパラメータのみ設定します(一般にテクスチャや色情報は除外される)。\n * このクラスでの実装は、setParameters()により、u_ridが設定されるようになっています。\n *
\n * @memberof mapray\n * @extends mapray.Material\n * @private\n */\nclass EntityMaterial extends Material {\n\n /**\n * @param {mapray.GLEnv} glenv WebGL 環境\n * @param {string} vs_code 頂点シェーダのソースコード\n * @param {string} fs_code フラグメントシェーダのソースコード\n */\n constructor( glenv, vs_code, fs_code )\n {\n super( glenv, vs_code, fs_code );\n }\n\n\n /**\n * @summary 背景との混合が必要か?\n * @param {mapray.RenderStage} stage レンダリングステージ\n * @param {mapray.Primitive} primitive プリミティブ\n * @return {boolean} 背景との混合が必要なとき true, それ以外のとき false\n * @default false\n * @abstract\n */\n isTranslucent( stage, primitive )\n {\n return false;\n }\n\n\n /**\n * @summary マテリアルパラメータを設定\n * @desc\n * 事前に material.bindProgram() すること。
\n * @param {mapray.RenderStage} stage レンダリングステージ\n * @param {mapray.Primitive} primitive プリミティブ\n * @abstract\n */\n setParameters( stage, primitive )\n {\n if (stage.getRenderTarget() === RenderTarget.RID) {\n this._setRenderId( primitive.rid );\n }\n }\n\n\n /**\n * @summary u_obj_to_clip 変数を設定\n * @param {mapray.RenderStage} stage レンダリングステージ\n * @param {mapray.Primitive} primitive プリミティブ\n * @protected\n */\n setObjToClip( stage, primitive )\n {\n var obj_to_gocs = primitive.transform;\n var obj_to_clip = EntityMaterial._obj_to_clip;\n\n // obj_to_clip = gocs_to_clip * obj_to_gocs\n GeoMath.mul_GA( stage._gocs_to_clip, obj_to_gocs, obj_to_clip );\n\n this.setMatrix( \"u_obj_to_clip\", obj_to_clip );\n }\n\n\n /**\n * @summary u_obj_to_view 変数を設定\n * @param {mapray.RenderStage} stage レンダリングステージ\n * @param {mapray.Primitive} primitive プリミティブ\n * @protected\n */\n setObjToView( stage, primitive )\n {\n var obj_to_gocs = primitive.transform;\n var obj_to_view = EntityMaterial._obj_to_view;\n\n // obj_to_view = gocs_to_view * obj_to_gocs\n GeoMath.mul_AA( stage._gocs_to_view, obj_to_gocs, obj_to_view );\n\n this.setMatrix( \"u_obj_to_view\", obj_to_view );\n }\n\n /**\n * @private\n */\n _setRenderId( id ) {\n this.setVector4( \"u_rid\", [\n (id >> 12 & 0xF) / 0xF,\n (id >> 8 & 0xF) / 0xF,\n (id >> 4 & 0xF) / 0xF,\n (id & 0xF) / 0xF\n ]);\n }\n}\n\n\n// クラス定数の定義\n{\n EntityMaterial._obj_to_clip = GeoMath.createMatrixf(); // 計算用一時領域\n EntityMaterial._obj_to_view = GeoMath.createMatrixf(); // 計算用一時領域\n}\n\n\nexport default EntityMaterial;\n","import EntityMaterial from \"./EntityMaterial\";\nimport Texture from \"./Texture\";\nimport model_vs_code from \"./shader/model.vert\";\nimport model_fs_code from \"./shader/model.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n/**\n * @summary 基本マテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n */\nclass ModelMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n * @param {object} [options] オプション指定\n * @param {boolean} [options.is_unlit=false] 無照光か?\n */\n constructor( glenv, options = {} )\n {\n const preamble = ModelMaterial._getPreamble( options );\n\n super( glenv,\n preamble + model_vs_code,\n preamble + ( options.ridMaterial ? rid_fs_code : model_fs_code ) );\n\n // 均一色テクスチャ\n this._white_texture = new Texture( glenv, null, { usage: Texture.Usage.COLOR, color: [1, 1, 1, 1] } );\n\n // 不変パラメータを事前設定\n this.bindProgram();\n this.setInteger( \"u_base_image\", ModelMaterial.TEXUNIT_BASE_IMAGE );\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n var pbrMR = props.pbrMetallicRoughness;\n\n // u_obj_to_clip, u_obj_to_view\n this.setObjToClip( stage, primitive );\n this.setObjToView( stage, primitive );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // 基本色係数\n var bcf = pbrMR[\"baseColorFactor\"];\n const u_base_color = (\n stage.getTranslucentMode() ? [ 0.5 * bcf[ 0 ], 0.5 * bcf[ 1 ], 0.5 * bcf[ 2 ], 0.5 * bcf[ 3 ] ] :\n bcf\n );\n this.setVector4( \"u_base_color\", u_base_color );\n\n // ライト逆方向 (視点座標系) と強さ\n this.setVector3( \"u_light_dir\", [0, 0, 1] );\n\n // テクスチャのバインド\n var base_image_texture = this._selectTexture( pbrMR[\"baseColorTexture\"], this._white_texture );\n this.bindTexture2D( ModelMaterial.TEXUNIT_BASE_IMAGE, base_image_texture.handle );\n }\n }\n\n\n /**\n * テクスチャを選択\n * @param {object} texinfo\n * @param {mapray.Texture} alt_texure\n * @return {mapray.Texture}\n * @private\n */\n _selectTexture( texinfo, alt_texure )\n {\n if ( texinfo !== null ) {\n return texinfo.texture;\n }\n else {\n return alt_texure;\n }\n }\n\n\n /**\n * @summary シェーダの前文を取得\n *\n * @param {object} options オプション指定\n * @param {boolean} [options.is_unlit=false] 無照光か?\n *\n * @private\n */\n static\n _getPreamble( options )\n {\n let is_unlit = (options.is_unlit !== undefined) ? options.is_unlit : false;\n\n let lines = [];\n\n // UNLIT マクロの定義\n if ( is_unlit ) {\n lines.push( \"#define UNLIT\" );\n }\n\n // lines を文字列にして返す\n return lines.join( \"\\n\" ) + \"\\n\\n\";\n }\n\n}\n\n\n// クラス定数の定義\n{\n ModelMaterial.TEXUNIT_BASE_IMAGE = 0; // 基本色画像のテクスチャユニット\n}\n\n\nexport default ModelMaterial;\n","/**\n * glTF の sampler に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Sampler {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {object} index サンプラー索引 (非数の場合は既定値サンプラー)\n */\n constructor( ctx, index )\n {\n // specification/2.0/schema/sampler.schema.json\n // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#samplers\n\n var jsampler = (typeof index == 'number') ? ctx.gjson.samplers[index] : {};\n\n this._magFilter = jsampler.magFilter; // フィルタの既定値は実装依存\n this._minFilter = jsampler.minFilter; // ↑↑↑\n this._wrapS = (jsampler.wrapS !== undefined) ? jsampler.wrapS : Sampler.WRAP_DEFAULT;\n this._wrapT = (jsampler.wrapT !== undefined) ? jsampler.wrapT : Sampler.WRAP_DEFAULT;\n }\n\n\n /**\n * 拡大フィルタ\n * @type {number|undefined}\n * @readonly\n */\n get magFilter() { return this._magFilter; }\n\n\n /**\n * 縮小フィルタ\n * @type {number|undefined}\n * @readonly\n */\n get minFilter() { return this._minFilter; }\n\n\n /**\n * S-wrap\n * @type {number}\n * @readonly\n */\n get wrapS() { return this._wrapS; }\n\n\n /**\n * T-wrap\n * @type {number}\n * @readonly\n */\n get wrapT() { return this._wrapT; }\n\n}\n\n\nSampler.WRAP_DEFAULT = 10497; // REPEAT\n\n\nexport default Sampler;\n","import Sampler from \"./Sampler\";\n\n\n/**\n * glTF の texture に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Texture {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {object} index テクスチャ索引\n */\n constructor( ctx, index )\n {\n // specification/2.0/schema/texture.schema.json\n\n var jtexture = ctx.gjson.textures[index];\n\n this._sampler = new Sampler( ctx, jtexture.sampler );\n this._source = ctx.findImage( jtexture.source );\n }\n\n\n /**\n * イメージを取得\n * @type {mapray.gltf.Image}\n * @readonly\n */\n get source() { return this._source; }\n\n\n /**\n * サンプラを取得\n * @type {mapray.gltf.Sampler}\n * @readonly\n */\n get sampler() { return this._sampler; }\n\n}\n\n\nexport default Texture;\n","import Texture from \"./Texture\";\n\n\n/**\n * glTF の textureInfo に対応\n * @memberof mapray.gltf\n * @private\n */\nclass TextureInfo {\n\n /**\n * 初期化\n * @param {object} jtexinfo テクスチャ情報\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n */\n constructor( jtexinfo, ctx )\n {\n // specification/2.0/schema/textureInfo.schema.json\n\n this._texture = new Texture( ctx, jtexinfo.index );\n this._texCoord = (jtexinfo.texCoord !== undefined) ? jtexinfo.texCoord : 0;\n }\n\n\n /**\n * 参照するテクスチャを取得\n * @type {mapray.gltf.Texture}\n */\n get texture() { return this._texture; }\n\n\n /**\n * 参照するテクスチャを設定\n * @type {mapray.gltf.Texture}\n */\n set texture( value ) { this._texture = value; }\n\n\n /**\n * テクスチャ座標のインデックス\n * @type {number}\n * @readonly\n */\n get texCoord() { return this._texCoord; }\n\n}\n\n\nexport default TextureInfo;\n","import TextureInfo from \"./TextureInfo\";\n\n\n/**\n * glTF の normalTextureInfo に対応\n * @memberof mapray.gltf\n * @private\n */\nclass NormalTextureInfo extends TextureInfo {\n\n /**\n * 初期化\n * @param {object} jtexinfo テクスチャ情報\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n */\n constructor( jtexinfo, ctx )\n {\n super( jtexinfo, ctx );\n\n // specification/2.0/schema/material.normalTextureInfo.schema.json\n this._scale = (jtexinfo.scale !== undefined) ? jtexinfo.scale : 1.0;\n }\n\n\n /**\n * 法線スケール\n * @type {number}\n * @readonly\n */\n get texCoord() { return this._scale; }\n\n}\n\n\nexport default NormalTextureInfo;\n","import TextureInfo from \"./TextureInfo\";\n\n\n/**\n * glTF の occlusionTextureInfo に対応\n * @memberof mapray.gltf\n * @private\n */\nclass OcclusionTextureInfo extends TextureInfo {\n\n /**\n * 初期化\n * @param {object} jtexinfo テクスチャ情報\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n */\n constructor( jtexinfo, ctx )\n {\n super( jtexinfo, ctx );\n\n // specification/2.0/schema/material.occlusionTextureInfo.schema.json\n this._strength = (jtexinfo.strength !== undefined) ? jtexinfo.strength : 1.0;\n }\n\n\n /**\n * 遮蔽強度\n * @type {number}\n * @readonly\n */\n get strength() { return this._strength; }\n\n}\n\n\nexport default OcclusionTextureInfo;\n","import Content from \"./gltf/Content\";\nimport GeoMath from \"./GeoMath\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport MeshBuffer from \"./MeshBuffer\";\nimport Texture from \"./Texture\";\nimport ModelMaterial from \"./ModelMaterial\";\nimport NormalTextureInfo from \"./gltf/NormalTextureInfo\";\nimport OcclusionTextureInfo from \"./gltf/OcclusionTextureInfo\";\n\n\n/**\n * @summary エンティティ用のモデルデータを格納\n *\n * @classdesc\n * エンティティが使用するモデルデータを格納するクラスである。
\n *\n * @memberof mapray\n * @private\n * @see mapray.ModelEntity\n */\nclass ModelContainer {\n\n /**\n * @param {mapray.Scene} scene エンティティが所属するシーン\n * @param {mapray.gltf.Content} content 入力モデルデータ\n */\n constructor( scene, content )\n {\n this._entries = []; // 辞書: 整数 -> Entry\n this._name_map = {}; // 辞書: 名前 -> Entry\n this._default = null; // 既定モデルの Entry\n this._offset_transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n\n const share = {};\n\n for ( const gltf_scene of content.scenes ) {\n const entry = new Entry( scene, gltf_scene, share );\n\n this._entries.push( entry );\n\n if ( gltf_scene.name !== null ) {\n this._name_map[gltf_scene.name] = entry;\n }\n }\n\n if ( content.default_scene_index >= 0 ) {\n if ( content.default_scene_index < this._entries.length ) {\n this._default = this._entries[content.default_scene_index];\n }\n else {\n throw new Error( \"default_scene_index is out of range\" );\n }\n }\n else {\n if ( this._entries.length >= 1 ) {\n this._default = this._entries[0];\n }\n }\n }\n\n\n /**\n * @summary 対応可能な glTF 拡張機能の配列を取得\n *\n * @desc\n * 例えば {@link mapray.gltf.Tools.load} の supported_extensions オプションのために使用する。
\n * glTF のコンテンツがこれらの拡張機能だけで対応できないとき、読み込みに失敗することがある。
\n *\n * @return {string[]}\n */\n static\n getSupportedExtensions_glTF()\n {\n return [\"KHR_materials_unlit\"];\n }\n\n\n /**\n * @summary オフセット用の変換行列を設定\n *\n * @param {mapray.Matrix} matrix モデルの頂点座標を変換する変換行列\n */\n setOffsetTransform( matrix )\n {\n GeoMath.copyMatrix( matrix, this._offset_transform );\n }\n\n\n /**\n * @summary モデルデータを生成\n *\n * @desc\n * id で指定したモデルのプリミティブを生成する。ただし id を省略したときは既定のモデルが選択される。
\n * id で指定したモデルが存在しないとき、または id を省略したがモデルがまったく存在しないとき null を返す。
\n *\n * @param {number|string} [id] モデル ID\n * @return {?mapray.Primitive[]} モデルのプリミティブ配列\n */\n createPrimitives( id, options )\n {\n const entry = this._getEntry( id );\n if ( entry === null ) return null;\n\n const primitives = [];\n\n for ( const prim of entry.getPrimitives(options) ) {\n const cloned_prim = prim.fastClone();\n GeoMath.mul_AA( this._offset_transform, cloned_prim.transform, cloned_prim.transform ); // オフセット変換行列を適用\n cloned_prim.properties = Builder.fastCloneProperties( cloned_prim.properties );\n primitives.push( cloned_prim );\n }\n\n return primitives;\n }\n\n\n /**\n * @summary エントリーを取得\n *\n * @param {number|string} [id] モデル ID\n * @return {?mapray.ModelContainer.Entry} モデルエントリー\n * @private\n */\n _getEntry( id )\n {\n if ( typeof id == 'number' ) {\n // id を整数で指定\n if ( 0 <= id && id < this._entries.length ) {\n return this._entries[id];\n }\n }\n else if ( typeof id == 'string' ) {\n // id を名前で指定\n if ( this._name_map.hasOwnProperty( id ) ) {\n return this._name_map[id];\n }\n }\n else {\n // id 指定なし\n if ( this._entries.length > 0 ) {\n return this._entries[0];\n }\n }\n\n return null;\n }\n\n}\n\n\n/**\n * @summary モデルエントリー\n *\n * @memberof mapray.ModelContainer\n * @private\n */\nclass Entry {\n\n /**\n * @param {mapray.Scene} mr_scene Mapray シーン\n * @param {mapray.gltf.Scene} gltf_scene glTF シーン\n * @param {object} share Builder インスタンス間の共有情報\n */\n constructor( mr_scene, gltf_scene, share )\n {\n this._builder = new Builder( mr_scene, gltf_scene, share );\n }\n\n\n /**\n * @summary mapray.Primitive の配列を取得\n * @desc\n * transform プロパティはプリミティブ座標系からエンティティ座標系への変換になっている。
\n * @type {mapray.Primitive[]}\n * @readonly\n */\n getPrimitives( options ) {\n return this._builder.getPrimitives( options );\n }\n\n}\n\n\n/**\n * @summary glTF シーンから mapray.Primitive の配列を構築\n *\n * @memberof mapray.ModelContainer\n * @private\n */\nclass Builder {\n\n /**\n * @param {mapray.Scene} mr_scene Mapray シーン\n * @param {mapray.gltf.Scene} gltf_scene glTF シーン\n * @param {object} share Builder インスタンス間の共有情報\n */\n constructor( mr_scene, gltf_scene, share )\n {\n // share を初期化\n if ( !share.buffer_map ) {\n share.buffer_map = new Map(); // gltf.Buffer -> MeshBuffer\n share.texture_map = new Map(); // gltf.Texture -> Texture\n }\n\n this._mr_scene = mr_scene;\n this._glenv = mr_scene.glenv;\n this._primitives = [];\n this._pickPrimitives = [];\n\n this._buffer_map = share.buffer_map;\n this._texture_map = share.texture_map;\n\n var identity = GeoMath.setIdentity( GeoMath.createMatrix() ); // シーンからシーンへの変換 (恒等行列)\n\n for ( var node of gltf_scene.root_nodes ) {\n this._addNode( node, identity );\n }\n }\n\n\n /**\n * @summary mapray.Primitive の配列を取得\n * @desc\n * transform プロパティはプリミティブ座標系からエンティティ座標系への変換になっている。
\n * @type {mapray.Primitive[]}\n * @readonly\n */\n getPrimitives( options={} ) {\n return options.ridMaterial ? this._pickPrimitives : this._primitives;\n }\n\n\n /**\n * ノードを追加\n *\n * @param {mapray.gltf.Node} node 追加対象のノード\n * @param {mapray.Matrix} ptos 親ノード座標系からシーン座標系への変換\n * @private\n */\n _addNode( node, ptos )\n {\n var ntos = Builder._getNodeToScene( node, ptos );\n\n if ( node.mesh !== null ) {\n for ( var primitive of node.mesh.primitives ) {\n // プリミティブを追加\n this._primitives.push( this._createPrimitive( primitive, ntos, { ridMaterial: false } ) );\n this._pickPrimitives.push( this._createPrimitive( primitive, ntos, { ridMaterial: true } ) );\n }\n }\n\n // 子孫の処理\n for ( var child of node.children ) {\n this._addNode( child, ntos );\n }\n }\n\n\n /**\n * node 座標系からシーン座標系の変換行列を取得\n *\n * @param {mapray.gltf.Node} node 追加対象のノード\n * @param {mapray.Matrix} ptos 親ノード座標系からシーン座標系への変換行列\n * @return {mapray.Matrix} node 座標系からシーン座標系の変換行列\n * @private\n */\n static\n _getNodeToScene( node, ptos )\n {\n var ntos = ptos; // node 座標系からシーン座標系の変換\n\n var ntop = node.matrix; // node 座標系から親ノード座標系の変換\n if ( ntop !== null ) {\n ntos = GeoMath.createMatrix();\n GeoMath.mul_AA( ptos, ntop, ntos );\n }\n\n return ntos;\n }\n\n\n /**\n * プリミティブを生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @param {mapray.Matrix} ntos ノード座標系からシーン座標系への変換\n * @return {mapray.Primitive} 出力プリミティブ\n * @private\n */\n _createPrimitive( iprim, ntos, opts={} )\n {\n var mesh = this._createMesh( iprim );\n var material = this._createMaterial( iprim, { ridMaterial: opts.ridMaterial } )\n var oprim = new Primitive( this._glenv, mesh, material, GeoMath.createMatrix( ntos ) );\n\n oprim.pivot = this._createMeshPivot( iprim );\n oprim.bbox = this._createBoundingBox( iprim );\n oprim.properties = this._createProperties( iprim );\n\n return oprim;\n }\n\n\n /**\n * メッシュを生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {mapray.Mesh} メッシュ\n * @private\n */\n _createMesh( iprim )\n {\n var init = new Mesh.Initializer( Builder._convertPrimitiveMode( iprim ), Builder._calcNumVertices( iprim ) );\n\n var attributes = iprim.attributes;\n for ( var name in attributes ) {\n this._addAttribToInit( init, name, attributes[name] );\n }\n\n var indices = iprim.indices;\n if ( indices !== null ) {\n this._addIndexToInit( init, indices );\n }\n\n return new Mesh( this._glenv, init );\n }\n\n\n /**\n * 描画モードに変換\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {mapray.Mesh.DrawMode} 描画モード\n * @private\n */\n static\n _convertPrimitiveMode( iprim )\n {\n return Builder._DrawMode[iprim.mode];\n }\n\n\n /**\n * 頂点数を計算\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {number} 頂点数\n * @private\n */\n static\n _calcNumVertices( iprim )\n {\n var attributes = iprim.attributes;\n\n var counts = [];\n\n for ( var name in attributes ) {\n var accessor = attributes[name];\n counts.push( accessor.count );\n }\n\n return Math.min.apply( null, counts );\n }\n\n\n /**\n * 頂点属性をイニシャライザに追加\n *\n * @param {mapray.Mesh.Initializer} init 追加先\n * @param {string} name 属性名\n * @param {mapray.gltf.Accessor} accessor アクセサ\n * @private\n */\n _addAttribToInit( init, name, accessor )\n {\n var buffer = this._findMeshBuffer( accessor.bufferView.buffer, MeshBuffer.Target.ATTRIBUTE );\n\n var num_components = Builder._NumComponents[accessor.type];\n var component_type = Builder._ComponentType[accessor.componentType];\n\n var options = {\n normalized: accessor.normalized,\n byte_stride: accessor.bufferView.byteStride,\n byte_offset: accessor.bufferView.byteOffset + accessor.byteOffset\n };\n\n var id = Builder._VertexAttribId[name] || name;\n\n init.addAttribute( id, buffer, num_components, component_type, options );\n }\n\n\n /**\n * インデックスをイニシャライザに追加\n *\n * @param {mapray.Mesh.Initializer} init 追加先\n * @param {mapray.gltf.Accessor} accessor アクセサ\n * @private\n */\n _addIndexToInit( init, accessor )\n {\n var buffer = this._findMeshBuffer( accessor.bufferView.buffer, MeshBuffer.Target.INDEX );\n\n var num_indices = accessor.count;\n var type = Builder._ComponentType[accessor.componentType];\n\n var options = {\n byte_offset: accessor.bufferView.byteOffset + accessor.byteOffset\n };\n\n init.addIndex( buffer, num_indices, type, options );\n }\n\n\n /**\n * MeshBuffer インスタンスを検索\n *\n * @param {mapray.gltf.Buffer} buffer 入力バッファ\n * @param {mapray.MeshBuffer.Target} target 使用目的\n * @return {mapray.MeshBuffer}\n * @private\n */\n _findMeshBuffer( buffer, target )\n {\n var meshBuffer = this._buffer_map.get( buffer );\n if ( meshBuffer === undefined ) {\n meshBuffer = new MeshBuffer( this._glenv, buffer.binary, { target: target } );\n this._buffer_map.set( buffer, meshBuffer );\n }\n\n return meshBuffer;\n }\n\n\n /**\n * マテリアルを生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {mapray.EntityMaterial} マテリアル\n * @private\n */\n _createMaterial( iprim, opts = {} )\n {\n // キャッシュの場所とオプションを決定\n let cache_suffix = \"basic\";\n let options = {};\n\n if ( iprim.material && iprim.material.commonData.getExtensions( \"KHR_materials_unlit\" ) ) {\n cache_suffix = \"unlit\";\n options.is_unlit = true;\n }\n\n if ( opts.ridMaterial ) {\n options.ridMaterial = true;\n }\n\n // マテリアルのインスタンスを取得\n const scene = this._mr_scene;\n const cache_id = \"_ModelEntity_model_material_\" + cache_suffix + (opts.ridMaterial ? \"_pick\" : \"\");\n\n if ( !scene[cache_id] ) {\n // scene にマテリアルをキャッシュ\n scene[cache_id] = new ModelMaterial( scene.glenv, options );\n }\n\n return scene[cache_id];\n }\n\n\n /**\n * メッシュ基点を生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {?mapray.Vector3} メッシュ基点\n * @private\n */\n _createMeshPivot( iprim )\n {\n var pivot = null;\n var bbox = this._createBoundingBox( iprim );\n\n if ( bbox !== null ) {\n pivot = GeoMath.createVector3();\n // 境界箱の中点\n for ( var i = 0; i < 3; ++i ) {\n pivot[i] = (bbox[0][i] + bbox[1][i]) / 2;\n }\n }\n\n return pivot;\n }\n\n\n /**\n * 境界箱を生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {?mapray.Vector3[]} 境界箱\n * @private\n */\n _createBoundingBox( iprim )\n {\n var bbox = null;\n\n var attrib = iprim.attributes['POSITION'];\n if ( attrib !== undefined ) {\n var min = attrib.min;\n var max = attrib.max;\n if ( min !== null && max !== null ) {\n bbox = [GeoMath.createVector3( min ), GeoMath.createVector3( max )];\n }\n }\n\n return bbox;\n }\n\n\n /**\n * プロパティを生成\n *\n * @param {mapray.gltf.Primitive} iprim 入力プリミティブ\n * @return {object} プロパティ\n * @private\n */\n _createProperties( iprim )\n {\n var material = iprim.material;\n\n if ( material === null ) {\n // 既定のマテリアル\n return {\n pbrMetallicRoughness: {\n baseColorFactor: GeoMath.createVector4f( [1.0, 1.0, 1.0, 1.0] ),\n baseColorTexture: null,\n metallicFactor: 1.0,\n roughnessFactor: 1.0,\n metallicRoughnessTexture: null\n },\n doubleSided: false,\n alphaMode: \"OPAQUE\",\n alphaCutoff: 0.5,\n emissiveFactor: GeoMath.createVector3f( [0.0, 0.0, 0.0] ),\n emissiveTexture: null,\n normalTexture: null,\n occlusionTexture: null\n };\n }\n else {\n const pbrMR = material.pbrMetallicRoughness;\n\n return {\n pbrMetallicRoughness: {\n baseColorFactor: GeoMath.createVector4f( pbrMR.baseColorFactor ),\n baseColorTexture: this._createTextureParam( pbrMR.baseColorTexture ),\n metallicFactor: pbrMR.metallicFactor,\n roughnessFactor: pbrMR.roughnessFactor,\n metallicRoughnessTexture: this._createTextureParam( pbrMR.metallicRoughnessTexture )\n },\n doubleSided: material.doubleSided,\n alphaMode: material.alphaMode,\n alphaCutoff: material.alphaCutoff,\n emissiveFactor: GeoMath.createVector3f( material.emissiveFactor ),\n emissiveTexture: this._createTextureParam( material.emissiveTexture ),\n normalTexture: this._createTextureParam( material.normalTexture ),\n occlusionTexture: this._createTextureParam( material.occlusionTexture )\n };\n }\n }\n\n\n /**\n * テクスチャパラメータを生成\n *\n * @param {mapray.gltf.TextureInfo} texinfo TextureInfo インスタンス\n * @return {object} テクスチャパラメータ\n * @private\n */\n _createTextureParam( texinfo )\n {\n if ( texinfo === null ) {\n return null;\n }\n\n var param = {\n texture: this._findTexture( texinfo.texture ),\n texCoord: texinfo.texCoord\n };\n\n if ( texinfo instanceof NormalTextureInfo ) {\n param.scale = texinfo.scale;\n }\n else if ( texinfo instanceof OcclusionTextureInfo ) {\n param.strength = texinfo.strength;\n }\n\n return param;\n }\n\n\n /**\n * モデル用のプロパティを複製\n *\n * @param {mapray.PropSet} props\n * @return {mapray.PropSet}\n *\n * @see _createProperties()\n */\n static\n fastCloneProperties( props )\n {\n const src_pbr = props.pbrMetallicRoughness;\n\n return {\n pbrMetallicRoughness: {\n baseColorFactor: GeoMath.createVector3f( src_pbr.baseColorFactor ),\n baseColorTexture: Builder._fastCloneTextureParam( src_pbr.baseColorTexture ),\n metallicFactor: src_pbr.metallicFactor,\n roughnessFactor: src_pbr.roughnessFactor,\n metallicRoughnessTexture: Builder._fastCloneTextureParam( src_pbr.metallicRoughnessTexture )\n },\n doubleSided: props.doubleSided,\n alphaMode: props.alphaMode,\n alphaCutoff: props.alphaCutoff,\n emissiveFactor: GeoMath.createVector3f( props.emissiveFactor ),\n emissiveTexture: Builder._fastCloneTextureParam( props.emissiveTexture ),\n normalTexture: Builder._fastCloneTextureParam( props.normalTexture ),\n occlusionTexture: Builder._fastCloneTextureParam( props.occlusionTexture )\n };\n }\n\n\n /**\n * テクスチャパラメータを複製\n *\n * @param {object} param\n * @return {!object}\n *\n * @private\n * @see _createTextureParam()\n */\n static\n _fastCloneTextureParam( iparam )\n {\n if ( iparam === null ) return null;\n\n var oparam = {\n texture: iparam.texture,\n texCoord: iparam.texCoord\n };\n\n if ( 'scale' in iparam ) {\n oparam.scale = iparam.scale;\n }\n else if ( 'strength' in iparam ) {\n oparam.strength = iparam.strength;\n }\n\n return oparam;\n }\n\n\n /**\n * テクスチャパラメータを生成\n *\n * @param {mapray.gltf.Texture} itexture glTF テクスチャ\n * @return {mapray.Texture} テクスチャ\n * @private\n */\n _findTexture( itexture )\n {\n var otexture = this._texture_map.get( itexture );\n\n if ( otexture === undefined ) {\n var sampler = itexture.sampler;\n var gl = this._glenv.context;\n var tex_opts = {\n mag_filter: (sampler.magFilter !== undefined) ? sampler.magFilter : gl.LINEAR,\n min_filter: (sampler.minFilter !== undefined) ? sampler.minFilter : gl.LINEAR_MIPMAP_LINEAR,\n wrap_s: sampler.wrapS,\n wrap_t: sampler.wrapT,\n flip_y: false // glTF のテクスチャ座標は左上が原点なので画像を反転しない\n };\n otexture = new Texture( this._glenv, itexture.source.image, tex_opts );\n this._texture_map.set( itexture, otexture );\n }\n\n return otexture;\n }\n\n}\n\n\n// gltf.Primitive.mode -> mapray.Mesh.DrawMode\nBuilder._DrawMode = {\n 0: Mesh.DrawMode.POINTS,\n 1: Mesh.DrawMode.LINES,\n 2: Mesh.DrawMode.LINE_LOOP,\n 3: Mesh.DrawMode.LINE_STRIP,\n 4: Mesh.DrawMode.TRIANGLES,\n 5: Mesh.DrawMode.TRIANGLE_STRIP,\n 6: Mesh.DrawMode.TRIANGLE_FAN\n};\n\n\n// gltf.Accessor.type -> 要素数\nBuilder._NumComponents = {\n 'SCALAR': 1,\n 'VEC2': 2,\n 'VEC3': 3,\n 'VEC4': 4\n};\n\n\n// gltf.Accessor.componentType -> mapray.Mesh.ComponentType\nBuilder._ComponentType = {\n 5120: Mesh.ComponentType.BYTE,\n 5121: Mesh.ComponentType.UNSIGNED_BYTE,\n 5122: Mesh.ComponentType.SHORT,\n 5123: Mesh.ComponentType.UNSIGNED_SHORT,\n 5125: Mesh.ComponentType.UNSIGNED_INT,\n 5126: Mesh.ComponentType.FLOAT\n};\n\n\n// gltf.Primitive.attributes のキー -> 頂点属性 ID\nBuilder._VertexAttribId = {\n 'POSITION': \"a_position\",\n 'NORMAL': \"a_normal\",\n 'TANGENT': \"a_tangent\",\n 'TEXCOORD_0': \"a_texcoord\",\n 'TEXCOORD_1': \"a_texcoord1\",\n 'COLOR_0': \"a_color\"\n};\n\n\nexport default ModelContainer;\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport line_vs_code from \"./shader/line.vert\";\nimport line_fs_code from \"./shader/line.frag\";\nimport AbstractLineEntity from \"./AbstractLineEntity\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary 太さ付き線分専用マテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n */\nclass LineMaterial extends EntityMaterial {\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, line_type, options = {} )\n {\n const preamble = LineMaterial._getPreamble( line_type, options );\n super( glenv, preamble + line_vs_code, preamble + line_fs_code );\n\n this._line_type = line_type;\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n var props = primitive.properties;\n var opacity = (props.opacity !== undefined) ? props.opacity : LineMaterial.DEFAULT_OPACITY;\n return opacity < 1.0;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // u_obj_to_clip\n this.setObjToClip( stage, primitive );\n\n // 画面パラメータ: {2/w, 2/h, h/w}\n // vec3 u_sparam\n var sparam = LineMaterial._sparam;\n sparam[0] = 2 / stage._width;\n sparam[1] = 2 / stage._height;\n sparam[2] = stage._height / stage._width;\n this.setVector3( \"u_sparam\", sparam );\n\n // 線の太さの半分: {u, v}\n // vec2 u_thickness\n var param_width = props.width || LineMaterial.DEFAULT_WIDTH;\n\n var thickness = LineMaterial._thickness;\n thickness[0] = param_width / 2;\n thickness[1] = param_width / 2;\n this.setVector2( \"u_thickness\", thickness );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // 線の基本色\n // vec4 u_color\n var param_color = (props.color !== undefined) ? props.color : LineMaterial.DEFAULT_COLOR;\n var param_opacity = (props.opacity !== undefined) ? props.opacity : LineMaterial.DEFAULT_OPACITY;\n\n var color = LineMaterial._color;\n GeoMath.copyVector3( param_color, color );\n color[3] = param_opacity;\n this.setVector4( \"u_color\", color );\n }\n\n // RID rendering also requires u_lower_length and u_upper_length.\n if ( this._line_type == AbstractLineEntity.LineType.PATH ) {\n var lower_length = props[\"lower_length\"];\n this.setFloat( \"u_lower_length\", lower_length );\n\n var upper_length = props[\"upper_length\"];\n this.setFloat( \"u_upper_length\", upper_length );\n }\n }\n\n\n /**\n * @summary シェーダの前文を取得\n *\n * @param {AbstractLineEntity.LineType} line_type\n * @param {object} options\n *\n * @private\n */\n static\n _getPreamble( line_type, options )\n {\n const lines = [];\n\n if ( line_type == AbstractLineEntity.LineType.PATH ) {\n lines.push( \"#define PATH\" );\n }\n\n if ( options.ridMaterial ) {\n lines.push( \"#define RID\" );\n }\n\n // lines を文字列にして返す\n return lines.join( \"\\n\" ) + \"\\n\\n\";\n }\n}\n\n\n// クラス定数の定義\n{\n LineMaterial.DEFAULT_WIDTH = 1.0;\n LineMaterial.DEFAULT_COLOR = GeoMath.createVector3f( [1.0, 1.0, 1.0] );\n LineMaterial.DEFAULT_OPACITY = 1.0;\n LineMaterial.DEFAULT_LOWER_LENGTH = 0.0;\n LineMaterial.DEFAULT_UPPER_LENGTH = 0.0;\n\n // 計算用一時領域\n LineMaterial._sparam = GeoMath.createVector3f();\n LineMaterial._thickness = GeoMath.createVector2f();\n LineMaterial._color = GeoMath.createVector4f();\n}\n\n\nexport default LineMaterial;\n","import GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\n\n/**\n * @summary 地理空間的Region\n *\n * @memberof mapray\n */\nclass GeoRegion {\n\n /**\n * 初期値は empty である。\n * lon は次の条件とする。\n * 条件 this._min_lon < this._max_lon
\n * 条件 this._max_lon - this._min_lon <= 360
\n */\n constructor()\n {\n this._empty = true;\n this._min_lon = 0;\n this._max_lon = 0;\n this._min_lat = 0;\n this._max_lat = 0;\n this._min_alt = 0;\n this._max_alt = 0;\n }\n\n\n /**\n * @summary 空確認\n *\n * @return {boolean} empty値\n */\n empty()\n {\n return this._empty;\n }\n\n\n /**\n * @summary Pointを追加\n * 条件 this._min_lon < this._max_lon
\n * 条件 this._max_lon - this._min_lon <= 360
\n *\n * @param {number} lon 地理空間位置 longitude\n * @param {number} lat 地理空間位置 latitude\n * @param {number} alt 地理空間位置 altitude\n * @private\n */\n _add( lon, lat, alt )\n {\n if ( this._empty ) {\n this._empty = false;\n lon = lon - 360 * Math.floor( lon/360 + 0.5 );\n this._min_lon = this._max_lon = lon;\n this._min_lat = this._max_lat = lat;\n this._min_alt = this._max_alt = alt;\n }\n else {\n const right_min_lon = this._calcRightPosition( this._min_lon, lon );\n\n if ( right_min_lon <= this._max_lon ) {\n // lon はmin,maxに内包されている\n // lon の min, max の確認は不要\n }\n else {\n const right_max_lon = this._calcRightPosition( this._max_lon, lon );\n\n const length1 = right_max_lon - this._max_lon ;\n const length2 = right_min_lon - 360 - this._min_lon;\n if ( Math.abs(length1) <= Math.abs(length2) ) {\n // expand east\n this._max_lon = this._max_lon + length1;\n }\n else {\n // expand west\n this._min_lon = this._min_lon + length2;\n }\n }\n\n if ( lat < this._min_lat ) this._min_lat = lat;\n else if ( lat > this._max_lat ) this._max_lat = lat;\n if ( alt < this._min_alt ) this._min_alt = alt;\n else if ( alt > this._max_alt ) this._max_alt = alt;\n }\n }\n\n /**\n * @summary base_lon より右となる最小の longitude を算出\n *\n * @param {number} base_lon 基準となるlongitude\n * @param {number} lon 確認したいlongitude\n * @return {number} 最小longitude\n * @private\n */\n _calcRightPosition( base_lon, lon )\n {\n let diff = lon - base_lon;\n diff = diff - 360 * Math.floor(diff / 360);\n return base_lon + diff;\n }\n\n\n /**\n * @summary GeoPointを追加\n *\n * @param {mapary.GeoPoint} point 地理空間位置\n */\n addPoint( point )\n {\n this._add( point.longitude, point.latitude, point.altitude );\n }\n\n\n /**\n * @summary GeoPointを追加\n *\n * @param {...mapary.GeoPoint} points 地理空間位置\n */\n addPoints( ...points )\n {\n points.forEach( point => {\n this._add( point.longitude, point.latitude, point.altitude );\n });\n }\n\n\n /**\n * @summary PointArrayを追加\n *\n * @param {number[]} pointsArray 地理空間位置Array\n */\n addPointsAsArray( pointsArray )\n {\n for ( let i = 0; i < pointsArray.length; i += 3 ) {\n this._add( pointsArray[i], pointsArray[i+1], pointsArray[i+2] );\n };\n }\n\n\n /**\n * @summary GeoRegionを結合\n *\n * @param {mapray.GeoRegion} region 地理空間Region\n */\n merge( region )\n {\n if( region.empty() ) { return; }\n if ( this._empty ) {\n this._min_lon = region._min_lon;\n this._max_lon = region._max_lon;\n this._min_lat = region._min_lat;\n this._max_lat = region._max_lat;\n this._min_alt = region._min_alt;\n this._max_alt = region._max_alt;\n this._empty = false;\n }\n else {\n // 内包判定\n let right_min_lon = this._calcRightPosition( this._min_lon, region._min_lon );\n const right_max_lon = this._calcRightPosition( this._min_lon, region._max_lon );\n\n if( right_min_lon > right_max_lon ) {\n right_min_lon -= 360;\n }\n\n let min_include = false;\n if ( ( right_min_lon >= this._min_lon ) && ( right_min_lon <= this._max_lon ) ) {\n min_include = true;\n }\n let max_include = false;\n if ( ( right_max_lon >= this._min_lon ) && ( right_max_lon <= this._max_lon ) ) {\n max_include = true;\n }\n\n // 1.thisがregionを内包\n if ( min_include && max_include ) {\n // 内包状態 -> 変化無し\n }\n\n // 2.westのみ内包 -> east拡大\n else if ( min_include ) {\n this._max_lon = right_max_lon;\n }\n\n // 3.eastのみ内包 -> west拡大\n else if ( max_include ) {\n this._min_lon = right_min_lon;\n }\n\n // 4.regionがthisを内包\n else if ( ( this._min_lon >= right_min_lon ) && ( this._max_lon <= right_max_lon ) )\n {\n this._min_lon = region._min_lon;\n this._max_lon = region._max_lon;\n }\n\n // 5.共有無し\n else {\n // east と west 比較\n const length1 = right_max_lon - this._max_lon ;\n const length2 = right_min_lon - 360 - this._min_lon;\n if ( Math.abs(length1) <= Math.abs(length2) ) {\n // expand east\n this._max_lon = this._max_lon + length1;\n }\n else {\n // expand west\n this._min_lon = this._min_lon + length2;\n }\n }\n\n // lat と alt\n this._min_lat = Math.min(this._min_lat, region._min_lat);\n this._max_lat = Math.max(this._max_lat, region._max_lat);\n this._min_alt = Math.min(this._min_alt, region._min_alt);\n this._max_alt = Math.max(this._max_alt, region._max_alt);\n }\n }\n\n\n /**\n * @summary Region内の任意点の取得\n * center ( 0.5, 0.5 )\n * east ( 1, 0.5 )\n * west ( 0, 0.5 )\n * north ( 0.5, 1 )\n * south ( 0.5, 0 )\n * northEast ( 1, 1 )\n * southWest ( 0, 0 )\n *\n * @param {number} lon_pos longitude位置割合\n * @param {number} lat_pos latitude位置割合\n * @param {number} [alt_pos] anlitude位置割合\n * @return {mapary.GeoPoint} GeoPoint\n */\n getPoint( lon_pos, lat_pos, alt_pos=0 )\n {\n if ( this._empty ) { return null; }\n return new GeoPoint(\n (1-lon_pos) * this._min_lon + lon_pos * this._max_lon,\n (1-lat_pos) * this._min_lat + lat_pos * this._max_lat,\n (1-alt_pos) * this._min_alt + alt_pos * this._max_alt\n );\n }\n\n\n /**\n * @summary SouthWestのGeopointを返却\n *\n * @return {mapary.GeoPoint} 南西(min)のGeoPoint\n */\n getSouthWest()\n {\n if ( this._empty ) { return null; }\n return new GeoPoint( this._min_lon, this._min_lat, this._min_alt );\n }\n\n\n /**\n * @summary NorthEastのGeopointを返却\n *\n * @return {mapary.GeoPoint} 北東(max)のGeoPoint\n */\n getNorthEast()\n {\n if ( this._empty ) { return null; }\n return new GeoPoint( this._max_lon, this._max_lat, this._max_alt );\n }\n\n\n /**\n * @summary 中心位置のGeopointを返却\n *\n * @return {mapary.GeoPoint} 中心(center)のGeoPoint\n */\n getCenter()\n {\n if ( this._empty ) { return null; }\n return this.getPoint( 0.5, 0.5, 0.5 );\n }\n\n\n /**\n * @summary RegionのLongitude方向 の地表面距離を算出\n *\n * @return {number} Longitude方向の地表面距離\n */\n getLongitudeDistance()\n {\n if ( this._empty ) { return null; }\n return this.getPoint( 0, 0.5 ).getGeographicalDistance( this.getPoint( 1, 0.5 ) );\n }\n\n\n /**\n * @summary RegionのLatitude方向 の地表面距離を算出\n *\n * @return {number} Latitude方向の地表面距離\n */\n getLatitudeDistance()\n {\n if ( this._empty ) { return null; }\n return this.getPoint( 0.5, 0 ).getGeographicalDistance( this.getPoint( 0.5, 1 ) );\n }\n}\n\n\nexport default GeoRegion;\n","import GeoMath from \"./GeoMath\";\n\n\n/**\n * @summary エンティティ領域\n *\n * @classdesc\n * 標高の変化に伴い、エンティティの更新を行うためのクラスである。
\n *\n * @memberof mapray\n * @private\n * @see mapray.UpdatedTileArea\n */\nclass EntityRegion {\n\n /**\n */\n constructor()\n {\n this._is_compiled = false;\n\n this._point_array = new Float64Array( 0 );\n this._num_points = 0;\n\n this._node_array = new Uint32Array( 0 );\n this._next_node = 0;\n }\n\n\n /**\n * @summary 位置を追加\n *\n * @desc\n * point.altitude は無視される。
\n *\n * @param {mapray.GeoPoint} point 位置\n */\n addPoint( point )\n {\n this._checkNotCompiled();\n this._ensurePointArrayCapacity( 2 );\n\n var index = 2 * this._num_points;\n\n this._point_array[index] = point.longitude;\n this._point_array[index + 1] = point.latitude;\n\n this._num_points += 1;\n }\n\n\n /**\n * @summary 複数の位置を追加\n *\n * @param {number[]} points 頂点配列 (経度, 緯度, ...)\n * @param {number} offset 先頭インデックス\n * @param {number} stride ストライド\n * @param {number} num_points 頂点数\n */\n addPoints( points, offset, stride, num_points )\n {\n this._checkNotCompiled();\n this._ensurePointArrayCapacity( 2 * num_points );\n\n var src_index = offset;\n var dst_index = 2 * this._num_points;\n var dst_array = this._point_array;\n\n for ( var i = 0; i < num_points; ++i ) {\n dst_array[dst_index] = points[src_index];\n dst_array[dst_index + 1] = points[src_index + 1];\n src_index += stride;\n dst_index += 2;\n }\n\n this._num_points += num_points;\n }\n\n\n /**\n * @summary 比較処理用に翻訳\n *\n * @package\n */\n compile()\n {\n if ( this._is_compiled ) {\n // すでに翻訳済み\n return;\n }\n\n this._buildCollisionQuadTree();\n\n // this._node_array から使っていない最後の領域を削除\n if ( this._node_array.length > this._next_node ) {\n this._node_array = new Uint32Array( this._node_array.slice( 0, this._next_node ) );\n }\n\n this._point_array = null; // 翻訳後は使わないので捨てる\n this._is_compiled = true; // 翻訳済みにする\n }\n\n\n /**\n * @summary this と area は交差するか?\n *\n * @param {mapray.UpdatedTileArea} area 判定する領域\n *\n * @return {boolean} 交差するとき true, それ以外のとき false\n *\n * @package\n */\n intersectsWith( area )\n {\n if ( this._node_array.length == 0 ) {\n // this は空領域\n return false;\n }\n\n var area_list = area.getFlatAreaList();\n\n for ( var i = 0; i < area_list.length; ++i ) {\n if ( this._intersectsWith( area_list[i] ) ) {\n // ある領域が交差した\n return true;\n }\n }\n\n // すべての領域が交差しなかった\n return false;\n }\n\n\n /**\n * @summary this と area は交差するか? (単一領域)\n *\n * @param {Uint8Array} area 判定する領域\n *\n * @return {boolean} 交差するとき true, それ以外のとき false\n *\n * @private\n */\n _intersectsWith( area )\n {\n // assert this._node_array.length > 0\n\n var node = 0;\n var node_array = this._node_array;\n\n for ( var i = 0; i < area.length; ++i ) {\n node = node_array[node + area[i]];\n\n if ( node == FULL_INDEX ) {\n // 交差する (area は全域ノードの内側)\n return true;\n }\n else if ( node == EMPTY_INDEX ) {\n // 交差しない (area は空ノードの内側)\n return false;\n }\n }\n\n // 交差する\n // area.length == 0 (全球領域) または area の最後が this 階層の途中\n return true;\n }\n\n\n /**\n * @summary すでに翻訳されてるときエラー\n *\n * @private\n */\n _checkNotCompiled()\n {\n if ( this._is_compiled ) {\n throw new Error( \"EitityRegion is already compiled.\" );\n }\n }\n\n\n /**\n * @summary this._point_array の容量を十分にする\n *\n * @param {number} added_size 追加サイズ\n *\n * @private\n */\n _ensurePointArrayCapacity( added_size )\n {\n var old_size = 2 * this._num_points;\n var needed_capacity = old_size + added_size;\n var old_capacity = this._point_array.length;\n\n if ( needed_capacity > old_capacity ) {\n // 配列を拡張する\n var new_capacity = Math.max( needed_capacity, Math.floor( 1.5 * old_capacity ) );\n var new_point_array = new Float64Array( new_capacity );\n new_point_array.set( this._point_array.slice( 0, old_size ) );\n this._point_array = new_point_array;\n }\n }\n\n\n /**\n * @summary 領域判定四分木を構築\n *\n * @private\n */\n _buildCollisionQuadTree()\n {\n var dPI = 2 * Math.PI;\n\n var point_array = this._point_array;\n var num_floats = 2 * this._num_points;\n\n for ( var i = 0; i < num_floats; ) {\n // 経緯度 (Degrees)\n var lon = point_array[i++];\n var lat = point_array[i++];\n\n // 正規化経緯度 (Degrees)\n var _lon = lon + 180 * Math.floor( (90 - lat) / 360 + Math.floor( (90 + lat) / 360 ) );\n var nlon = _lon - 360 - 360 * Math.floor( (_lon - 180) / 360 ); // 正規化経度 [-180,180)\n var nlat = 90 - Math.abs( 90 - lat + 360 * Math.floor( (90 + lat) / 360 ) ); // 正規化緯度 [-90,90]\n\n // 単位球メルカトル座標\n var xm = nlon * GeoMath.DEGREE;\n var ym = GeoMath.invGudermannian( nlat * GeoMath.DEGREE );\n\n // 基底タイル座標 (左上(0, 0)、右下(1, 1))\n var xt = xm / dPI + 0.5;\n var yt = 0.5 - ym / dPI;\n\n // ノードを追加\n this._addCollisionQuadTreeNode( xt, yt );\n }\n\n // 全域ノードを設定\n if ( this._next_node > 0 ) {\n this._setFullNodeRecur( 0 );\n this._reduceNodeRecur( 0 );\n }\n }\n\n\n /**\n * @summary 領域判定四分木のノードを追加\n *\n * @param {number} xt 基底タイル座標 X\n * @param {number} yt 基底タイル座標 Y\n *\n * @private\n */\n _addCollisionQuadTreeNode( xt, yt )\n {\n if ( yt < 0 || yt > 1 ) {\n // 緯度が範囲外 (極に近い)\n return;\n }\n\n var size = 1 << MAX_LEVEL;\n var ubits = GeoMath.clamp( Math.floor( xt * size ), 0, size - 1 );\n var vbits = Math.min( Math.floor( yt * size ), size - 1 ); // >= 0\n var node = this._findRootNode();\n\n for ( var mask = size >> 1; mask != 0; mask >>= 1 ) {\n var u = ((ubits & mask) == 0) ? 0 : 1;\n var v = ((vbits & mask) == 0) ? 0 : 2;\n node = this._findChildNode( node, u + v );\n }\n }\n\n\n /**\n * @summary 最上位ノードを検索\n *\n * @return {number} 最上位ノード\n *\n * @private\n */\n _findRootNode()\n {\n if ( this._next_node == 0 ) {\n // まだ最上位ノードが存在しない\n // 最上位ノードを生成する\n this._ensureNodeArrayCapacity();\n\n for ( var i = 0; i < 4; ++i ) {\n this._node_array[i] = EMPTY_INDEX;\n }\n this._next_node = 4;\n }\n\n return 0;\n }\n\n\n /**\n * @summary 子ノードを検索\n *\n * @param {number} parent 親ノード\n * @param {number} ichild 子ノード序列 (0-3)\n * @return {number} 子ノード\n *\n * @private\n */\n _findChildNode( parent, ichild )\n {\n var child = this._node_array[parent + ichild];\n\n if ( child == 0 ) {\n // まだ子ノードが存在しない\n // 子ノードを生成する\n this._ensureNodeArrayCapacity();\n\n child = this._next_node;\n for ( var i = 0; i < 4; ++i ) {\n this._node_array[child + i] = EMPTY_INDEX;\n }\n this._next_node += 4;\n\n // 親ノードに生成した子ノードを取り付ける\n this._node_array[parent + ichild] = child;\n }\n\n return child;\n }\n\n\n /**\n * @summary 全域ノードを再帰的に設定\n *\n * @desc\n * 末端ノードの子ノードを FULL_INDEX に設定する。
\n *\n * @param {number} node ノードの索引\n *\n * @private\n */\n _setFullNodeRecur( node )\n {\n var node_array = this._node_array;\n var is_leaf = true;\n\n for ( var i = 0; i < 4; ++i ) {\n var child = node_array[node + i];\n if ( child != EMPTY_INDEX ) {\n this._setFullNodeRecur( child );\n is_leaf = false;\n }\n }\n\n // 末端なら子ノードを FULL_INDEX\n if ( is_leaf ) {\n for ( i = 0; i < 4; ++i ) {\n node_array[node + i] = FULL_INDEX;\n }\n }\n }\n\n\n /**\n * @summary 全域ノードを再帰的に設定\n *\n * @param {number} node ノードの索引\n * @return {boolean} 全域ノードなら true, その他なら false\n *\n * @private\n */\n _reduceNodeRecur( node )\n {\n var node_array = this._node_array;\n var num_fulls = 0;\n\n for ( var i = 0; i < 4; ++i ) {\n var child = node_array[node + i];\n if ( child == FULL_INDEX ) {\n ++num_fulls;\n }\n else if ( child != EMPTY_INDEX ) {\n if ( this._reduceNodeRecur( child ) ) {\n node_array[node + i] = FULL_INDEX;\n ++num_fulls;\n }\n }\n }\n\n return (num_fulls == 4);\n }\n\n\n /**\n * @summary this._node_array の容量を十分にする\n *\n * @private\n */\n _ensureNodeArrayCapacity()\n {\n var old_size = this._next_node;\n var needed_capacity = old_size + 4;\n var old_capacity = this._node_array.length;\n\n if ( needed_capacity > old_capacity ) {\n // 配列を拡張する\n var new_capacity = Math.max( needed_capacity, Math.floor( 1.5 * old_capacity ) );\n var new_node_array = new Uint32Array( new_capacity );\n new_node_array.set( this._node_array.slice( 0, old_size ) );\n this._node_array = new_node_array;\n }\n }\n\n}\n\n\nvar MAX_LEVEL = 20; // 整数: 0~30\nvar EMPTY_INDEX = 0; // 空ノードの索引\nvar FULL_INDEX = 4294967295; // 全域ノードの索引 = 2^32 - 1\n\n\nexport default EntityRegion;\n","import Entity from \"./Entity\";\n\n\n/**\n * @summary 4分木ベースの領域管理\n *\n * Entity.FlakePrimitiveProducer の getAreaStatus() と createMesh()\n * メソッドを補助するためのクラスである。
\n *\n * @memberof mapray\n * @private\n * @see mapray.Entity.FlakePrimitiveProducer\n */\nclass QAreaManager {\n\n /**\n */\n constructor()\n {\n this._tree_root = null; // QAreaNode | AreaStatus.EMPTY | AreaStatus.FULL\n }\n\n\n /**\n * @summary 領域状態を取得\n *\n * @desc\n * area が示す領域の状態を取得する。
\n *\n * @param {mapray.Area} area 確認する領域\n *\n * @return {mapray.Entity.AreaStatus} 領域の状態\n */\n getAreaStatus( area )\n {\n let node = this._get_area_node( area );\n\n if ( node === Entity.AreaStatus.EMPTY || node === Entity.AreaStatus.FULL ) {\n // Entity.AreaStatus のとき\n return node;\n }\n else {\n // QAreaNode のとき\n return Entity.AreaStatus.PARTIAL;\n }\n }\n\n\n /**\n * @summary 内容データを取得\n *\n * @param {mapray.Area} area 対象領域\n *\n * @return {object|mapray.Entity.AreaStatus} area に対応する内容データ | AreaStatus.EMPTY | AreaStatus.FULL\n */\n getAreaContent( area )\n {\n let node = this._get_area_node( area );\n\n if ( node === Entity.AreaStatus.EMPTY || node === Entity.AreaStatus.FULL ) {\n // Entity.AreaStatus のとき\n return node;\n }\n else {\n // QAreaNode のとき\n return node.content;\n }\n }\n\n\n /**\n * @summary 初めの内容データを取得\n *\n * @desc\n * 最上位領域の内容データを生成するための内容データを取得する。
\n * FlakePrimitiveProducer の実装者がこのメソッドを実装する。
\n *\n * @return {object} 内容データ\n *\n * @abstract\n */\n getInitialContent()\n {\n return null;\n }\n\n\n /**\n * @summary 領域の内容データを生成\n *\n * @desc\n * 領域と parent_content から内容データを生成する。
\n * パラメータの座標系は正規化メルカトル座標系である。
\n * FlakePrimitiveProducer の実装者がこのメソッドを実装する。
\n *\n * @param {number} min_x 領域の最小 x 座標\n * @param {number} min_y 領域の最小 y 座標\n * @param {number} msize 領域の寸法\n * @param {object} parent_content 親領域の内容データ\n *\n * @return {object|mapray.Entity.AreaStatus} 内容データ | AreaStatus.EMPTY | AreaStatus.FULL\n *\n * @abstract\n */\n createAreaContent( min_x, min_y, msize, parent_content )\n {\n return Entity.AreaStatus.EMPTY;\n }\n\n\n /**\n * @summary 内容データが更新されたこと通知\n *\n * @desc\n * 内容データが更新されときに FlakePrimitiveProducer の実装者がこのメソッドを呼び出す。
\n */\n notifyForUpdateContent()\n {\n this._tree_root = null;\n }\n\n\n /**\n * @summary 領域のノードを生成\n *\n * @param {number} min_x 領域の最小 x 座標\n * @param {number} max_y 領域の最大 y 座標\n * @param {number} msize 領域の寸法\n * @param {object} parent_content 親領域の内容データ\n *\n * @return {QAreaNode|mapray.Entity.AreaStatus} ノード | AreaStatus.EMPTY | AreaStatus.FULL\n *\n * @private\n */\n _create_area_node( min_x, max_y, msize, parent_content )\n {\n let content = this.createAreaContent( min_x, max_y - msize, msize, parent_content );\n\n if ( content === Entity.AreaStatus.EMPTY || content === Entity.AreaStatus.FULL ) {\n return content;\n }\n else {\n return new QAreaNode( content );\n }\n }\n\n\n /**\n * @summary 領域のノードを取得\n *\n * @desc\n * area に対応するノードを取得する。
\n *\n * @param {mapray.Area} area 領域\n *\n * @return {QAreaNode|mapray.Entity.AreaStatus} area に対応するノード | AreaStatus.EMPTY | AreaStatus.FULL\n *\n * @private\n */\n _get_area_node( area )\n {\n let msize = 2;\n let min_x = -1;\n let max_y = 1;\n\n if ( this._tree_root === null ) {\n let content = this.getInitialContent();\n this._tree_root = this._create_area_node( min_x, max_y, msize, content );\n }\n\n let node = this._tree_root;\n\n let tsize = Math.round( Math.pow( 2, area.z ) ); // 現行レベルでの縦横タイル数\n let rx = area.x; // 現行レベルでのタイル x 座標\n let ry = area.y; // 現行レベルでのタイル y 座標\n\n while ( tsize != 1 && node !== Entity.AreaStatus.EMPTY && node !== Entity.AreaStatus.FULL ) {\n\n tsize /= 2;\n\n let u = (rx >= tsize) ? 1 : 0;\n let v = (ry >= tsize) ? 1 : 0;\n\n msize /= 2;\n min_x += u * msize;\n max_y -= v * msize;\n\n let index = u + 2*v;\n let child = node.children[index];\n\n if ( child === null ) {\n // 子ノードを生成して node に設定\n child = this._create_area_node( min_x, max_y, msize, node.content );\n node.children[index] = child;\n }\n\n rx -= u * tsize;\n ry -= v * tsize;\n node = child;\n }\n\n return node;\n }\n\n}\n\n\n/**\n * @summary QAreaManager が管理するノード\n *\n * @memberof mapray.QAreaManager\n * @private\n */\nclass QAreaNode {\n\n /**\n * @param {object} content 内容データ\n */\n constructor( content )\n {\n this.children = [null, null, null, null]; // QAreaNode | AreaStatus.EMPTY | AreaStatus.FULL\n this.content = content;\n }\n\n}\n\n\nexport default QAreaManager;\n","import Entity from \"./Entity\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport LineMaterial from \"./LineMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport GeoRegion from \"./GeoRegion\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport AreaUtil from \"./AreaUtil\";\nimport QAreaManager from \"./QAreaManager\";\nimport Type from \"./animation/Type\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary 線エンティティ\n *\n * @classdesc\n * {@link mapray.MarkerLineEntity} と {@link mapray.PathEntity} の共通機能を\n * 提供するクラスである。
\n *\n * @memberof mapray\n * @extends mapray.Entity\n * @abstract\n * @protected\n */\nclass AbstractLineEntity extends Entity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {mapray.AbstractLineEntity.LineType} line_type クラス種別\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, line_type, opts )\n {\n super( scene, opts );\n \n this._line_type = line_type;\n\n if ( this.altitude_mode === AltitudeMode.CLAMP ) {\n this._producer = new FlakePrimitiveProducer( this );\n this._is_flake_mode = true;\n }\n else {\n this._producer = new PrimitiveProducer( this );\n this._is_flake_mode = false;\n }\n }\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return (!this._is_flake_mode) ? this._producer : null;\n }\n\n\n /**\n * @override\n */\n getFlakePrimitiveProducer()\n {\n return (this._is_flake_mode) ? this._producer : null;\n }\n\n\n /**\n * @override\n */\n onChangeAltitudeMode( prev_mode )\n {\n if ( this.altitude_mode === AltitudeMode.CLAMP ) {\n this._producer = new FlakePrimitiveProducer( this );\n this._is_flake_mode = true;\n }\n else {\n this._producer = new PrimitiveProducer( this );\n this._is_flake_mode = false;\n }\n }\n\n\n /**\n * @summary 線の太さを設定\n *\n * @param {number} width 線の太さ (画素単位)\n */\n setLineWidth( width )\n {\n if ( this._width !== width ) {\n this._width = width;\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 基本色を設定\n *\n * @param {mapray.Vector3} color 基本色\n */\n setColor( color )\n {\n if ( this._color[0] !== color[0] ||\n this._color[1] !== color[1] ||\n this._color[2] !== color[2] ) {\n // 位置が変更された\n GeoMath.copyVector3( color, this._color );\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 不透明度を設定\n *\n * @param {number} opacity 不透明度\n */\n setOpacity( opacity )\n {\n if ( this._opacity !== opacity ) {\n this._opacity = opacity;\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary すべての頂点のバウンディングを算出\n *\n * @override\n * @return {mapray.GeoRegion} バウンディング情報を持ったGeoRegion\n */\n getBounds()\n {\n const region = new GeoRegion();\n region.addPointsAsArray( this._point_array );\n return region;\n }\n\n\n /**\n * @summary 専用マテリアルを取得\n * @private\n */\n _getLineMaterial( render_target )\n {\n const scene = this.scene;\n const cache_id = (\n \"_AbstractLineEntity_material\" +\n (this._line_type === LineType.PATH ? \"_path\" : \"_markerline\") +\n (render_target === RenderTarget.RID ? \"_pick\" : \"\")\n );\n\n if ( !scene[cache_id] ) {\n // scene にマテリアルをキャッシュ\n const opt = { ridMaterial: render_target === RenderTarget.RID };\n scene[cache_id] = new LineMaterial( scene.glenv, this._line_type, opt );\n }\n\n return scene[cache_id];\n }\n}\n\n\n/**\n * @summary MarkerLineEntity の PrimitiveProducer\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n /**\n * @param {mapray.MarkerLineEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n // プリミティブの要素\n this._transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._pivot = GeoMath.createVector3();\n this._bbox = [GeoMath.createVector3(),\n GeoMath.createVector3()];\n\n this._properties = {\n width: 1.0,\n color: GeoMath.createVector3f(),\n opacity: 1.0\n };\n \n if ( entity._line_type == LineType.PATH ) {\n this._properties[\"lower_length\"] = 0.0;\n this._properties[\"upper_length\"] = 0.0;\n }\n\n // プリミティブ\n const material = entity._getLineMaterial( RenderTarget.SCENE );\n const primitive = new Primitive( entity.scene.glenv, null, material, this._transform );\n primitive.pivot = this._pivot;\n primitive.bbox = this._bbox;\n primitive.properties = this._properties;\n this._primitive = primitive;\n\n const pick_material = entity._getLineMaterial( RenderTarget.RID );\n const pickPrimitive = new Primitive( entity.scene.glenv, null, pick_material, this._transform );\n pickPrimitive.pivot = this._pivot;\n pickPrimitive.bbox = this._bbox;\n pickPrimitive.properties = this._properties;\n this._pickPrimitive = pickPrimitive;\n\n // プリミティブ配列\n this._primitives = [primitive];\n this._pickPrimitives = [pickPrimitive];\n\n this._geom_dirty = true;\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n let region = new EntityRegion();\n\n region.addPoints( this.entity._point_array, 0, 3, this._numPoints() );\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n this._geom_dirty = true;\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n if ( this._num_floats < 6 ) {\n // 2頂点未満は表示しない\n return [];\n }\n else {\n this._updatePrimitive();\n return stage.getRenderTarget() === RenderTarget.SCENE ? this._primitives : this._pickPrimitives;\n }\n }\n\n\n /**\n * @summary 頂点が変更されたことを通知\n */\n onChangePoints()\n {\n this.needToCreateRegions();\n this._geom_dirty = true;\n }\n\n\n /**\n * @summary プロパティが変更されたことを通知\n */\n onChangeProperty()\n {\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * \n * 条件: this._num_floats >= 6\n * 入力:\n * this._geom_dirty\n * this.entity._point_array\n * this.entity._num_floats\n * this.entity._width\n * this.entity._color\n * this.entity._opacity\n * this.entity._length_array\n * 出力:\n * this._transform\n * this._pivot\n * this._bbox\n * this._properties\n * this._primitive.mesh\n * this._geom_dirty\n *
\n *\n * @private\n */\n _updatePrimitive()\n {\n this._updateProperties();\n\n if ( !this._geom_dirty ) {\n // メッシュは更新する必要がない\n return;\n }\n\n let entity = this.entity;\n\n // GeoPoint 平坦化配列を GOCS 平坦化配列に変換\n var num_points = this._numPoints();\n var gocs_buffer = GeoPoint.toGocsArray( this._getFlatGeoPoints_with_Absolute(), num_points,\n new Float64Array( entity._num_floats ) );\n\n // プリミティブの更新\n // primitive.transform\n // primitive.pivot\n // primitive.bbox\n this._updateTransformPivotBBox( gocs_buffer, num_points );\n\n let add_length = (entity._line_type === LineType.PATH);\n let length_array = add_length ? entity._length_array : undefined;\n\n // メッシュ生成\n var mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_direction\", size: 3 },\n { name: \"a_where\", size: 2 }\n ],\n vertices: this._createVertices( gocs_buffer, num_points, length_array ),\n indices: this._createIndices()\n };\n\n if ( add_length ) {\n mesh_data.vtype.push( { name: \"a_length\", size: 1 } );\n }\n\n var mesh = new Mesh( entity.scene.glenv, mesh_data );\n\n // メッシュ設定\n // primitive.mesh\n var primitive = this._primitive;\n if ( primitive.mesh ) {\n primitive.mesh.dispose();\n }\n primitive.mesh = mesh;\n\n var pickPrimitive = this._pickPrimitive;\n if ( pickPrimitive.mesh ) {\n pickPrimitive.mesh.dispose();\n }\n pickPrimitive.mesh = mesh;\n\n // 更新終了\n this._geom_dirty = false;\n }\n\n\n /**\n * @summary プロパティを更新\n *\n * @desc\n * \n * 入力:\n * this.entity._width\n * this.entity._color\n * this.entity._opacity\n * this.entity._lower_length\n * this.entity._upper_length\n * 出力:\n * this._properties\n *
\n *\n * @private\n */\n _updateProperties()\n {\n let entity = this.entity;\n let props = this._properties;\n\n props.width = entity._width;\n GeoMath.copyVector3( entity._color, props.color );\n props.opacity = entity._opacity;\n props.lower_length = entity._lower_length;\n props.upper_length = entity._upper_length;\n }\n\n\n /**\n * @summary GeoPoint 平坦化配列を取得 (絶対高度)\n *\n * @return {number[]} GeoPoint 平坦化配列\n * @private\n */\n _getFlatGeoPoints_with_Absolute()\n {\n let entity = this.entity;\n let point_array = entity._point_array;\n let num_floats = entity._num_floats;\n\n var abs_buffer = null;\n\n switch ( entity.altitude_mode ) {\n case AltitudeMode.RELATIVE:\n var num_points = this._numPoints();\n abs_buffer = new Float64Array( num_floats );\n // abs_buffer[] の高度要素に現在の標高を設定\n entity.scene.viewer.getExistingElevations( num_points, point_array, 0, 3, abs_buffer, 2, 3 );\n // abs_buffer[] に経度要素と緯度要素を設定し、高度要素に絶対高度を設定\n for ( var i = 0; i < num_floats; i += 3 ) {\n abs_buffer[i ] = point_array[i ]; // 経度\n abs_buffer[i + 1] = point_array[i + 1]; // 緯度\n abs_buffer[i + 2] += point_array[i + 2]; // 絶対高度\n }\n break;\n\n default: // AltitudeMode.ABSOLUTE\n abs_buffer = point_array;\n break;\n }\n\n return abs_buffer;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * \n * 出力:\n * this._transform\n * this._pivot\n * this._bbox\n *
\n *\n * @param {Float64Array} gocs_buffer 入力頂点配列 (GOCS)\n * @param {number} num_points 入力頂点数\n * @private\n */\n _updateTransformPivotBBox( gocs_buffer, num_points )\n {\n // モデル座標系の原点 (GOCS)\n var ox = gocs_buffer[0];\n var oy = gocs_buffer[1];\n var oz = gocs_buffer[2];\n\n // 変換行列の更新\n var transform = this._transform;\n transform[12] = ox;\n transform[13] = oy;\n transform[14] = oz;\n\n // 統計\n var xsum = 0;\n var ysum = 0;\n var zsum = 0;\n\n var xmin = Number.MAX_VALUE;\n var ymin = Number.MAX_VALUE;\n var zmin = Number.MAX_VALUE;\n\n var xmax = -Number.MAX_VALUE;\n var ymax = -Number.MAX_VALUE;\n var zmax = -Number.MAX_VALUE;\n\n for ( var i = 0; i < num_points; ++i ) {\n var b = 3 * i;\n var x = gocs_buffer[b] - ox;\n var y = gocs_buffer[b + 1] - oy;\n var z = gocs_buffer[b + 2] - oz;\n\n xsum += x;\n ysum += y;\n zsum += z;\n\n if ( x < xmin ) { xmin = x; }\n if ( y < ymin ) { ymin = y; }\n if ( z < zmin ) { zmin = z; }\n\n if ( x > xmax ) { xmax = x; }\n if ( y > ymax ) { ymax = y; }\n if ( z > zmax ) { zmax = z; }\n }\n\n // 中心点\n var pivot = this._pivot;\n pivot[0] = xsum / num_points;\n pivot[1] = ysum / num_points;\n pivot[2] = zsum / num_points;\n\n // 境界箱\n var bbox = this._bbox;\n var bmin = bbox[0];\n var bmax = bbox[1];\n bmin[0] = xmin;\n bmin[1] = ymin;\n bmin[2] = zmin;\n bmax[0] = xmax;\n bmax[1] = ymax;\n bmax[2] = zmax;\n }\n\n\n /**\n * @summary 頂点配列の生成\n *\n * @param {Float64Array} gocs_buffer 入力頂点配列 (GOCS)\n * @param {number} num_points 入力頂点数\n * @return {Float32Array} Mesh 用の頂点配列\n *\n * @private\n */\n _createVertices( gocs_buffer, num_points, length_array = undefined )\n {\n // 頂点の距離を追加するか\n var add_length = (length_array !== undefined);\n\n // モデル座標系の原点 (GOCS)\n var ox = gocs_buffer[0];\n var oy = gocs_buffer[1];\n var oz = gocs_buffer[2];\n\n var num_segments = num_points - 1;\n var num_vertices = 4 * num_segments;\n var vertices = new Float32Array( (add_length ? 9 : 8) * num_vertices );\n\n for ( var i = 0; i < num_segments; ++i ) {\n var b = 3 * i;\n var sx = gocs_buffer[b] - ox;\n var sy = gocs_buffer[b + 1] - oy;\n var sz = gocs_buffer[b + 2] - oz;\n var ex = gocs_buffer[b + 3] - ox;\n var ey = gocs_buffer[b + 4] - oy;\n var ez = gocs_buffer[b + 5] - oz;\n var dx = gocs_buffer[b + 3] - gocs_buffer[b];\n var dy = gocs_buffer[b + 4] - gocs_buffer[b + 1];\n var dz = gocs_buffer[b + 5] - gocs_buffer[b + 2];\n var v = (add_length ? 36 : 32) * i;\n\n // 始左、始右、終左、終右のループ\n for ( var j = 0; j < 4; ++j ) {\n var start = j < 2;\n var id = v + j * ( add_length ? 9 : 8 );\n\n vertices[id] = start ? sx : ex; // a_position.x\n vertices[id + 1] = start ? sy : ey; // a_position.y\n vertices[id + 2] = start ? sz : ez; // a_position.z\n vertices[id + 3] = dx; // a_direction.x\n vertices[id + 4] = dy; // a_direction.y\n vertices[id + 5] = dz; // a_direction.z\n\n switch ( j ) {\n case 0:\n vertices[id + 6] = -1; // a_where.x\n vertices[id + 7] = 1; // a_where.y\n break;\n case 1:\n vertices[id + 6] = -1; // a_where.x\n vertices[id + 7] = -1; // a_where.y\n break;\n case 2:\n vertices[id + 6] = 1; // a_where.x\n vertices[id + 7] = 1; // a_where.y\n break;\n case 3:\n vertices[id + 6] = 1; // a_where.x\n vertices[id + 7] = -1; // a_where.y\n break;\n }\n\n if ( add_length ) {\n vertices[id + 8] = length_array[start ? i : i + 1];\n }\n }\n }\n\n return vertices;\n }\n\n\n /**\n * @summary 頂点インデックスの生成\n *\n * @desc\n * \n * 条件: this.entity._num_floats >= 6\n * 入力: this.entity._num_floats\n *
\n *\n * @return {Uint32Array} インデックス配列\n *\n * @private\n */\n _createIndices()\n {\n var num_points = this._numPoints();\n var num_segments = num_points - 1;\n var num_indices = 6 * num_segments;\n var indices = new Uint32Array( num_indices );\n\n for ( var i = 0; i < num_segments; ++i ) {\n var base_d = 6 * i;\n var base_s = 4 * i;\n indices[base_d] = base_s;\n indices[base_d + 1] = base_s + 1;\n indices[base_d + 2] = base_s + 2;\n indices[base_d + 3] = base_s + 2;\n indices[base_d + 4] = base_s + 1;\n indices[base_d + 5] = base_s + 3;\n }\n\n return indices;\n }\n\n\n /**\n * @summary 頂点数を取得\n *\n * @return {number} 頂点数\n *\n * @private\n */\n _numPoints()\n {\n return Math.floor( this.entity._num_floats / 3 );\n }\n\n}\n\n\n/**\n * @summary MarkerLineEntity の FlakePrimitiveProducer\n *\n * @private\n */\nclass FlakePrimitiveProducer extends Entity.FlakePrimitiveProducer {\n\n /**\n * @param {mapray.MarkerLineEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._material_map = Object.keys(RenderTarget).reduce((map, key) => {\n const render_target = RenderTarget[key];\n map.set( render_target, entity._getLineMaterial( render_target ) );\n return map;\n }, new Map());\n this._properties = null;\n this._area_manager = new LineAreaManager( entity );\n }\n\n\n /**\n * @override\n */\n getAreaStatus( area )\n {\n return this._area_manager.getAreaStatus( area );\n }\n\n\n /**\n * @override\n */\n createMesh( area, dpows, dem )\n {\n let segments = this._divideXY( area, dpows );\n if ( segments.length == 0 ) {\n return null;\n }\n\n let add_length = (this.entity._line_type === LineType.PATH);\n\n // メッシュ生成\n let mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_direction\", size: 3 },\n { name: \"a_where\", size: 2 }\n ],\n vertices: this._createVertices( area, dem, segments, add_length ),\n indices: this._createIndices( segments.length )\n };\n\n if ( add_length ) {\n mesh_data.vtype.push( { name: \"a_length\", size: 1 } );\n }\n\n return new Mesh( this.entity.scene.glenv, mesh_data );\n }\n\n\n /**\n * @override\n */\n getMaterialAndProperties( stage )\n {\n if ( this._properties === null ) {\n let entity = this.entity;\n this._properties = {\n width: entity._width,\n color: GeoMath.createVector3f( entity._color ),\n opacity: entity._opacity\n };\n\n if ( entity._line_type == LineType.PATH ) {\n this._properties[\"lower_length\"] = entity._lower_length;\n this._properties[\"upper_length\"] = entity._upper_length;\n }\n }\n\n return {\n material: this._material_map.get( stage.getRenderTarget() ),\n properties: this._properties\n };\n }\n\n\n /**\n * @summary 頂点が変更されたことを通知\n */\n onChangePoints()\n {\n this._area_manager.notifyForUpdateContent();\n this.notifyForUpdate();\n }\n\n\n /**\n * @summary プロパティが変更されたことを通知\n */\n onChangeProperty()\n {\n this._properties = null;\n }\n\n\n /**\n * @summary すべての線分を垂直グリッドで分割\n *\n * @param {mapray.Area} area 地表断片の領域\n * @param {number} msize area 寸法 ÷ π (厳密値)\n * @param {number} dpow area の x 分割指数\n *\n * @private\n */\n _divideXOnly( area, msize, dpow )\n {\n let x_min = Math.PI * (area.x * msize - 1);\n let x_max = Math.PI * ((area.x + 1) * msize - 1);\n\n let div_x = 1 << dpow; // 横分割数: 2^dpow\n let step_x = (x_max - x_min) / div_x; // 横分割間隔\n\n let segments = [];\n\n // 垂直グリッド線で分割\n for ( let [px, py, pl, qx, qy, ql] of this._area_manager.getAreaContent( area ) ) {\n\n let [x0, y0, l0, x1, y1, l1] = (px <= qx) ? [px, py, pl, qx, qy, ql] : [qx, qy, ql, px, py, pl];\n // assert: x0 <= x1\n\n if ( x1 < x_min || x0 >= x_max ) {\n // 線分の x 座標が area の範囲外\n continue;\n }\n\n if ( x0 == x1 ) {\n // 垂直線分なので、垂直グリッド線で分割しない\n segments.push( [x0, y0, l0, x1, y1, l1] );\n continue;\n }\n\n // 左端でトリミング\n let tx0 = x0;\n let ty0 = y0;\n let tl0 = l0;\n if ( x0 < x_min ) {\n let mu1 = (x_min - x0) / (x1 - x0);\n let mu0 = 1 - mu1;\n tx0 = x_min;\n ty0 = mu0*y0 + mu1*y1; // 左端線と線分の交点の y 座標\n tl0 = mu0*l0 + mu1*l1;\n }\n\n // 右端でトリミング\n let tx1 = x1;\n let ty1 = y1;\n let tl1 = l1;\n if ( x1 > x_max ) {\n let mu1 = (x_max - x0) / (x1 - x0);\n let mu0 = 1 - mu1;\n tx1 = x_max;\n ty1 = mu0*y0 + mu1*y1; // 右端線と線分の交点の y 座標\n tl1 = mu0*l0 + mu1*l1;\n }\n\n // グリッド線の範囲\n let i_min = Math.max( Math.ceil( (x0 - x_min) / step_x ), 1 );\n let i_max = Math.min( Math.floor( (x1 - x_min) / step_x ), div_x - 1 );\n\n let prev_x = tx0;\n let prev_y = ty0;\n let prev_l = tl0;\n\n for ( let i = i_min; i <= i_max; ++i ) {\n let next_x = x_min + step_x * i; // 垂直グリッド線の x 座標\n\n let mu1 = (next_x - x0) / (x1 - x0);\n let mu0 = 1 - mu1;\n\n let next_y = mu0*y0 + mu1*y1; // 垂直グリッド線と線分の交点の y 座標\n let next_l = mu0*l0 + mu1*l1;\n\n if ( prev_x != next_x || prev_y != next_y ) {\n segments.push( [prev_x, prev_y, prev_l, next_x, next_y, next_l] );\n }\n\n prev_x = next_x;\n prev_y = next_y;\n prev_l = next_l;\n }\n\n if ( prev_x != tx1 || prev_y != ty1 ) {\n segments.push( [prev_x, prev_y, prev_l, tx1, ty1, tl1] );\n }\n }\n\n return segments;\n }\n\n\n /**\n * @summary すべての線分をグリッドで分割\n *\n * @param {mapray.Area} area 地表断片の領域\n * @param {number[]} dpows area の xy 分割指数\n *\n * @private\n */\n _divideXY( area, dpows )\n {\n // area 寸法 ÷ π (厳密値)\n // 線分の場合、領域の端によるクリッピングがシビアなので厳密値 (2^整数) を使う\n let msize = 2 / Math.round( Math.pow( 2, area.z ) );\n\n // area の y 座標の範囲\n let y_min = Math.PI * (1 - (area.y + 1) * msize);\n let y_max = Math.PI * (1 - area.y * msize);\n\n let div_y = 1 << dpows[1]; // 縦分割数: 2^dpow\n let step_y = (y_max - y_min) / div_y; // 縦分割間隔\n\n let segments = [];\n\n // 水平グリッド線で分割\n for ( let [px, py, pl, qx, qy, ql] of this._divideXOnly( area, msize, dpows[0] ) ) {\n\n let [x0, y0, l0, x1, y1, l1] = (py <= qy) ? [px, py, pl, qx, qy, ql] : [qx, qy, ql, px, py, pl];\n // assert: y0 <= y1\n\n if ( y1 < y_min || y0 >= y_max ) {\n // 線分の y 座標が area の範囲外\n continue;\n }\n\n if ( y0 == y1 ) {\n // 水平線分なので、水平グリッド線で分割しない\n segments.push( [x0, y0, l0, x1, y1, l1] );\n continue;\n }\n\n // 下端でトリミング\n let tx0 = x0;\n let ty0 = y0;\n let tl0 = l0;\n if ( y0 < y_min ) {\n let mu1 = (y_min - y0) / (y1 - y0);\n let mu0 = 1 - mu1;\n tx0 = mu0*x0 + mu1*x1; // 下端線と線分の交点の x 座標\n ty0 = y_min;\n tl0 = mu0*l0 + mu1*l1;\n }\n\n // 上端でトリミング\n let tx1 = x1;\n let ty1 = y1;\n let tl1 = l1;\n if ( y1 > y_max ) {\n let mu1 = (y_max - y0) / (y1 - y0);\n let mu0 = 1 - mu1;\n tx1 = mu0*x0 + mu1*x1; // 上端線と線分の交点の x 座標\n ty1 = y_max;\n tl1 = mu0*l0 + mu1*l1;\n }\n\n // グリッド線の範囲\n let i_min = Math.max( Math.ceil( (y0 - y_min) / step_y ), 1 );\n let i_max = Math.min( Math.floor( (y1 - y_min) / step_y ), div_y - 1 );\n\n let prev_x = tx0;\n let prev_y = ty0;\n let prev_l = tl0;\n\n for ( let i = i_min; i <= i_max; ++i ) {\n let next_y = y_min + step_y * i; // 水平グリッド線の y 座標\n\n let mu1 = (next_y - y0) / (y1 - y0);\n let mu0 = 1 - mu1;\n\n let next_x = mu0*x0 + mu1*x1; // 水平グリッド線と線分の交点の x 座標\n let next_l = mu0*l0 + mu1*l1;\n\n if ( prev_x != next_x || prev_y != next_y ) {\n segments.push( [prev_x, prev_y, prev_l, next_x, next_y, next_l] );\n }\n\n prev_x = next_x;\n prev_y = next_y;\n prev_l = next_l;\n }\n\n if ( prev_x != tx1 || prev_y != ty1 ) {\n segments.push( [prev_x, prev_y, prev_l, tx1, ty1, tl1] );\n }\n }\n\n return segments;\n }\n\n\n /**\n * @summary 頂点配列の生成\n *\n * @param {mapray.Area} area 地表断片の領域\n * @param {mapray.DemBinary} dem DEM バイナリ\n *\n * @return {Float32Array} Mesh 用の頂点配列\n *\n * @private\n */\n _createVertices( area, dem, segments, add_length = false )\n {\n let sampler = dem.newLinearSampler();\n let [ox, oy, oz] = AreaUtil.getCenter( area, GeoMath.createVector3() );\n\n let num_segments = segments.length;\n let num_vertices = 4 * num_segments;\n let vertices = new Float32Array( (add_length ? 9 : 8) * num_vertices );\n\n for ( let i = 0; i < num_segments; ++i ) {\n let [smx, smy, prev_length, emx, emy, next_length] = segments[i];\n\n let [sgx, sgy, sgz] = toGocs( smx, smy, sampler );\n let [egx, egy, egz] = toGocs( emx, emy, sampler );\n\n let sx = sgx - ox;\n let sy = sgy - oy;\n let sz = sgz - oz;\n\n let ex = egx - ox;\n let ey = egy - oy;\n let ez = egz - oz;\n\n let dx = egx - sgx;\n let dy = egy - sgy;\n let dz = egz - sgz;\n\n let v = (add_length ? 36 : 32) * i;\n\n // 始左、始右、終左、終右のループ\n for ( var j = 0; j < 4; ++j ) {\n var start = j < 2;\n var id = v + j * ( add_length ? 9 : 8 );\n\n vertices[id] = start ? sx : ex; // a_position.x\n vertices[id + 1] = start ? sy : ey; // a_position.y\n vertices[id + 2] = start ? sz : ez; // a_position.z\n vertices[id + 3] = dx; // a_direction.x\n vertices[id + 4] = dy; // a_direction.y\n vertices[id + 5] = dz; // a_direction.z\n \n switch ( j ) {\n case 0:\n vertices[id + 6] = -1; // a_where.x\n vertices[id + 7] = 1; // a_where.y\n break;\n case 1:\n vertices[id + 6] = -1; // a_where.x\n vertices[id + 7] = -1; // a_where.y\n break;\n case 2:\n vertices[id + 6] = 1; // a_where.x\n vertices[id + 7] = 1; // a_where.y\n break;\n case 3:\n vertices[id + 6] = 1; // a_where.x\n vertices[id + 7] = -1; // a_where.y\n break;\n }\n\n if ( add_length ) {\n vertices[id + 8] = start ? prev_length : next_length;\n }\n }\n }\n\n return vertices;\n }\n\n\n /**\n * @summary @summary 頂点インデックスの生成\n *\n * @param {number} num_segments 線分の数\n *\n * @return {Uint32Array} Mesh 用の頂点インデックス\n *\n * @private\n */\n _createIndices( num_segments )\n {\n let num_indices = 6 * num_segments;\n let indices = new Uint32Array( num_indices );\n\n for ( let i = 0; i < num_segments; ++i ) {\n let base_d = 6 * i;\n let base_s = 4 * i;\n indices[base_d ] = base_s;\n indices[base_d + 1] = base_s + 1;\n indices[base_d + 2] = base_s + 2;\n indices[base_d + 3] = base_s + 2;\n indices[base_d + 4] = base_s + 1;\n indices[base_d + 5] = base_s + 3;\n }\n\n return indices;\n }\n\n}\n\n\n/**\n * @private\n */\nfunction\ntoGocs( x, y, sampler )\n{\n let λ = x;\n let φ = GeoMath.gudermannian( y );\n let r = GeoMath.EARTH_RADIUS + sampler.sample( x, y );\n\n let cosφ = Math.cos( φ );\n\n return [r * cosφ * Math.cos( λ ),\n r * cosφ * Math.sin( λ ),\n r * Math.sin( φ )];\n}\n\n\n/**\n * @summary 線分の領域管理\n *\n * @private\n */\nclass LineAreaManager extends QAreaManager {\n\n /**\n * @param {mapray.MarkerLineEntity} entity 管理対象のエンティティ\n */\n constructor( entity )\n {\n super();\n\n this._entity = entity;\n }\n\n\n /**\n * @override\n */\n getInitialContent()\n {\n const Degree = GeoMath.DEGREE;\n const RAngle = Math.PI / 2; // 直角\n const TwoPI = 2 * Math.PI; // 2π\n\n let segments = [];\n\n // 頂点データ\n let points = this._entity._point_array;\n let end_point = this._entity._num_floats;\n\n if ( end_point < 6 ) {\n // 線分なし\n return segments;\n }\n\n let is_path = (this._entity._line_type === LineType.PATH);\n let length_array = is_path ? this._entity._length_array : null;\n\n // 線分の始点 (ラジアン)\n let lon0 = points[0] * Degree;\n let lat0 = points[1] * Degree;\n let length0 = (is_path ? length_array[0] : 0);\n let lon1;\n let lat1;\n let length1;\n\n for ( let i = 3; i < end_point; i += 3, lon0 = lon1, lat0 = lat1, length0 = length1 ) {\n // 線分の終点 (ラジアン)\n lon1 = points[i ] * Degree;\n lat1 = points[i + 1] * Degree;\n length1 = (is_path ? length_array[i / 3] : 0);\n\n if ( lat0 <= -RAngle || lat0 >= RAngle ||\n lat1 <= -RAngle || lat1 >= RAngle ) {\n // 端点の緯度の絶対値が RAngle 以上の線分は除外\n // ※ まだ検討していないので、とりあえずの処置\n continue;\n }\n\n // 単位球メルカトル座標系に変換\n let x0 = lon0;\n let y0 = GeoMath.invGudermannian( lat0 );\n let l0 = length0;\n let x1 = lon1;\n let y1 = GeoMath.invGudermannian( lat1 );\n let l1 = length1;\n\n // 左端点と右端点\n let [xL, yL, lL, xR, yR, lR] = (x0 < x1) ? [x0, y0, l0, x1, y1, l1] : [x1, y1, l1, x0, y0, l0];\n\n // -π <= xL < π になるように xL を正規化\n if ( xL < -Math.PI || xL >= Math.PI ) {\n let dx = xR - xL;\n xL -= TwoPI * (Math.floor( (xL - Math.PI) / TwoPI ) + 1);\n if ( xL < -Math.PI || xL >= Math.PI ) {\n // 誤差対策\n xL = -Math.PI;\n }\n xR = xL + dx;\n }\n\n if ( xL == xR && yL == yR ) {\n // 長さ 0 の線分は除外\n continue;\n }\n\n // 線分を追加\n segments.push( [xL, yL, lL, xR, yR, lR] );\n\n if ( xR > Math.PI ) {\n // 線分が 180 度子午線をまたぐとき\n // こちらは多少厳密さを無視する\n segments.push( [xL - TwoPI, yL, lL, xR - TwoPI, yR, lR] );\n }\n }\n\n return segments;\n }\n\n\n /**\n * @override\n */\n createAreaContent( min_x, min_y, msize, parent_content )\n {\n // 単位球メルカトルでの領域に変換\n const x_area_min = Math.PI * min_x;\n const x_area_max = Math.PI * (min_x + msize);\n const y_area_min = Math.PI * min_y;\n const y_area_max = Math.PI * (min_y + msize);\n\n let segments = [];\n\n for ( let segment of parent_content ) {\n let [xP, yP, /*lP*/, xQ, yQ, /*lQ*/] = segment;\n if ( this._intersect( x_area_min, x_area_max, y_area_min, y_area_max, xP, yP, xQ, yQ ) ) {\n segments.push( segment );\n }\n }\n\n return (segments.length > 0) ? segments : Entity.AreaStatus.EMPTY;\n }\n\n\n /**\n * @summary 矩形と線分の交差判定\n *\n * @desc\n * 矩形領域と線分が交差するかどうかを返す。
\n * 矩形領域には x 座標が x_area_max の点と、y 座標が y_area_max の点は含まれないものとする。
\n *\n * \n * 事前条件:\n * x_area_min < x_area_max\n * y_area_min < y_area_max\n *
\n *\n * @param {number} x_area_min 矩形領域の最小 x 座標\n * @param {number} x_area_max 矩形領域の最大 x 座標\n * @param {number} y_area_min 矩形領域の最小 y 座標\n * @param {number} y_area_max 矩形領域の最大 y 座標\n * @param {number} xP 線分端点 P の x 座標\n * @param {number} yP 線分端点 P の y 座標\n * @param {number} xQ 線分端点 Q の x 座標\n * @param {number} yQ 線分端点 Q の y 座標\n *\n * @return {boolean} 交差するとき true, それ以外のとき false\n *\n * @private\n */\n _intersect( x_area_min, x_area_max, y_area_min, y_area_max, xP, yP, xQ, yQ )\n {\n if ( Math.abs( xP - xQ ) < Math.abs( yP - yQ ) ) {\n // 線分が垂直に近いとき\n return this._nhorz_intersect( x_area_min, x_area_max, y_area_min, y_area_max, xP, yP, xQ, yQ );\n }\n else {\n // 線分が水平に近いとき\n return this._nhorz_intersect( y_area_min, y_area_max, x_area_min, x_area_max, yP, xP, yQ, xQ );\n }\n }\n\n\n /**\n * @summary 矩形と非水平線分の交差判定\n *\n * @desc\n * 矩形領域と線分が交差するかどうかを返す。
\n * 矩形領域には x 座標が x_area_max の点と、y 座標が y_area_max の点は含まれないものとする。
\n *\n * \n * 事前条件:\n * x_area_min < x_area_max\n * y_area_min < y_area_max\n * yP != yQ\n *
\n *\n * 注意: |yP - yQ| が小さいと精度が悪くなる。
\n *\n * @param {number} x_area_min 矩形領域の最小 x 座標\n * @param {number} x_area_max 矩形領域の最大 x 座標\n * @param {number} y_area_min 矩形領域の最小 y 座標\n * @param {number} y_area_max 矩形領域の最大 y 座標\n * @param {number} xP 線分端点 P の x 座標\n * @param {number} yP 線分端点 P の y 座標\n * @param {number} xQ 線分端点 Q の x 座標\n * @param {number} yQ 線分端点 Q の y 座標\n *\n * @return {boolean} 交差するとき true, それ以外のとき false\n *\n * @private\n */\n _nhorz_intersect( x_area_min, x_area_max, y_area_min, y_area_max, xP, yP, xQ, yQ )\n {\n // 線分の y 座標の範囲\n let [y_line_min, y_line_max] = (yP < yQ) ? [yP, yQ] : [yQ, yP];\n\n if ( y_line_min >= y_area_max || y_line_max < y_area_min ) {\n // 線分の y 範囲が矩形領域の y 範囲の外側なので交差しない\n return false;\n }\n\n // 矩形領域と線分の y 座標が重なる範囲 (順不同)\n let y_range_0 = (y_area_min >= y_line_max) ? y_area_min : y_line_max;\n let y_range_1 = (y_area_max <= y_line_min) ? y_area_max : y_line_min;\n\n // y が {y_range_0, y_range_1} 範囲での線分の x 範囲 (順不同)\n let x_range_0 = xP + (xQ - xP) * (y_range_0 - yP) / (yQ - yP);\n let x_range_1 = xP + (xQ - xP) * (y_range_1 - yP) / (yQ - yP);\n\n // y が {y_range_0, y_range_1} 範囲での線分の x 範囲\n let [x_range_min, x_range_max] = (x_range_0 < x_range_1) ? [x_range_0, x_range_1] : [x_range_1, x_range_0];\n\n // [x_range_min, x_range_max] 範囲は矩形領域の x の範囲と重なるか?\n return (x_range_min < x_area_max) && (x_range_max >= x_area_min);\n }\n\n}\n\n\n /**\n * @summary エンティティの種類の列挙型\n * @enum {object}\n * @memberof mapray.AbstractLineEntity\n * @constant\n * @see mapray.AbstractLineEntity#line_type\n */\nvar LineType = {\n\n /**\n * MarkerLineEntity\n */\n MARKERLINE: { id: \"MARKERLINE\" },\n\n\n /**\n * PathEntity\n */\n PATH: { id: \"PATH\" }\n};\n\nAbstractLineEntity.LineType = LineType;\n\n\nexport default AbstractLineEntity;\n","import GeoMath from \"./GeoMath\";\nimport Type from \"./animation/Type\";\nimport AbstractLineEntity from \"./AbstractLineEntity\";\n\n\n/**\n * @summary 太さ付き連続線エンティティ\n * @memberof mapray\n * @extends mapray.AbstractLineEntity\n */\nclass MarkerLineEntity extends AbstractLineEntity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, AbstractLineEntity.LineType.MARKERLINE, opts );\n\n this._point_array = new Float64Array( 0 );\n this._num_floats = 0;\n\n this._width = 1.0;\n this._color = GeoMath.createVector3( [1.0, 1.0, 1.0] );\n this._opacity = 1.0;\n\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector3 = Type.find( \"vector3\" );\n\n // パラメータ名: width\n // パラメータ型: number\n // 線の太さ\n block.addEntry( \"width\", [number], null, value => {\n this.setLineWidth( value );\n } );\n \n // パラメータ名: color\n // パラメータ型: vector3\n // 色\n block.addEntry( \"color\", [vector3], null, value => {\n this.setColor( value );\n } );\n \n // パラメータ名: opacity\n // パラメータ型: number\n // 不透明度\n block.addEntry( \"opacity\", [number], null, value => {\n this.setOpacity( value );\n } ); \n }\n\n\n /**\n * @summary 複数の頂点を追加\n *\n * @desc\n * points は [lon_0, lat_0, alt_0, lon_1, lat_1, alt_1, ...] のような形式の配列を与える。
\n *\n * @param {number[]} points 頂点の配列\n */\n addPoints( points )\n {\n var add_size = points.length;\n if ( add_size == 0 ) {\n // 追加頂点が無いので変化なし\n return;\n }\n\n // バッファを拡張\n var target_size = this._num_floats + add_size;\n var buffer_size = this._point_array.length;\n if ( target_size > buffer_size ) {\n var new_buffer = new Float64Array( Math.max( target_size, 2 * buffer_size ) );\n var old_buffer = this._point_array;\n var copy_size = this._num_floats;\n for ( var i = 0; i < copy_size; ++i ) {\n new_buffer[i] = old_buffer[i];\n }\n this._point_array = new_buffer;\n }\n\n // 頂点追加処理\n var buffer = this._point_array;\n var base = this._num_floats;\n for ( var j = 0; j < add_size; ++j ) {\n buffer[base + j] = points[j];\n }\n this._num_floats = target_size;\n\n // 形状が変化した可能性がある\n this._producer.onChangePoints();\n }\n\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n // json.points\n this.addPoints( json.points );\n\n // json.line_width\n // .color\n // .opacity\n if ( json.line_width !== undefined ) this.setLineWidth( json.line_width );\n if ( json.color !== undefined ) this.setColor( json.color );\n if ( json.opacity !== undefined ) this.setOpacity( json.opacity );\n }\n\n}\n\n\nexport default MarkerLineEntity;\n","import GeoMath from \"./GeoMath\";\nimport Type from \"./animation/Type\";\nimport AbstractLineEntity from \"./AbstractLineEntity\";\n\n\n/**\n * @summary 太さ付き連続線エンティティ\n * @memberof mapray\n * @extends mapray.AbstractLineEntity\n */\nclass PathEntity extends AbstractLineEntity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, AbstractLineEntity.LineType.PATH, opts );\n\n this._point_array = new Float64Array( 0 );\n this._num_floats = 0;\n this._length_array = new Float64Array( 0 );\n\n this._width = 1.0;\n this._color = GeoMath.createVector3( [1.0, 1.0, 1.0] );\n this._opacity = 1.0;\n this._lower_length = 0;\n this._upper_length = 0;\n\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector3 = Type.find( \"vector3\" );\n\n // パラメータ名: width\n // パラメータ型: number\n // 線の太さ\n block.addEntry( \"width\", [number], null, value => {\n this.setLineWidth( value );\n } );\n \n // パラメータ名: color\n // パラメータ型: vector3\n // 色\n block.addEntry( \"color\", [vector3], null, value => {\n this.setColor( value );\n } );\n \n // パラメータ名: opacity\n // パラメータ型: number\n // 不透明度\n block.addEntry( \"opacity\", [number], null, value => {\n this.setOpacity( value );\n } );\n \n // パラメータ名: lower_length\n // パラメータ型: number\n // 距離の下限値\n block.addEntry( \"lower_length\", [number], null, value => {\n this.setLowerLength( value );\n } );\n\n // パラメータ名: upper_length\n // パラメータ型: number\n // 距離の上限値\n block.addEntry( \"upper_length\", [number], null, value => {\n this.setUpperLength( value );\n } );\n }\n\n\n /**\n * @summary 距離の下限値を設定\n *\n * @param {number} lower_length 距離の下限値\n */\n setLowerLength( lower_length )\n { \n if ( this._lower_length !== lower_length ) {\n this._lower_length = lower_length;\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 距離の上限値を設定\n *\n * @param {number} upper_length 距離の上限値\n */\n setUpperLength( upper_length )\n { \n if ( this._upper_length !== upper_length ) {\n this._upper_length = upper_length;\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 複数の頂点を追加\n *\n * @desc\n * points は [lon_0, lat_0, alt_0, lon_1, lat_1, alt_1, ...] のような形式の配列を与える。
\n *\n * @param {number[]} points 頂点の配列\n * @param {number[]} length_array 始点からの距離の配列\n */\n addPoints( points, length_array )\n {\n let add_size = points.length;\n let add_length_size = length_array.length;\n if ( add_size == 0 || add_length_size == 0) {\n // 追加頂点が無いので変化なし\n return;\n }\n\n let num_length_floats = this._num_floats / 3;\n\n // バッファを拡張\n let target_size = this._num_floats + add_size;\n let buffer_size = this._point_array.length;\n if ( target_size > buffer_size ) {\n let new_buffer = new Float64Array( Math.max( target_size, 2 * buffer_size ) );\n let old_buffer = this._point_array;\n let copy_size = this._num_floats;\n for ( let i = 0; i < copy_size; ++i ) {\n new_buffer[i] = old_buffer[i];\n }\n this._point_array = new_buffer;\n }\n\n // 距離配列バッファを拡張\n let target_length_size = num_length_floats + add_length_size;\n let buffer_length_size = this._length_array.length;\n if ( target_length_size > buffer_length_size ) {\n let new_buffer = new Float64Array( Math.max( target_length_size, 2 * buffer_length_size ) );\n let old_buffer = this._length_array;\n let copy_size = num_length_floats;\n for ( let i = 0; i < copy_size; ++i ) {\n new_buffer[i] = old_buffer[i];\n }\n this._length_array = new_buffer;\n }\n\n // 頂点追加処理\n let buffer = this._point_array;\n let base = this._num_floats;\n for ( let i = 0; i < points.length; ++i ) {\n buffer[base + i] = points[i];\n }\n\n // 距離の配列を追加\n let buffer_length = this._length_array;\n let base_length = num_length_floats;\n for ( let i = 0; i < length_array.length; ++i ) {\n buffer_length[base_length + i] = length_array[i];\n }\n\n this._num_floats = target_size;\n\n // 形状が変化した可能性がある\n this._producer.onChangePoints();\n }\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n // json.points\n this.addPoints( json.points.positions, json.points.lengths );\n\n // json.line_width\n // .color\n // .opacity\n // .lower_length\n // .upper_length\n if ( json.line_width !== undefined ) this.setLineWidth( json.line_width );\n if ( json.color !== undefined ) this.setColor( json.color );\n if ( json.opacity !== undefined ) this.setOpacity( json.opacity );\n if ( json.lower_length !== undefined ) this.setLowerLength( json.lower_length );\n if ( json.upper_length !== undefined ) this.setUpperLength( json.upper_length );\n }\n\n}\n\nexport default PathEntity;\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport text_vs_code from \"./shader/text.vert\";\nimport text_fs_code from \"./shader/text.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary テキストマテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n * @see mapray.TextEntity\n */\nclass TextMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, options = {} )\n {\n super( glenv, text_vs_code, options.ridMaterial ? rid_fs_code : text_fs_code );\n\n // 不変パラメータを事前設定\n this.bindProgram();\n this.setInteger( \"u_image\", TextMaterial.TEXUNIT_IMAGE );\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n var props = primitive.properties;\n // If drawing background color, alpha is disable.\n return !props.enable_bg;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // mat4 u_obj_to_clip\n this.setObjToClip( stage, primitive );\n\n // 画面パラメータ: {2/w, 2/h}\n // vec2 u_sparam\n var sparam = TextMaterial._sparam;\n sparam[0] = 2 / stage._width;\n sparam[1] = 2 / stage._height;\n this.setVector2( \"u_sparam\", sparam );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // テクスチャのバインド\n // sampler2D u_image\n var image_tex = props[\"image\"];\n this.bindTexture2D( TextMaterial.TEXUNIT_IMAGE, image_tex.handle );\n }\n }\n\n}\n\n\n// クラス定数の定義\n{\n TextMaterial.TEXUNIT_IMAGE = 0; // 画像のテクスチャユニット\n\n // 計算用一時領域\n TextMaterial._sparam = GeoMath.createVector2f();\n}\n\n\nexport default TextMaterial;\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport text_vs_code from \"./shader/simple_text.vert\";\nimport text_fs_code from \"./shader/simple_text.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary テキストマテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n * @see mapray.TextEntity\n */\nclass SimpleTextMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, options = {} )\n {\n super( glenv, text_vs_code, options.ridMaterial ? rid_fs_code : text_fs_code );\n\n // 不変パラメータを事前設定\n this.bindProgram();\n this.setInteger( \"u_image\", SimpleTextMaterial.TEXUNIT_IMAGE );\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n // アンチエイリアス用のブレンドのため常に半透明\n return true;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // mat4 u_obj_to_clip\n this.setObjToClip( stage, primitive );\n\n // 画面パラメータ: {2/w, 2/h}\n // vec2 u_sparam\n var sparam = SimpleTextMaterial._sparam;\n sparam[0] = 2 / stage._width;\n sparam[1] = 2 / stage._height;\n this.setVector2( \"u_sparam\", sparam );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // テクスチャのバインド\n // sampler2D u_image\n var image_tex = props[\"image\"];\n this.bindTexture2D( SimpleTextMaterial.TEXUNIT_IMAGE, image_tex.handle );\n }\n }\n\n}\n\n\n// クラス定数の定義\n{\n SimpleTextMaterial.TEXUNIT_IMAGE = 0; // 画像のテクスチャユニット\n\n // 計算用一時領域\n SimpleTextMaterial._sparam = GeoMath.createVector2f();\n}\n\n\nexport default SimpleTextMaterial;\n","/**\n * @summary Utility Class for Color\n * @memberof mapray\n */\nclass Color {\n /**\n * @param r {number} The red (0.0 ~ 1.0)\n * @param g {number} The green (0.0 ~ 1.0)\n * @param b {number} The blue (0.0 ~ 1.0)\n * @param a {number} The alpha (0.0 ~ 1.0)\n */\n constructor( r, g, b, a ) \n {\n this._r = r;\n this._g = g;\n this._b = b;\n this._a = a;\n }\n\n /**\n * @summary 不透明色を生成\n * @param {mapray.Vector3}\n * @return {mapray.Color}\n */\n static generateOpacityColor( rgb ) {\n return new Color(rgb[0], rgb[1], rgb[2], 1)\n }\n\n /**\n * @summary 色を代入\n * @desc\n * src を dst に代入する。
\n * @param {mapray.Color} src 代入元\n * @param {mapray.Color} dst 代入先\n * @return {mapray.Color} dst\n */\n static copyColor( src, dst )\n {\n dst._r = src._r;\n dst._g = src._g;\n dst._b = src._b;\n dst._a = src._a;\n\n return dst;\n }\n\n /**\n * @summary 不透明色を代入\n * @desc\n * src を dst に代入する。
\n * @param {mapray.Vector3} rgb 代入元\n * @param {mapray.Color} dst 代入先\n * @return {mapray.Color} dst\n */\n static setOpacityColor( rgb, dst )\n {\n dst._r = rgb[0];\n dst._g = rgb[1];\n dst._b = rgb[2];\n dst._a = 1;\n }\n\n /**\n * @summary 色配列に変換する\n * @desc\n * 0から255に正規化。 [R, G, B, A]の順番
\n *\n * @return {mapray.Vector4} dst\n */\n toArray()\n {\n return (this._a === 0 ? [0, 0, 0, 0] : [\n this.floatToByte(this._r) / this._a, \n this.floatToByte(this._g) / this._a, \n this.floatToByte(this._b) / this._a, \n this._a\n ]); \n }\n\n /**\n * @summary 色配列に変換する\n * @desc\n * 0から1に正規化。 [R, G, B, A]の順番
\n *\n * @return {mapray.Vector4} dst\n */\n toVector4()\n {\n return (this._a === 0 ? [0, 0, 0, 0] : [\n this._r / this._a, \n this._g / this._a, \n this._b / this._a, \n this._a\n ]);\n }\n\n /**\n * @summary RGBA文字列に変換する\n * @desc\n * RGBA文字列に変換して文字列を返却
\n *\n * @return {mapray.string}\n */\n toRGBString()\n {\n const rgba = this.toArray();\n return `rgba(${Math.round(rgba[0])},${Math.round(rgba[1])},${Math.round(rgba[2])},${rgba[3]})`;\n }\n\n /**\n * @summary 0~1.0の色値を255までで正規化\n * @desc\n * 0から1で正規化された色値を255までに拡張する
\n *\n * @return {mapray.string}\n */\n floatToByte( value )\n {\n return value === 1.0 ? 255.0 : (value * 256.0) | 0;\n }\n\n /**\n * @summary Red\n * @type {number}\n * @readonly\n */\n get r() {\n return this._r;\n }\n\n /**\n * @summary Green\n * @type {number}\n * @readonly\n */\n get g() {\n return this._g;\n }\n\n /**\n * @summary Blue\n * @type {number}\n * @readonly\n */\n get b() {\n return this._b;\n }\n\n /**\n * @summary Alpha\n * @type {number}\n * @readonly\n */\n get a() {\n return this._a;\n }\n};\n\nexport default Color;\n","import Entity from \"./Entity\";\nimport GeoPoint from \"./GeoPoint\";\nimport GeoRegion from \"./GeoRegion\";\n\n\n/**\n * @summary 点エンティティ\n *\n * @classdesc\n * {@link mapray.ImageIconEntity} と {@link mapray.PinEntity}\n * と {@link mapray.TextEntity} の共通機能を提供するクラスである。
\n *\n * @memberof mapray\n * @extends mapray.Entity\n * @abstract\n * @protected\n */\nclass AbstractPointEntity extends Entity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n // 要素管理\n this._entries = [];\n }\n\n\n /**\n * @summary すべてのEntryのバウンディングを算出\n *\n * @override\n * @return {mapray.GeoRegion} バウンディング情報を持ったGeoRegion\n */\n getBounds()\n {\n const region = new GeoRegion();\n for ( let entry of this._entries ) {\n region.addPoint( entry._position );\n }\n return region;\n }\n\n\n}\n\n\nexport default AbstractPointEntity;\n","import Entity from \"./Entity\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport Texture from \"./Texture\";\nimport TextMaterial from \"./TextMaterial\";\nimport SimpleTextMaterial from \"./SimpleTextMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport { RenderTarget } from \"./RenderStage\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport Dom from \"./util/Dom\";\nimport Color from \"./util/Color\";\nimport EasyBindingBlock from \"./animation/EasyBindingBlock\";\nimport Type from \"./animation/Type\";\nimport AbstractPointEntity from \"./AbstractPointEntity\";\n\n/**\n * @summary テキストエンティティ\n *\n * @memberof mapray\n * @extends mapray.Entity\n */\nclass TextEntity extends AbstractPointEntity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n // テキストの親プロパティ\n this._text_parent_props = {\n font_style: \"normal\",\n font_weight: \"normal\",\n font_size: TextEntity.DEFAULT_FONT_SIZE,\n font_family: TextEntity.DEFAULT_FONT_FAMILY,\n color: Color.generateOpacityColor( TextEntity.DEFAULT_COLOR ),\n stroke_color: Color.generateOpacityColor( TextEntity.DEFAULT_STROKE_COLOR ),\n stroke_width: TextEntity.DEFAULT_STROKE_WIDTH,\n bg_color: Color.generateOpacityColor( TextEntity.DEFAULT_BG_COLOR ),\n enable_stroke: false,\n enable_bg: false\n };\n\n this._animation.addDescendantUnbinder( () => { this._unbindDescendantAnimations(); } );\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return this._primitive_producer;\n }\n\n\n /**\n * @override\n */\n onChangeAltitudeMode( prev_mode )\n {\n if ( this._primitive_producer ) {\n this._primitive_producer.onChangeAltitudeMode();\n }\n }\n\n\n /**\n * EasyBindingBlock.DescendantUnbinder 処理\n *\n * @private\n */\n _unbindDescendantAnimations()\n {\n // すべてのエントリーを解除\n for ( let entry of this._entries ) {\n entry.animation.unbindAllRecursively();\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const string = Type.find( \"string\" );\n const vector3 = Type.find( \"vector3\" );\n \n // パラメータ名: font_style\n // パラメータ型: string\n // フォントスタイル\n block.addEntry( \"font_style\", [string], null, value => {\n this.setFontStyle( value );\n } );\n \n // パラメータ名: font_weight\n // パラメータ型: string\n // フォントの太さ\n block.addEntry( \"font_weight\", [string], null, value => {\n this.setFontWeight( value );\n } );\n \n // パラメータ名: font_size\n // パラメータ型: number\n // フォントの大きさ\n block.addEntry( \"font_size\", [number], null, value => {\n this.setFontSize( value );\n } );\n \n // パラメータ名: color\n // パラメータ型: vector3\n // テキストの色\n block.addEntry( \"color\", [vector3], null, value => {\n this.setColor( value );\n } );\n \n // パラメータ名: stroke_color\n // パラメータ型: vector3\n // 縁の色\n block.addEntry( \"stroke_color\", [vector3], null, value => {\n this.setStrokeColor( value );\n } );\n \n // パラメータ名: stroke_width\n // パラメータ型: number\n // 縁の線幅\n block.addEntry( \"stroke_width\", [number], null, value => {\n this.setStrokeLineWidth( value );\n } );\n }\n\n\n /**\n * @summary フォントスタイルを設定\n * @param {string} style フォントスタイル (\"normal\" | \"italic\" | \"oblique\")\n */\n setFontStyle( style )\n {\n this._setValueProperty( \"font_style\", style );\n }\n\n\n /**\n * @summary フォントの太さを設定\n * @param {string} weight フォントの太さ (\"normal\" | \"bold\")\n */\n setFontWeight( weight )\n {\n this._setValueProperty( \"font_weight\", weight );\n }\n\n\n /**\n * @summary フォントの大きさを設定\n * @param {number} size フォントの大きさ (Pixels)\n */\n setFontSize( size )\n {\n this._setValueProperty( \"font_size\", size );\n }\n\n\n /**\n * @summary フォントファミリーを設定\n * @param {string} family フォントファミリー\n * @see https://developer.mozilla.org/ja/docs/Web/CSS/font-family\n */\n setFontFamily( family )\n {\n this._setValueProperty( \"font_family\", family );\n }\n\n\n /**\n * @summary テキストの色を設定\n * @param {mapray.Vector3} color テキストの色\n */\n setColor( color )\n {\n this._setColorProperty( \"color\", color );\n }\n\n /**\n * @summary テキスト縁の色を設定\n * @param {mapray.Vector3} color 縁の色\n */\n setStrokeColor( color )\n {\n this._setColorProperty( \"stroke_color\", color );\n }\n\n /**\n * @summary テキスト縁の太さを設定\n * @param {mapray.number} width 縁の線幅\n */\n setStrokeLineWidth( width )\n {\n this._setValueProperty( \"stroke_width\", width );\n }\n\n /**\n * @summary テキスト縁を有効にするかどうか\n * @param {boolean} enable trueなら有効\n */\n setEnableStroke( enable )\n {\n this._setValueProperty( \"enable_stroke\", enable );\n this._primitive_producer = new PrimitiveProducer( this );\n }\n\n /**\n * @summary テキスト背景の色を設定\n * @param {mapray.Vector3} color テキストの色\n */\n setBackgroundColor( color )\n {\n this._setColorProperty( \"bg_color\", color );\n }\n\n /**\n * @summary テキスト背景を有効にするかどうか\n * @param {boolean} enable trueなら有効\n */\n setEnableBackground( enable )\n {\n this._setValueProperty( \"enable_bg\", enable );\n this._primitive_producer = new PrimitiveProducer( this );\n }\n\n /**\n * @summary テキストを追加\n * @param {string} text テキスト\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {string} [props.font_style] フォントスタイル (\"normal\" | \"italic\" | \"oblique\")\n * @param {string} [props.font_weight] フォントの太さ (\"normal\" | \"bold\")\n * @param {number} [props.font_size] フォントの大きさ (Pixels)\n * @param {string} [props.font_family] フォントファミリー\n * @param {mapray.Color} [props.color] テキストの色\n * @param {mapray.Color} [props.stroke_color] テキスト縁の色\n * @param {number} [props.stroke_width] テキスト縁の幅\n * @param {mapray.Color} [props.bg_color] テキスト背景色\n * @param {boolean} [props.enable_stroke] テキストの縁取りを有効にするか\n * @param {string} [props.id] Entryを識別するID\n * @return {mapray.TextEntity.Entry} 追加したEntry\n */\n addText( text, position, props )\n {\n var entry = new Entry( this, text, position, props );\n this._entries.push( entry );\n this._primitive_producer = new PrimitiveProducer( this );\n this._primitive_producer.onAddTextEntry();\n return entry;\n }\n\n\n /**\n * @summary 専用マテリアルを取得\n * @private\n */\n _getTextMaterial( render_target )\n {\n var scene = this.scene;\n if ( render_target === RenderTarget.SCENE ) {\n if ( !scene._TextEntity_text_material ) {\n // scene にマテリアルをキャッシュ\n scene._TextEntity_text_material = new TextMaterial( scene.glenv );\n }\n return scene._TextEntity_text_material;\n }\n else if (render_target === RenderTarget.RID) {\n if ( !scene._TextEntity_text_material_pick ) {\n // scene にマテリアルをキャッシュ\n scene._TextEntity_text_material_pick = new TextMaterial( scene.glenv, { ridMaterial: true } );\n }\n return scene._TextEntity_text_material_pick;\n }\n }\n\n /**\n * @summary テキストだけを描画する専用マテリアルを取得\n * @private\n */\n _getSimpleTextMaterial( render_target )\n {\n var scene = this.scene;\n if ( render_target === RenderTarget.SCENE ) {\n if ( !scene._SimpleTextEntity_text_material ) {\n // scene にマテリアルをキャッシュ\n scene._SimpleTextEntity_text_material = new SimpleTextMaterial( scene.glenv );\n }\n return scene._SimpleTextEntity_text_material;\n }\n else if (render_target === RenderTarget.RID) {\n if ( !scene._SimpleTextEntity_text_material_pick ) {\n // scene にマテリアルをキャッシュ\n scene._SimpleTextEntity_text_material_pick = new SimpleTextMaterial( scene.glenv, { ridMaterial: true } );\n }\n return scene._SimpleTextEntity_text_material_pick;\n }\n }\n\n /**\n * @private\n */\n _setValueProperty( name, value )\n {\n var props = this._text_parent_props;\n if ( props[name] != value ) {\n props[name] = value;\n if ( this._primitive_producer ) {\n this._primitive_producer.onChangeParentProperty();\n }\n }\n }\n\n\n /**\n * @private\n */\n _setColorProperty( name, value )\n {\n var dst = this._text_parent_props[name];\n if ( dst.r != value[0] || dst.g != value[1] || dst.b != value[2] ) {\n Color.setOpacityColor( value, dst );\n if ( this._primitive_producer ) {\n this._primitive_producer.onChangeParentProperty();\n }\n }\n }\n\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n var position = new GeoPoint();\n\n for ( let entry of json.entries ) {\n position.setFromArray( entry.position );\n this.addText( entry.text, position, entry );\n }\n\n if ( json.font_style !== undefined ) this.setFontStyle( json.font_style );\n if ( json.font_weight !== undefined ) this.setFontWeight( json.font_weight );\n if ( json.font_size !== undefined ) this.setFontSize( json.font_size );\n if ( json.font_family !== undefined ) this.setFontFamily( json.font_family );\n if ( json.color !== undefined ) this.setColor( json.color );\n if ( json.stroke_color !== undefined ) this.setStrokeColor ( json.stroke_color );\n if ( json.stroke_width !== undefined ) this.setStrokeLineWidth ( json.stroke_width );\n if ( json.enable_stroke !== undefined ) this.setEnableStroke ( json.enable_stroke );\n if ( json.bg_color !== undefined ) this.setBackgroundColor( json.bg_color );\n if ( json.enable_bg !== undefined ) this.setEnableBackground ( json.enable_bg );\n }\n\n /**\n * @private\n */\n _enableStroke( )\n {\n return this._text_parent_props[\"enable_stroke\"];\n }\n\n \n /**\n * @summary IDでEntryを取得\n * @param {string} id ID\n * @return {mapray.TextEntity.Entry} IDが一致するEntry(無ければundefined)\n */\n getEntry( id )\n {\n return this._entries.find((entry) => entry.id === id);\n }\n}\n\n\n// クラス定数の定義\n{\n TextEntity.DEFAULT_FONT_SIZE = 16;\n TextEntity.DEFAULT_FONT_FAMILY = \"sans-serif\";\n TextEntity.DEFAULT_COLOR = [1, 1, 1];\n TextEntity.DEFAULT_STROKE_COLOR = [0.0, 0.0, 0.0];\n TextEntity.DEFAULT_STROKE_WIDTH = 0.48;\n TextEntity.DEFAULT_BG_COLOR = [0.3, 0.3, 0.3];\n\n TextEntity.DEFAULT_TEXT_UPPER = 1.1;\n TextEntity.DEFAULT_TEXT_LOWER = 0.38;\n TextEntity.SAFETY_PIXEL_MARGIN = 1;\n TextEntity.MAX_IMAGE_WIDTH = 4096;\n}\n\n\n/**\n * @summary TextEntity の PrimitiveProducer\n *\n * TODO: relative で標高の変化のたびにテクスチャを生成する必要はないので\n * Layout でのテクスチャの生成とメッシュの生成を分離する\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n /**\n * @param {mapray.TextEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._glenv = entity.scene.glenv;\n this._dirty = true;\n\n // プリミティブの要素\n this._transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._properties = {\n enable_bg: false,\n image: null // テキスト画像\n };\n\n // プリミティブ\n var material = null, pickMaterial = null;\n if ( this._isSimpleText() ) {\n material = entity._getSimpleTextMaterial( RenderTarget.SCENE );\n pickMaterial = entity._getSimpleTextMaterial( RenderTarget.RID );\n } else {\n material = entity._getTextMaterial( RenderTarget.SCENE );\n pickMaterial = entity._getTextMaterial( RenderTarget.RID );\n }\n var primitive = new Primitive( this._glenv, null, material, this._transform );\n primitive.properties = this._properties;\n this._primitive = primitive;\n\n var pickPrimitive = new Primitive( this._glenv, null, pickMaterial, this._transform );\n pickPrimitive.properties = this._properties;\n this._pickPrimitive = pickPrimitive;\n\n // プリミティブ配列\n this._primitives = [];\n this._pickPrimitives = [];\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n const region = new EntityRegion();\n\n for ( let {position} of this.entity._entries ) {\n region.addPoint( position );\n }\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n this._dirty = true;\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n this._updatePrimitive();\n return stage.getRenderTarget() === RenderTarget.SCENE ? this._primitives : this._pickPrimitives;\n }\n\n\n /**\n * @summary 親プロパティが変更されたことを通知\n */\n onChangeParentProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 子プロパティが変更されたことを通知\n */\n onChangeChildProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 高度モードが変更されたことを通知\n */\n onChangeAltitudeMode()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary テキストが追加されたことを通知\n */\n onAddTextEntry()\n {\n // 変化した可能性がある\n this.needToCreateRegions();\n this._dirty = true;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 入力:\n * this.entity._entries\n * this._dirty\n * 出力:\n * this._transform\n * this._properties.image\n * this._primitive.mesh\n * this._primitives\n * this._dirty\n *\n * @return {array.} this._primitives\n *\n * @private\n */\n _updatePrimitive()\n {\n if ( !this._dirty ) {\n // 更新する必要はない\n return;\n }\n this._updateProperties();\n\n if ( this.entity._entries.length == 0 ) {\n this._primitives = [];\n this._pickPrimitives = [];\n this._dirty = false;\n return;\n }\n\n // 各エントリーの GOCS 位置を生成 (平坦化配列)\n var gocs_array = this._createFlatGocsArray();\n\n // プリミティブの更新\n // primitive.transform\n this._updateTransform( gocs_array );\n\n var layout = new Layout( this, gocs_array );\n if ( !layout.isValid() ) {\n // 更新に失敗\n this._primitives = [];\n this._dirty = false;\n return this._primitives;\n }\n\n // テクスチャ設定\n var properties = this._properties;\n if ( properties.image ) {\n properties.image.dispose();\n }\n properties.image = layout.texture;\n\n // メッシュ生成\n var vtype = [];\n if ( this._isSimpleText() ) {\n vtype = [\n { name: \"a_position\", size: 3 },\n { name: \"a_offset\", size: 2 },\n { name: \"a_texcoord\", size: 2 },\n { name: \"a_color\", size: 4 }\n ];\n } else {\n vtype = [\n { name: \"a_position\", size: 3 },\n { name: \"a_offset\", size: 2 },\n { name: \"a_texcoord\", size: 2 },\n ];\n }\n var mesh_data = {\n vtype,\n vertices: layout.vertices,\n indices: layout.indices\n };\n var mesh = new Mesh( this._glenv, mesh_data );\n\n // メッシュ設定\n // primitive.mesh\n var primitive = this._primitive;\n if ( primitive.mesh ) {\n primitive.mesh.dispose();\n }\n primitive.mesh = mesh;\n\n var pickPrimitive = this._pickPrimitive;\n if ( pickPrimitive.mesh ) {\n pickPrimitive.mesh.dispose();\n }\n pickPrimitive.mesh = mesh;\n\n // 更新に成功\n this._primitives = [primitive];\n this._pickPrimitives = [pickPrimitive];\n this._dirty = false;\n }\n\n /**\n * @summary プロパティを更新\n *\n * @desc\n * \n * 入力:\n * this.entity\n * 出力:\n * this._properties\n *
\n *\n * @private\n */\n _updateProperties()\n {\n let entity = this.entity;\n let props = this._properties;\n\n props.enable_bg = entity._text_parent_props.enable_bg;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 条件:\n * this.entity._entries.length > 0\n * 入力:\n * this.entity._entries.length\n * 出力:\n * this._transform\n *\n * @param {number[]} gocs_array GOCS 平坦化配列\n *\n * @private\n */\n _updateTransform( gocs_array )\n {\n var num_entries = this.entity._entries.length;\n var xsum = 0;\n var ysum = 0;\n var zsum = 0;\n\n for ( let i = 0; i < num_entries; ++i ) {\n let ibase = 3*i;\n xsum += gocs_array[ibase];\n ysum += gocs_array[ibase + 1];\n zsum += gocs_array[ibase + 2];\n }\n\n // 変換行列の更新\n var transform = this._transform;\n transform[12] = xsum / num_entries;\n transform[13] = ysum / num_entries;\n transform[14] = zsum / num_entries;\n }\n\n\n /**\n * @summary GOCS 平坦化配列を取得\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GOCS 平坦化配列\n * @private\n */\n _createFlatGocsArray()\n {\n const num_points = this.entity._entries.length;\n return GeoPoint.toGocsArray( this._getFlatGeoPoints_with_Absolute(), num_points,\n new Float64Array( 3 * num_points ) );\n }\n\n\n /**\n * @summary GeoPoint 平坦化配列を取得 (絶対高度)\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GeoPoint 平坦化配列\n * @private\n */\n _getFlatGeoPoints_with_Absolute()\n {\n const owner = this.entity;\n const entries = owner._entries;\n const num_points = entries.length;\n const flat_array = new Float64Array( 3 * num_points );\n\n // flat_array[] に経度要素と緯度要素を設定\n for ( let i = 0; i < num_points; ++i ) {\n let pos = entries[i].position;\n flat_array[3*i] = pos.longitude;\n flat_array[3*i + 1] = pos.latitude;\n }\n\n switch ( owner.altitude_mode ) {\n case AltitudeMode.RELATIVE:\n case AltitudeMode.CLAMP:\n // flat_array[] の高度要素に現在の標高を設定\n owner.scene.viewer.getExistingElevations( num_points, flat_array, 0, 3, flat_array, 2, 3 );\n\n if ( owner.altitude_mode === AltitudeMode.RELATIVE ) {\n // flat_array[] の高度要素に相対高度を加える\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] += entries[i].position.altitude;\n }\n }\n break;\n\n default: // AltitudeMode.ABSOLUTE\n // flat_array[] の高度要素に絶対高度を設定\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] = entries[i].position.altitude;\n }\n break;\n }\n\n return flat_array;\n }\n\n /**\n * @summary シンプルテキストモードかどうかを確認\n *\n *\n * @return {boolean} シンプルテキストモードならtrue.\n * @private\n */\n _isSimpleText() \n {\n let entity = this.entity;\n\n let enable = true;\n // check enable bg color or stroke;\n if ( entity._text_parent_props.enable_bg || entity._text_parent_props.enable_stroke ) {\n enable = false;\n }\n\n // check enable stroke\n let i = 0;\n while ( enable && entity._entries.length > i ) {\n let entry = entity._entries[i];\n enable = !entry.enable_stroke;\n i++;\n }\n\n return enable;\n }\n\n}\n\n\n/**\n * @summary テキスト要素\n * @hideconstructor\n * @memberof mapray.TextEntity\n * @public\n */\nclass Entry {\n\n /**\n * @param {mapray.TextEntity} owner 所有者\n * @param {string} text テキスト\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {string} [props.font_style] フォントスタイル (\"normal\" | \"italic\" | \"oblique\")\n * @param {string} [props.font_weight] フォントの太さ (\"normal\" | \"bold\")\n * @param {number} [props.font_size] フォントの大きさ (Pixels)\n * @param {string} [props.font_family] フォントファミリー\n * @param {mapray.Color} [props.color] テキストの色\n * @param {mapray.Color} [props.stroke_color] テキスト縁の色\n * @param {number} [props.stroke_width] テキスト縁の幅\n * @param {number} [props.enable_stroke] テキストの縁取りを有効にするか\n * @param {string} [props.id] Entryを識別するID\n */\n constructor( owner, text, position, props )\n {\n this._owner = owner;\n this._text = text;\n this._position = position.clone();\n\n // animation.BindingBlock\n this._animation = new EasyBindingBlock();\n \n this._setupAnimationBindingBlock();\n\n this._props = Object.assign( {}, props ); // props の複製\n this._copyColorProperty( \"color\" ); // deep copy\n this._copyColorProperty( \"stroke_color\" ); // deep copy\n this._copyColorProperty( \"bg_color\" ); // deep copy\n }\n\n \n /**\n * @summary テキスト\n * @type {string}\n * @readonly\n * @package\n */\n get text()\n {\n return this._text;\n }\n\n\n /**\n * @summary 位置\n * @type {mapray.GeoPoint}\n * @readonly\n * @package\n */\n get position()\n {\n return this._position;\n }\n\n \n /**\n * @summary ID\n * @type {string}\n * @readonly\n */\n get id()\n {\n return this._props.hasOwnProperty( \"id\" ) ? this._props.id : \"\";\n }\n \n\n /**\n * @summary フォントサイズ (Pixels)\n * @type {number}\n * @readonly\n * @package\n */\n get size()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.font_size || parent.font_size;\n }\n\n\n /**\n * @summary テキストの色\n * @type {mapray.Vector3}\n * @readonly\n * @package\n */\n get color()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.color || parent.color;\n }\n\n\n /**\n * @summary フォント\n * @type {string}\n * @readonly\n * @package\n * @see https://developer.mozilla.org/ja/docs/Web/CSS/font\n */\n get font()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n\n var style = props.font_style || parent.font_style;\n var variant = \"normal\";\n var weight = props.font_weight || parent.font_weight;\n var family = props.font_family || parent.font_family;\n\n return style + \" \" + variant + \" \" + weight + \" \" + this.size + \"px \" + family;\n }\n\n /**\n * @summary テキスト縁の色\n * @type {mapray.Color}\n * @readonly\n * @package\n */\n get stroke_color()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.stroke_color || parent.stroke_color;\n }\n\n /**\n * @summary 縁の幅 (Pixels)\n * @type {number}\n * @readonly\n * @package\n */\n get stroke_width()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.stroke_width || parent.stroke_width;\n }\n\n /**\n * @summary 縁を描画するか\n * @type {boolean}\n * @readonly\n * @package\n */\n get enable_stroke()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.enable_stroke || parent.enable_stroke;\n }\n\n /**\n * @summary 背景色\n * @type {mapray.Color}\n * @readonly\n * @package\n */\n get bg_color()\n {\n var props = this._props;\n var parent = this._owner._text_parent_props;\n return props.bg_color || parent.bg_color;\n }\n\n /**\n * @summary 背景描画するか\n * @type {boolean}\n * @readonly\n * @package\n */\n get enable_background()\n {\n // Enable or Disable background can be set by parent.\n var parent = this._owner._text_parent_props;\n return parent.enable_bg;\n }\n\n \n /**\n * @summary アニメーションパラメータ設定\n *\n * @type {mapray.animation.BindingBlock}\n * @readonly\n */\n get animation() { return this._animation; }\n \n \n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const string = Type.find( \"string\" );\n const vector3 = Type.find( \"vector3\" );\n \n // パラメータ名: position\n // パラメータ型: vector3\n // ベクトルの要素が longitude, latitude, altitude 順であると解釈\n const position_temp = new GeoPoint();\n\n block.addEntry( \"position\", [vector3], null, value => {\n position_temp.setFromArray( value ); // Vector3 -> GeoPoint\n this.setPosition( position_temp );\n } );\n\n // パラメータ名: font_style\n // パラメータ型: string\n // フォントスタイル\n block.addEntry( \"font_style\", [string], null, value => {\n this.setFontStyle( value );\n } );\n \n // パラメータ名: font_weight\n // パラメータ型: string\n // フォントの太さ\n block.addEntry( \"font_weight\", [string], null, value => {\n this.setFontWeight( value );\n } );\n \n // パラメータ名: font_size\n // パラメータ型: number\n // フォントの大きさ\n block.addEntry( \"font_size\", [number], null, value => {\n this.setFontSize( value );\n } );\n \n // パラメータ名: color\n // パラメータ型: vector3\n // テキストの色\n block.addEntry( \"color\", [vector3], null, value => {\n this.setColor( value );\n } );\n \n // パラメータ名: stroke_color\n // パラメータ型: vector3\n // 縁の色\n block.addEntry( \"stroke_color\", [vector3], null, value => {\n this.setStrokeColor( value );\n } );\n \n // パラメータ名: stroke_width\n // パラメータ型: number\n // 縁の線幅\n block.addEntry( \"stroke_width\", [number], null, value => {\n this.setStrokeLineWidth( value );\n } );\n\n // パラメータ名: text\n // パラメータ型: string\n // テキスト\n block.addEntry( \"text\", [string], null, value => {\n this.setText( value );\n } ); \n }\n\n\n /**\n * @summary テキスト原点位置を設定\n *\n * @param {mapray.GeoPoint} position テキスト原点の位置\n */\n setPosition( position )\n {\n if ( this._position.longitude !== position.longitude ||\n this._position.latitude !== position.latitude ||\n this._position.altitude !== position.altitude ) {\n // 位置が変更された\n this._position.assign( position );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n\n /**\n * @summary フォントスタイルを設定\n * @param {string} style フォントスタイル (\"normal\" | \"italic\" | \"oblique\")\n */\n setFontStyle( style )\n {\n this._setValueProperty( \"font_style\", style );\n }\n\n\n /**\n * @summary フォントの太さを設定\n * @param {string} weight フォントの太さ (\"normal\" | \"bold\")\n */\n setFontWeight( weight )\n {\n this._setValueProperty( \"font_weight\", weight );\n }\n\n\n /**\n * @summary フォントの大きさを設定\n * @param {number} size フォントの大きさ (Pixels)\n */\n setFontSize( size )\n {\n this._setValueProperty( \"font_size\", size );\n }\n\n\n /**\n * @summary テキストの色を設定\n * @param {mapray.Vector3} color テキストの色\n */\n setColor( color )\n {\n this._setColorProperty( \"color\", color );\n }\n\n\n /**\n * @summary テキスト縁の色を設定\n * @param {mapray.Vector3} color 縁の色\n */\n setStrokeColor( color )\n {\n this._setColorProperty( \"stroke_color\", color );\n }\n\n\n /**\n * @summary テキスト縁の太さを設定\n * @param {mapray.number} width 縁の線幅\n */\n setStrokeLineWidth( width )\n {\n this._setValueProperty( \"stroke_width\", width );\n }\n\n\n /**\n * @summary テキスト縁を有効にするかどうか\n * @param {boolean} enable trueなら有効\n */\n setEnableStroke( enable )\n {\n this._setValueProperty( \"enable_stroke\", enable );\n this._owner._primitive_producer = new PrimitiveProducer( this._owner );\n }\n\n\n /**\n * @summary テキストを設定\n * @param {string} text テキスト\n */\n setText( text )\n {\n if ( this._text !== text ) {\n this._text = text;\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n \n\n /**\n * @private\n */\n _copyColorProperty( name )\n {\n var props = this._props;\n if ( props.hasOwnProperty( name ) ) {\n props[name] = Color.generateOpacityColor( props[name] );\n }\n }\n\n\n /**\n * @private\n */\n _setValueProperty( name, value )\n {\n var props = this._props;\n if ( props[name] != value ) {\n props[name] = value;\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n\n /**\n * @private\n */\n _setColorProperty( name, value )\n {\n var dst = this._props[name];\n if ( dst )\n {\n if ( dst.r != value[0] || dst.g != value[1] || dst.b != value[2] ) {\n Color.setOpacityColor( value, dst );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n else\n {\n this._props[name] = Color.generateOpacityColor( value );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n}\n\nTextEntity.Entry = Entry;\n\n\n/**\n * @summary テキスト画像を Canvas 上にレイアウト\n *\n * @memberof mapray.TextEntity\n * @private\n */\nclass Layout {\n\n /**\n * @desc\n * 入力:\n * owner._glenv\n * owner.entity._entries\n * owner._transform\n *\n * @param {PrimitiveProducer} owner 所有者\n * @param {number[]} gocs_array GOCS 平坦化配列\n */\n constructor( owner, gocs_array )\n {\n this._owner = owner;\n this._items = this._createItemList();\n this._is_valid = true;\n\n var row_layouts = this._createRowLayouts();\n if ( row_layouts.length == 0 ) {\n // 有効なテキストが1つも無い\n this._is_valid = false;\n return;\n }\n\n // アイテムの配置の設定とキャンバスサイズの決定\n var size = this._setupLocation( row_layouts );\n\n if ( this._isSimpleTextWithAllItems( this._items ) ) { \n this._texture = this._createTextureForSimple( size.width, size.height );\n this._vertices = this._createVerticesForSimple( size.width, size.height, gocs_array );\n } else {\n this._texture = this._createTexture( size.width, size.height );\n this._vertices = this._createVertices( size.width, size.height, gocs_array );\n }\n this._indices = this._createIndices();\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._is_valid;\n }\n\n\n /**\n * @summary テクスチャ\n * @type {mapray.Texture}\n * @readonly\n */\n get texture()\n {\n return this._texture;\n }\n\n\n /**\n * @summary 頂点配列\n * @desc\n * 条件:\n * this._entries.length > 0\n * 入力:\n * this._entries\n * this._transform\n * @type {Float32Array}\n * @readonly\n */\n get vertices()\n {\n return this._vertices;\n }\n\n\n /**\n * @summary インデックス配列\n * @type {Uint32Array}\n * @readonly\n */\n get indices()\n {\n return this._indices;\n }\n\n\n /**\n * @summary レイアウトアイテムのリストを生成\n * @return {array.}\n * @private\n */\n _createItemList()\n {\n var context = Dom.createCanvasContext( 1, 1 );\n\n var items = [];\n for ( let entry of this._owner.entity._entries ) {\n items.push( new LItem( this, entry, context ) );\n }\n\n return items;\n }\n\n\n /**\n * @summary RowLayout のリストを生成\n * @return {array.}\n * @private\n */\n _createRowLayouts()\n {\n // アイテムリストの複製\n var items = [].concat( this._items );\n\n // RowLayout 内であまり高さに差が出ないように、アイテムリストを高さで整列\n items.sort( function( a, b ) { return a.height_pixel - b.height_pixel; } );\n\n // リストを生成\n var row_layouts = [];\n while ( items.length > 0 ) {\n var row_layout = new RowLayout( items );\n if ( row_layout.isValid() ) {\n row_layouts.push( row_layout );\n }\n }\n\n return row_layouts;\n }\n\n\n /**\n * @summary テクスチャを生成\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @return {mapray.Texture} テキストテクスチャ\n * @private\n */\n _createTexture( width, height )\n {\n var context = Dom.createCanvasContext( width, height );\n\n context.textAlign = \"left\";\n context.textBaseline = \"alphabetic\";\n context.fillStyle = \"rgba( 255, 255, 255, 1.0 )\";\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n if ( item._entry.enable_background ) {\n item.drawRect( context );\n }\n if ( item._entry.enable_stroke ) {\n item.drawStrokeText( context );\n }\n item.drawText( context );\n }\n\n var glenv = this._owner._glenv;\n var opts = {\n usage: Texture.Usage.TEXT\n };\n return new Texture( glenv, context.canvas, opts );\n }\n\n\n /**\n * @summary 頂点配列を生成\n *\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @param {number[]} gocs_array GOCS 平坦化配列\n * @return {array.} 頂点配列 [左下0, 右下0, 左上0, 右上0, ...]\n *\n * @private\n */\n _createVertices( width, height, gocs_array )\n {\n var vertices = [];\n\n // テキスト集合の原点 (GOCS)\n var transform = this._owner._transform;\n var xo = transform[12];\n var yo = transform[13];\n var zo = transform[14];\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n // テキストの位置 (モデル座標系)\n var ibase = 3 * i;\n var xm = gocs_array[ibase] - xo;\n var ym = gocs_array[ibase + 1] - yo;\n var zm = gocs_array[ibase + 2] - zo;\n\n // ベースライン左端 (キャンバス座標系)\n var xc = item.pos_x;\n var yc = item.pos_y;\n\n var upper = item.upper;\n var lower = item.lower;\n var xsize = item.width;\n\n var xn = 1 / width;\n var yn = 1 / height;\n\n // 左下\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -xsize / 2, -lower ); // a_offset\n vertices.push( xc * xn, 1 - (yc + lower) * yn ); // a_texcoord\n\n // 右下\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( xsize / 2, -lower ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - (yc + lower) * yn ); // a_texcoord\n\n // 左上\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -xsize / 2, upper ); // a_offset\n vertices.push( xc * xn, 1 - (yc - upper) * yn ); // a_texcoord\n\n // 右上\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( xsize / 2, upper ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - (yc - upper) * yn ); // a_texcoord\n }\n\n return vertices;\n }\n\n\n /**\n * @summary 単純テキスト用テクスチャを生成\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @return {mapray.Texture} テキストテクスチャ\n * @private\n */\n _createTextureForSimple( width, height )\n {\n var context = Dom.createCanvasContext( width, height );\n\n context.textAlign = \"left\";\n context.textBaseline = \"alphabetic\";\n context.fillStyle = \"rgba( 255, 255, 255, 1.0 )\";\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n item.drawText( context );\n }\n\n var glenv = this._owner._glenv;\n var opts = {\n usage: Texture.Usage.SIMPLETEXT\n };\n return new Texture( glenv, context.canvas, opts );\n }\n\n\n /**\n * @summary 単純テキスト用頂点配列を生成\n *\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @param {number[]} gocs_array GOCS 平坦化配列\n * @return {array.} 頂点配列 [左下0, 右下0, 左上0, 右上0, ...]\n *\n * @private\n */\n _createVerticesForSimple( width, height, gocs_array )\n {\n var vertices = [];\n\n // テキスト集合の原点 (GOCS)\n var transform = this._owner._transform;\n var xo = transform[12];\n var yo = transform[13];\n var zo = transform[14];\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n var entry = item.entry;\n\n // テキストの色\n var color = entry.color.toVector4();\n\n // テキストの位置 (モデル座標系)\n var ibase = 3 * i;\n var xm = gocs_array[ibase] - xo;\n var ym = gocs_array[ibase + 1] - yo;\n var zm = gocs_array[ibase + 2] - zo;\n\n // ベースライン左端 (キャンバス座標系)\n var xc = item.pos_x;\n var yc = item.pos_y;\n\n var upper = item.upper;\n var lower = item.lower;\n var xsize = item.width;\n\n var xn = 1 / width;\n var yn = 1 / height;\n\n // 左下\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -xsize / 2, -lower ); // a_offset\n vertices.push( xc * xn, 1 - (yc + lower) * yn ); // a_texcoord\n vertices.push( color[0], color[1], color[2], 1 ); // a_color\n\n // 右下\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( xsize / 2, -lower ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - (yc + lower) * yn ); // a_texcoord\n vertices.push( color[0], color[1], color[2], 1 ); // a_color\n\n // 左上\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -xsize / 2, upper ); // a_offset\n vertices.push( xc * xn, 1 - (yc - upper) * yn ); // a_texcoord\n vertices.push( color[0], color[1], color[2], 1 ); // a_color\n\n // 右上\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( xsize / 2, upper ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - (yc - upper) * yn ); // a_texcoord\n vertices.push( color[0], color[1], color[2], 1 ); // a_color\n }\n\n return vertices;\n }\n \n\n /**\n * @summary インデックス配列を生成\n * @return {array.} インデックス配列 []\n * @private\n */\n _createIndices()\n {\n var indices = [];\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n var b = 4 * i;\n indices.push( b, b + 1, b + 2, b + 2, b + 1 , b + 3 );\n }\n\n return indices;\n }\n\n\n /**\n * @summary アイテムの配置を設定\n * @param {array.} row_layouts\n * @return {object} キャンバスサイズ\n * @private\n */\n _setupLocation( row_layouts )\n {\n var width = 0;\n var height = 0;\n\n height += TextEntity.SAFETY_PIXEL_MARGIN;\n\n for ( var i = 0; i < row_layouts.length; ++i ) {\n var row_layout = row_layouts[i];\n row_layout.locate( height );\n width = Math.max( row_layout.width_assumed, width );\n height += row_layout.height_pixel + TextEntity.SAFETY_PIXEL_MARGIN;\n }\n\n return {\n width: width,\n height: height\n };\n }\n\n /**\n * @summary シンプルテキストモードかどうか\n * @param {mapray.TextEntity.LItem} item\n * @return {boolean} シンプルテキストモードならtrue\n * @private\n */\n _isSimpleText( item ) \n {\n if ( item._entry.enable_background || item._entry.enable_stroke ) {\n return false;\n }\n return true;\n } \n\n /**\n * @summary シンプルテキストモードかどうか\n * @param {array.} items\n * @return {boolean} シンプルテキストモードならtrue\n * @private\n */\n _isSimpleTextWithAllItems( items ) \n {\n let enable = true;\n let i = 0;\n while( enable && items.length > i) \n {\n let item = items[i];\n enable = this._isSimpleText( item );\n i++;\n }\n return enable;\n } \n\n}\n\n\n/**\n * @summary レイアウト対象\n * @memberof mapray.TextEntity\n * @private\n */\nclass LItem {\n\n /**\n * @param {mapray.TextEntity.Layout} layout 所有者\n * @param {mapray.TextEntity.Entry} entry TextEntity エントリ\n * @param {CanvasRenderingContext2D} context 測定用コンテキスト\n */\n constructor( layout, entry, context )\n {\n this._entry = entry;\n\n // テキストの基点\n this._pos_x = 0; // 左端\n this._pos_y = 0; // ベースライン位置\n\n // テキストの横幅\n context.font = entry.font;\n this._width = context.measureText( entry.text ).width;\n\n // テキストの上下範囲\n this._upper = entry.size * TextEntity.DEFAULT_TEXT_UPPER;\n this._lower = entry.size * TextEntity.DEFAULT_TEXT_LOWER;\n\n this._is_canceled = false;\n }\n\n\n /**\n * @type {mapray.TextEntity.Entry}\n * @readonly\n */\n get entry()\n {\n return this._entry;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_x()\n {\n return this._pos_x;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_y()\n {\n return this._pos_y;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get width()\n {\n return this._width;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get upper()\n {\n return this._upper;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get lower()\n {\n return this._lower;\n }\n\n\n /**\n * キャンバス上でのテキストの横画素数\n * @type {number}\n * @readonly\n */\n get width_pixel()\n {\n return Math.ceil( this._width );\n }\n\n\n /**\n * キャンバス上でのテキストの縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return Math.ceil( this._upper ) + Math.ceil( this._lower );\n }\n\n\n /**\n * 取り消し状態か?\n * @type {boolean}\n * @readonly\n */\n get is_canceled()\n {\n return this._is_canceled;\n }\n\n\n /**\n * @summary 取り消し状態に移行\n */\n cancel()\n {\n this._is_canceled = true;\n }\n\n\n /**\n * @summary 配置を決定\n * @param {number} x テキスト矩形左辺の X 座標 (キャンバス座標系)\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( x, y )\n {\n this._pos_x = x;\n this._pos_y = y + Math.ceil( this._upper );\n }\n\n\n /**\n * @summary テキストだけを描画 (stokeやfillRectとは組み合わせ不可)\n * @desc\n * context は以下のように設定していること。
\n * \n * context.textAlign = \"left\";\n * context.textBaseline = \"alphabetic\";\n * context.fillStyle = \"rgba( 255, 255, 255, 1.0 )\";\n *
\n * @param {CanvasRenderingContext2D} context 描画先コンテキスト\n */\n drawTextOnly( context )\n {\n var entry = this._entry;\n context.font = entry.font;\n context.fillText( entry.text, this._pos_x, this._pos_y );\n }\n\n\n /**\n * @summary テキストを描画\n * @desc\n * context は以下のように設定していること。
\n * \n * context.textAlign = \"left\";\n * context.textBaseline = \"alphabetic\";\n * context.fillStyle = \"rgba( 255, 255, 255, 1.0 )\";\n *
\n * @param {CanvasRenderingContext2D} context 描画先コンテキスト\n */\n drawText( context )\n {\n var entry = this._entry;\n\n context.font = entry.font;\n context.fillStyle = entry.color.toRGBString();\n context.fillText( entry.text, this._pos_x, this._pos_y );\n }\n\n \n /**\n * @summary テキストの淵を描画\n * @desc\n * drawTextOnlyとは組み合わせ不可
\n\n * @param {CanvasRenderingContext2D} context 描画先コンテキスト\n */\n drawStrokeText( context )\n {\n /*\n context.fillText()\n .------------. \n |',',',',',',| \n |',',',',',',| \n |',',',',',',| \n |',',',',',',| \n |',',',',',',| \n\n context.strokeText()\n .--------------------.\n |',',',',',',',',',',|\n |','.------------.,',|\n |','|',',',',',',|,',|\n |','|','.----.,',|,',|\n |','|','| |,',|,',|\n |','|','| |,',|,',|\n |<--|-->|\n a b\n b will be overwrite by fillText();\n */\n var entry = this._entry;\n\n context.font = entry.font;\n context.strokeStyle = entry.stroke_color.toRGBString();\n context.lineWidth = entry.stroke_width * 2;\n context.lineJoin = \"round\";\n context.strokeText( entry.text, this._pos_x, this._pos_y );\n }\n\n\n /**\n * @summary テキストの背景を描画\n * @desc\n * drawTextOnlyとは組み合わせ不可
\n\n * @param {CanvasRenderingContext2D} context 描画先コンテキスト\n */\n drawRect( context )\n {\n var entry = this._entry;\n\n context.fillStyle = entry.bg_color.toRGBString();\n context.fillRect( this._pos_x - TextEntity.SAFETY_PIXEL_MARGIN, this._pos_y - this._upper - TextEntity.SAFETY_PIXEL_MARGIN, this.width_pixel + TextEntity.SAFETY_PIXEL_MARGIN, this.height_pixel + TextEntity.SAFETY_PIXEL_MARGIN );\n }\n}\n\n\n/**\n * @summary 水平レイアウト\n * @memberof mapray.TextEntity\n * @private\n */\nclass RowLayout {\n\n /**\n * @desc\n * レイアウトされた、またはレイアウトに失敗したアイテムは src_items から削除される。
\n * レイアウトに失敗したアイテムは取り消し (is_canceled) になる。
\n * @param {array.} src_items アイテムリスト\n */\n constructor( src_items )\n {\n var width_assumed_total = 0;\n var height_pixel_max = 0;\n var row_items = [];\n\n width_assumed_total += TextEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n while ( src_items.length > 0 ) {\n var item = src_items.shift();\n var width_assumed = item.width_pixel + TextEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n\n if ( width_assumed_total + width_assumed <= TextEntity.MAX_IMAGE_WIDTH ) {\n // 行にアイテムを追加\n row_items.push( item );\n width_assumed_total += width_assumed;\n height_pixel_max = Math.max( item.height_pixel, height_pixel_max );\n }\n else {\n if ( row_items.length == 0 ) {\n // テキストが長すぎて表示できない\n item.cancel();\n }\n else {\n // 次の行になるため差し戻して終了\n src_items.unshift( item );\n break;\n }\n }\n }\n\n this._items = row_items;\n this._width_assumed = width_assumed_total;\n this._height_pixel = height_pixel_max;\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._items.length > 0;\n }\n\n\n /**\n * \n * @type {array.}\n * @readonly\n */\n get items()\n {\n return this._items;\n }\n\n\n /**\n * キャンバス上での行の横占有画素数\n * @type {number}\n * @readonly\n */\n get width_assumed()\n {\n return this._width_assumed;\n }\n\n\n /**\n * キャンバス上での行の縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return this._height_pixel;\n }\n\n\n /**\n * @summary レイアウトの配置を決定\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( y )\n {\n var items = this._items;\n var x = 0;\n\n x += TextEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n item.locate( x, y );\n x += item.width_pixel + TextEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n }\n }\n\n}\n\n\nexport default TextEntity;\n","import Entity from \"./Entity\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport GeoRegion from \"./GeoRegion\";\nimport Orientation from \"./Orientation\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport Type from \"./animation/Type\";\nimport AnimUtil from \"./animation/AnimUtil\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary モデルエンティティ\n * @memberof mapray\n * @extends mapray.Entity\n */\nclass ModelEntity extends Entity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n *\n * @throws Error ModelContainer からモデルが見つからなかった\n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n this._position = new GeoPoint( 0, 0, 0 );\n this._rotation = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._scale = GeoMath.createVector3( [1, 1, 1] );\n\n this._primitive_producer = new PrimitiveProducer( this );\n\n this._setupAnimationBindingBlock();\n\n // アンカーモード\n this._anchor_mode = false;\n\n if ( opts && opts.json ) {\n var json = opts.json;\n var refs = opts.refs || {};\n this._setupTransform( json );\n this._setupModelObject( json, refs );\n }\n }\n\n\n /**\n * @override\n * @private\n */\n get anchor_mode() { return this._anchor_mode; }\n\n /**\n * @summary アンカーモードを設定。\n * @see {@link mapray.Entity#anchor_mode}\n * @param {boolean} anchor_mode\n * @private\n */\n setAnchorMode( anchor_mode )\n {\n this._anchor_mode = anchor_mode;\n }\n\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return this._primitive_producer;\n }\n\n\n /**\n * @override\n */\n onChangeAltitudeMode( prev_mode )\n {\n this._primitive_producer.onChangeAltitudeMode();\n }\n\n\n /**\n * @summary bboxを利用して簡易的にバウンディングを算出\n *\n * @override\n * @return {mapray.GeoRegion} バウンディング情報を持ったGeoRegion\n */\n getBounds()\n {\n const bounds = this._primitive_producer.getBounds();\n const region = new GeoRegion();\n region.addPointsAsArray( bounds );\n return region;\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector3 = Type.find( \"vector3\" );\n const matrix = Type.find( \"matrix\" );\n\n // パラメータ名: position\n // パラメータ型: vector3\n // ベクトルの要素が longitude, latitude, altitude 順であると解釈\n const position_temp = new GeoPoint();\n\n block.addEntry( \"position\", [vector3], null, value => {\n position_temp.setFromArray( value ); // Vector3 -> GeoPoint\n this.setPosition( position_temp );\n } );\n\n // パラメータ名: orientation\n // パラメータ型: matrix | vector3\n // 型が matrix のとき MLOCS での回転行列\n // 型が vector3 のとき、要素が heading, tilt, roll 順であると解釈\n const orientation_temp = new Orientation();\n let orientation_type;\n\n let orientation_tsolver = curve => {\n orientation_type = AnimUtil.findFirstTypeSupported( curve, [matrix, vector3] );\n return orientation_type;\n };\n\n block.addEntry( \"orientation\", [matrix, vector3], orientation_tsolver, value => {\n if ( orientation_type === matrix ) {\n this._setRotation( value );\n }\n else { // orientation_type === vector3\n orientation_temp.heading = value[0];\n orientation_temp.tilt = value[1];\n orientation_temp.roll = value[2];\n this.setOrientation( orientation_temp );\n }\n } );\n\n // パラメータ名: scale\n // パラメータ型: vector3 | number\n // 型が vector3 のときは XYZ 別の倍率\n // 型が number のときは均等倍率\n const scale_temp = GeoMath.createVector3();\n let scale_type;\n\n let scale_tsolver = curve => {\n scale_type = AnimUtil.findFirstTypeSupported( curve, [vector3, number] );\n return scale_type;\n };\n\n block.addEntry( \"scale\", [vector3, number], scale_tsolver, value => {\n if ( scale_type === vector3 ) {\n this.setScale( value );\n }\n else { // scale_type === number\n scale_temp[0] = value;\n scale_temp[1] = value;\n scale_temp[2] = value;\n this.setScale( scale_temp );\n }\n } );\n }\n\n\n /**\n * @summary position, orientation, scale を設定\n *\n * @param {object} json 生成情報\n *\n * @private\n */\n _setupTransform( json )\n {\n let tr = json.transform; // \n\n // position\n this.setPosition( new GeoPoint().setFromArray( tr.position ) );\n\n // heading, tilt, roll\n this.setOrientation( new Orientation( tr.heading, tr.tilt, tr.roll ) );\n\n // scale\n var scale = (tr.scale !== undefined) ? tr.scale : [1, 1, 1]; // \n if ( typeof scale == 'number' ) {\n // スケールをベクトルに正規化\n scale = [scale, scale, scale];\n }\n this.setScale( scale );\n }\n\n\n /**\n * @summary モデルを設定\n *\n * @param {object} json 生成情報\n * @param {object} refs 参照辞書\n *\n * @throws Error\n *\n * @private\n */\n _setupModelObject( json, refs )\n {\n let container = refs[json.ref_model];\n\n this._primitive_producer.setModelObject( container, json.index );\n }\n\n\n /**\n * @summary モデル原点位置を設定\n *\n * @param {mapray.GeoPoint} value モデル原点の位置\n */\n setPosition( value )\n {\n let op = this._position; // 変更前の位置\n\n if ( value.longitude != op.longitude ||\n value.latitude != op.latitude ||\n value.altitude != op.altitude ) {\n // 位置が変更された\n this._position.assign( value );\n this._primitive_producer.onChangePosition();\n }\n }\n\n\n /**\n * @summary モデルの向きを設定\n *\n * @param {mapray.Orientation} value モデルの向き\n */\n setOrientation( value )\n {\n value.getTransformMatrix( sameScaleVector3, this._rotation );\n }\n\n\n /**\n * @summary モデルのスケールを設定\n *\n * @param {mapray.Vector3} value モデルのスケール\n */\n setScale( value )\n {\n GeoMath.copyVector3( value, this._scale );\n }\n\n\n /**\n * @summary モデルの回転を設定\n *\n * @desc\n * 今のところアニメーション専用
\n *\n * @param {mapray.Matrix} value 回転行列\n *\n * @private\n */\n _setRotation( value )\n {\n GeoMath.copyMatrix( value, this._rotation );\n }\n\n\n /**\n * @summary モデル位置の標高を取得\n *\n * @return {number} 標高値\n *\n * @private\n */\n _getElevation()\n {\n return this.scene.viewer.getExistingElevation( this._position );\n }\n\n}\n\n\n/**\n * @summary ModelEntity の PrimitiveProducer\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n /**\n * @param {mapray.ModelEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._primitives = []; // プリミティブ配列\n this._pickPrimitives = []; // プリミティブ配列\n this._ptoe_array = []; // 各プリミティブ座標系からエンティティ座標系への変換行列\n\n this._abs_position = null; // 絶対高度に変換した位置のキャッシュ (null なら無効)\n }\n\n\n /**\n * @summary モデルを設定\n *\n * @param {mapray.ModelContainer} container モデルコンテナ\n * @param {number|string} [id] モデル ID\n *\n * @throws Error\n */\n setModelObject( container, index )\n {\n let primitives = container.createPrimitives( index, { ridMaterial: false } );\n let pickPrimitives = container.createPrimitives( index, { ridMaterial: true } );\n\n if ( primitives ) {\n this._primitives = primitives;\n this._pickPrimitives = pickPrimitives;\n this._ptoe_array = primitives.map( prim => GeoMath.createMatrix( prim.transform ) );\n }\n else {\n // ModelContainer からモデルが見つからなかった\n throw new Error( \"model is not found in ModelContainer\" );\n }\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n const region = new EntityRegion();\n\n region.addPoint( this.entity._position );\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n this._abs_position = null; // キャッシュを無効化\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n let entity = this.entity;\n\n // this._abs_position を更新\n this._updateAbsPosition();\n\n var mlocs_to_gocs = this._abs_position.getMlocsToGocsMatrix( GeoMath.createMatrix() );\n var entity_to_mlocs = mul_RS( entity._rotation, entity._scale, GeoMath.createMatrix() );\n var entity_to_gocs = GeoMath.mul_AA( mlocs_to_gocs, entity_to_mlocs, GeoMath.createMatrix() );\n\n // Primitive#transform を設定\n var primitives = stage.getRenderTarget() === RenderTarget.SCENE ? this._primitives : this._pickPrimitives;\n var ptoe_array = this._ptoe_array;\n for ( var i = 0; i < primitives.length; ++i ) {\n var prim = primitives[i];\n var ptoe = ptoe_array[i];\n // prim.transform = entity_to_gocs * ptoe\n GeoMath.mul_AA( entity_to_gocs, ptoe, prim.transform );\n }\n return primitives;\n }\n\n\n /**\n * @summary bboxを利用して簡易的にバウンディングを算出\n *\n * @return {Float64Array} min_lon,min_lat,min_alt,max_lon,max_lat,max_alt\n */\n getBounds()\n {\n const entity = this.entity;\n\n // this._abs_position を更新\n this._updateAbsPosition();\n\n const mlocs_to_gocs = this._abs_position.getMlocsToGocsMatrix( GeoMath.createMatrix() );\n const entity_to_mlocs = mul_RS( entity._rotation, entity._scale, GeoMath.createMatrix() );\n const entity_to_gocs = GeoMath.mul_AA( mlocs_to_gocs, entity_to_mlocs, GeoMath.createMatrix() );\n\n // Primitive#transform を設定\n const primitives = this._primitives;\n const ptoe_array = this._ptoe_array;\n\n let min_lon = Number.MAX_VALUE;\n let max_lon = -Number.MAX_VALUE;\n let min_lat = Number.MAX_VALUE;\n let max_lat = -Number.MAX_VALUE;\n let min_alt = Number.MAX_VALUE;\n let max_alt = -Number.MAX_VALUE;\n\n let transform = new Float64Array( 4 * 4 );\n for ( var i = 0; i < primitives.length; ++i ) {\n const prim = primitives[i];\n const ptoe = ptoe_array[i];\n // prim.transform = entity_to_gocs * ptoe\n GeoMath.mul_AA( entity_to_gocs, ptoe, transform );\n\n const bbox = prim.bbox;\n const bbox0_x = bbox[0][0]*transform[0] + bbox[0][1]*transform[4] + bbox[0][2]*transform[8] + transform[12];\n const bbox0_y = bbox[0][0]*transform[1] + bbox[0][1]*transform[5] + bbox[0][2]*transform[9] + transform[13];\n const bbox0_z = bbox[0][0]*transform[2] + bbox[0][1]*transform[6] + bbox[0][2]*transform[10] + transform[14];\n\n const bbox1_x = bbox[1][0]*transform[0] + bbox[1][1]*transform[4] + bbox[1][2]*transform[8] + transform[12];;\n const bbox1_y = bbox[1][0]*transform[1] + bbox[1][1]*transform[5] + bbox[1][2]*transform[9] + transform[13];;\n const bbox1_z = bbox[1][0]*transform[2] + bbox[1][1]*transform[6] + bbox[1][2]*transform[10] + transform[14];;\n\n let points0 = new GeoPoint();\n points0.setFromGocs( [bbox0_x, bbox0_y, bbox0_z] );\n let points1 = new GeoPoint();\n points1.setFromGocs( [bbox1_x, bbox1_y, bbox1_z] );\n\n // bbox0\n let lon = points0.longitude;\n let lat = points0.latitude;\n let alt = points0.altitude;\n\n if ( lon < min_lon ) min_lon = lon;\n if ( lon > max_lon ) max_lon = lon;\n if ( lat < min_lat ) min_lat = lat;\n if ( lat > max_lat ) max_lat = lat;\n if ( alt < min_alt ) min_alt = alt;\n if ( alt > max_alt ) max_alt = alt;\n\n // bbox1\n lon = points1.longitude;\n lat = points1.latitude;\n alt = points1.altitude;\n\n if ( lon < min_lon ) min_lon = lon;\n if ( lon > max_lon ) max_lon = lon;\n if ( lat < min_lat ) min_lat = lat;\n if ( lat > max_lat ) max_lat = lat;\n if ( alt < min_alt ) min_alt = alt;\n if ( alt > max_alt ) max_alt = alt;\n }\n const bounds_array = new Float64Array( 3 * 2 );\n bounds_array[0] = min_lon;\n bounds_array[1] = min_lat;\n bounds_array[2] = min_alt;\n bounds_array[3] = max_lon;\n bounds_array[4] = max_lat;\n bounds_array[5] = max_alt;\n\n return bounds_array;\n }\n\n\n /**\n * @summary 高度モードが変更されたときに呼び出される\n */\n onChangeAltitudeMode()\n {\n this._abs_position = null; // キャッシュを無効化\n }\n\n\n /**\n * @summary 位置が変更されたときに呼び出される\n */\n onChangePosition()\n {\n this.needToCreateRegions();\n this._abs_position = null;\n }\n\n\n /**\n * @summary 絶対高度位置を更新\n *\n * 出力: this._abs_position\n *\n * @private\n */\n _updateAbsPosition()\n {\n if ( this._abs_position !== null ) {\n // キャッシュされている\n return;\n }\n\n let entity = this.entity;\n\n this._abs_position = entity._position.clone();\n\n switch ( entity.altitude_mode ) {\n case AltitudeMode.RELATIVE:\n this._abs_position.altitude += entity._getElevation();\n break;\n\n case AltitudeMode.CLAMP:\n this._abs_position.altitude = entity._getElevation();\n break;\n\n default: // AltitudeMode.ABSOLUTE\n break;\n }\n }\n\n}\n\n\n// 等倍を表すベクトル\nconst sameScaleVector3 = GeoMath.createVector3( [1, 1, 1] );\n\n\n/**\n * @summary 回転行列 * 倍率\n *\n * @param {mapray.Matrix} rmat 回転行列\n * @param {mapray.Vector3} svec 倍率ベクトル\n * @param {mapray.Matrix} dst 結果\n *\n * @return {mapray.Matrix} dst\n *\n * @private\n */\nfunction\nmul_RS( rmat, svec, dst )\n{\n let sx = svec[0];\n let sy = svec[1];\n let sz = svec[2];\n \n dst[ 0] = rmat[ 0] * sx;\n dst[ 1] = rmat[ 1] * sx;\n dst[ 2] = rmat[ 2] * sx;\n dst[ 3] = 0;\n\n dst[ 4] = rmat[ 4] * sy;\n dst[ 5] = rmat[ 5] * sy;\n dst[ 6] = rmat[ 6] * sy;\n dst[ 7] = 0;\n\n dst[ 8] = rmat[ 8] * sz;\n dst[ 9] = rmat[ 9] * sz;\n dst[10] = rmat[10] * sz;\n dst[11] = 0;\n\n dst[12] = 0;\n dst[13] = 0;\n dst[14] = 0;\n dst[15] = 1;\n \n return dst;\n}\n\n\nexport default ModelEntity;\n","'use strict';\nvar TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS = require('../internals/typed-array-constructors-require-wrappers');\nvar exportTypedArrayStaticMethod = require('../internals/array-buffer-view-core').exportTypedArrayStaticMethod;\nvar typedArrayFrom = require('../internals/typed-array-from');\n\n// `%TypedArray%.from` method\n// https://tc39.github.io/ecma262/#sec-%typedarray%.from\nexportTypedArrayStaticMethod('from', typedArrayFrom, TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS);\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport polygon_vs_code from \"./shader/polygon.vert\";\nimport polygon_fs_code from \"./shader/polygon.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary 多角形分専用マテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n */\nclass PolygonMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, options = {} )\n {\n super( glenv, polygon_vs_code, options.ridMaterial ? rid_fs_code : polygon_fs_code );\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n var props = primitive.properties;\n var opacity = (props.opacity !== undefined) ? props.opacity : PolygonMaterial.DEFAULT_OPACITY;\n return opacity < 1.0;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // 変換行列\n // u_obj_to_clip, u_obj_to_view\n this.setObjToClip( stage, primitive );\n this.setObjToView( stage, primitive );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // 基本色\n // vec4 u_color\n var param_color = (props.color !== undefined) ? props.color : PolygonMaterial.DEFAULT_COLOR;\n var param_opacity = (props.opacity !== undefined) ? props.opacity : PolygonMaterial.DEFAULT_OPACITY;\n\n var color = PolygonMaterial._color;\n GeoMath.copyVector3( param_color, color );\n color[3] = param_opacity;\n this.setVector4( \"u_color\", color );\n\n // 照光の有無\n // bool u_lighting\n this.setBoolean( \"u_lighting\", props.lighting );\n\n // ライト逆方向 (視点座標系) と強さ\n // vec3 u_light_dir\n this.setVector3( \"u_light_dir\", [0, 0, 1] );\n }\n }\n\n}\n\n\n// クラス定数の定義\n{\n PolygonMaterial.DEFAULT_COLOR = GeoMath.createVector3f( [1.0, 1.0, 1.0] );\n PolygonMaterial.DEFAULT_OPACITY = 1.0;\n\n // 計算用一時領域\n PolygonMaterial._color = GeoMath.createVector4f();\n}\n\n\nexport default PolygonMaterial;\n","/**\n * @summary 多角形を三角形に分割\n *\n * @classdesc\n * 入力した多角形を三角形に分割して結果を返す。
\n * 構築子と addBoundary() でで多角形を入力して、run() メソッドでそれを三角形に分割して返す。
\n *\n * 実装のアルゴリズムと用語は基本的にコンピュータ・ジオメトリ (近代科学社) の第3章「多角形の三角形分割」を参考にしている。
\n *\n * @memberof mapray\n * @private\n */\nclass Triangulator {\n\n /**\n * @desc\n * points は多角形の頂点の xy 座標の配列である。
\n * 座標系は x 軸が右方向、y 軸が上方向を想定している。
\n *\n * @param {number[]} points 頂点配列\n * @param {number} offset 最初の頂点のインデックス (>= 0)\n * @param {number} stride 頂点の間隔 (>= 2)\n * @param {number} count 頂点数\n */\n constructor( points, offset, stride, count )\n {\n this._points = new Float64Array( 2 * count );\n this._polygons = new Set();\n\n // 頂点座標を複製\n let src = offset;\n let dst = 0;\n for ( let i = 0; i < count; ++i ) {\n this._points[dst ] = points[src ];\n this._points[dst + 1] = points[src + 1];\n src += stride;\n dst += 2;\n }\n }\n\n\n /**\n * @summary 多角形の境界を追加\n *\n * @desc\n * インデックスの順序は外側境界のときは反時計回り、内側境界のときは時計回りでなければならない。
\n * 内側境界を追加するときは、その外側の境界も追加しなければならない。追加の順序はどちらが先でも良い。
\n * 境界内または複数の境界間でエッジが交差してはならない。同じように頂点が他の頂点またはエッジの上に乗ってはならない。
\n *\n * @param {number[]} indices 多角形の境界を表す 3 個以上の頂点インデックス\n */\n addBoundary( indices )\n {\n this._polygons.add( Polygon.create( this._points, indices ) );\n }\n\n\n /**\n * @summary 多角形を三角形に分割して結果を取得\n *\n * @desc\n * 各々の三角形は 3 つの整数インデックスが反時計回りに並べて表現される。\n * すべての三角形に対して、その表現を連結した配列を出力する。
\n * インデックスは入力頂点の最初の頂点を 0 とする。
\n *\n * @throws Error 想定外の多角形\n *\n * @return {Uint32Array} 三角形の頂点インデックスの配列\n */\n run()\n {\n // 多角形を y 単調多角形に分割\n this._makeYMonotonePolygons();\n\n let triangles = new Uint32Array( 3 * this._numTriangles() );\n let offset = 0;\n\n // 各 y 単調多角形を三角形に分割\n for ( let polygon of this._polygons ) {\n let temp = this._makeTriangleArray( polygon );\n triangles.set( temp, offset );\n offset += temp.length;\n };\n\n return triangles;\n }\n\n\n /**\n * @summary 三角形の数を取得\n *\n * @return {number} 三角形の数\n *\n * @private\n */\n _numTriangles()\n {\n let count = 0;\n\n for ( let polygon of this._polygons ) {\n count += polygon.numVertices() - 2;\n };\n\n return count;\n }\n\n\n /**\n * @summary 多角形を y 単調多角形に分割\n *\n * @desc\n * this._polygons を y 軸単調な部分多角形に分割する。
\n *\n * @private\n */\n _makeYMonotonePolygons()\n {\n let vertices = this._getYOrderedVertices();\n let edge_mgr = new EdgeManager();\n let diag_mgr = new DiagonalManager( this._polygons );\n\n for ( let i = 0; i < vertices.length; ++i ) {\n let vertex = vertices[i];\n\n switch ( vertex.getVertexType() ) {\n case \"start\": {\n let ledge = vertex.getFrontEdge();\n edge_mgr.addEdge( ledge, vertex );\n } break;\n\n case \"end\": {\n let ledge = vertex.getBackEdge();\n let lhelp = edge_mgr.getHelper( ledge );\n if ( lhelp.getVertexType() == \"merge\" ) {\n diag_mgr.addDiagonal( vertex, lhelp );\n }\n edge_mgr.removeEdge( ledge );\n } break;\n\n case \"split\": {\n let nedge = edge_mgr.findNearestLeftEdge( vertex );\n diag_mgr.addDiagonal( vertex, edge_mgr.getHelper( nedge ) );\n edge_mgr.setHelper( nedge, vertex );\n\n let redge = vertex.getFrontEdge();\n edge_mgr.addEdge( redge, vertex );\n } break;\n\n case \"merge\": {\n let redge = vertex.getBackEdge();\n let rhelp = edge_mgr.getHelper( redge );\n if ( rhelp.getVertexType() == \"merge\" ) {\n diag_mgr.addDiagonal( vertex, rhelp );\n }\n edge_mgr.removeEdge( redge );\n\n let nedge = edge_mgr.findNearestLeftEdge( vertex );\n let nhelp = edge_mgr.getHelper( nedge );\n if ( nhelp.getVertexType() == \"merge\" ) {\n diag_mgr.addDiagonal( vertex, nhelp );\n }\n edge_mgr.setHelper( nedge, vertex );\n } break;\n\n default: { // \"regular\"\n if ( vertex.isRightInner() ) {\n // vertex の局所右は多角形の内側\n let uedge = vertex.getBackEdge();\n let uhelp = edge_mgr.getHelper( uedge );\n if ( uhelp.getVertexType() == \"merge\" ) {\n diag_mgr.addDiagonal( vertex, uhelp );\n }\n edge_mgr.removeEdge( uedge );\n\n let dedge = vertex.getFrontEdge();\n edge_mgr.addEdge( dedge, vertex );\n }\n else {\n // vertex の局所右は多角形の外側\n let nedge = edge_mgr.findNearestLeftEdge( vertex );\n let nhelp = edge_mgr.getHelper( nedge );\n if ( nhelp.getVertexType() == \"merge\" ) {\n diag_mgr.addDiagonal( vertex, nhelp );\n }\n edge_mgr.setHelper( nedge, vertex );\n }\n } break;\n }\n }\n\n // this._polygons を対角線により分割\n diag_mgr.splitPolygons();\n }\n\n\n /**\n * @summary 上から順の頂点配列を取得\n *\n * @return {Vertex[]} Y 位置降順の頂点配列\n *\n * @private\n */\n _getYOrderedVertices()\n {\n let vertices = [];\n\n for ( let polygon of this._polygons ) {\n Array.prototype.push.apply( vertices, polygon.getVertices() );\n };\n\n return vertices.sort( (a, b) => Vertex.comparePositionY( b, a ) );\n }\n\n\n /**\n * @summary 多角形を三角形に分割\n *\n * @desc\n * y 単調多角形 polygon を 1 つ以上の三角形に分割して返す。
\n * 1 つの三角形は 3 つの整数インデックスで表現され、三角形数の3倍の長さの整数配列を返す。\n * このインデックスは入力頂点配列に対するもので、3 頂点は反時計回りで格納される。
\n *\n * @param {Polygon} polygon y 単調多角形\n *\n * @return {Uint32Array} 三角形の配列\n *\n * @private\n */\n _makeTriangleArray( polygon )\n {\n let ti = 0;\n let triangles = new Uint32Array( 3 * (polygon.numVertices() - 2) );\n\n let stack = new Stack();\n\n let vi = 0;\n let vertices = polygon.getVertices().sort( (a, b) => Vertex.comparePositionY( b, a ) );\n let is_right = (vertices[0].prev === vertices[1]); // スタックのチェインが右側か?\n\n stack.push( vertices[vi++] );\n stack.push( vertices[vi++] );\n\n while ( vi < vertices.length - 1 ) {\n let vertex = vertices[vi];\n\n let is_same_chain = is_right ? (vertex === stack.top.prev) : (vertex === stack.top.next);\n\n if ( is_same_chain ) {\n let v2 = stack.pop();\n while ( stack.size > 0 ) {\n let v3 = stack.top;\n let [a, b] = is_right ? [v2, v3] : [v3, v2];\n if ( Vertex.isCCW( vertex, a, b ) ) {\n // 対角線が多角形の内部 -> 三角形を取り出す\n triangles[ti++] = vertex.id;\n triangles[ti++] = a.id;\n triangles[ti++] = b.id;\n v2 = stack.pop();\n }\n else {\n break;\n }\n }\n stack.push( v2 ); // 最後にポップした頂点をスタックに戻す\n stack.push( vertex );\n }\n else {\n // すべての頂点をスタックからポップ\n let v2 = stack.pop();\n while ( stack.size > 0 ) {\n // 三角形を取り出す\n let v3 = stack.pop();\n let [a, b] = is_right ? [v2, v3] : [v3, v2];\n triangles[ti++] = vertex.id;\n triangles[ti++] = a.id;\n triangles[ti++] = b.id;\n v2 = v3;\n }\n stack.push( vertices[vi - 1] );\n stack.push( vertex );\n\n // スタックのチェインが左右反転\n is_right = !is_right;\n }\n\n ++vi;\n }\n\n // スタックの最初と最後の頂点を除いて、最下点から\n // スタック上のすべての頂点への対角線を加える\n let vertex = vertices[vi];\n let v2 = stack.pop();\n\n while ( stack.size > 0 ) {\n // 三角形を取り出す\n let v3 = stack.pop();\n let [a, b] = is_right ? [v2, v3] : [v3, v2];\n triangles[ti++] = vertex.id;\n triangles[ti++] = a.id;\n triangles[ti++] = b.id;\n v2 = v3;\n }\n\n return triangles;\n }\n\n}\n\n\n/**\n * @summary 多角形の頂点\n *\n * @private\n */\nclass Vertex {\n\n /**\n * @param {number} id 頂点 ID\n * @param {number} x x 座標\n * @param {number} y y 座標\n */\n constructor( id, x, y )\n {\n this.id = id;\n this.x = x;\n this.y = y;\n this.polygon = null;\n this.next = null;\n this.prev = null;\n }\n\n\n /**\n * @summary 複製\n *\n * @desc\n * ただし this.polygon, this.next, this.prev には null が設定される。
\n *\n * @return {Vertex} this の複製\n */\n clone()\n {\n return new Vertex( this.id, this.x, this.y );\n }\n\n\n /**\n * @summary 頂点タイプを取得\n *\n * @desc\n * 以下のタイプの何れかを返す。\n * \n * \"start\" 出発点 (□)\n * \"end\" 最終点 (■)\n * \"split\" 分離点 (▲)\n * \"merge\" 統合点 (▼)\n * \"regular\" 普通点 (●)\n *
\n *\n * @return {string} 頂点タイプ\n */\n getVertexType()\n {\n let prev = this.prev;\n let next = this.next;\n let cprev = Vertex.comparePositionY( prev, this );\n let cnext = Vertex.comparePositionY( next, this );\n\n if ( (cprev > 0 && cnext > 0) || (cprev < 0 && cnext < 0) ) {\n // 両側の点が上、または両側の点が下にある\n\n // r: (prev -> this) ベクトルに対して左 (反時計回り) に直角なベクトル\n let rx = prev.y - this.y;\n let ry = this.x - prev.x;\n\n // v: (this -> next) ベクトル\n let vx = next.x - this.x;\n let vy = next.y - this.y;\n\n // dot( r, v ): 負数なら内角が 180 度より大きい\n let dot = rx*vx + ry*vy;\n\n if ( cprev > 0 && cnext > 0 ) {\n // 両側の点が上にある\n return (dot < 0) ? \"merge\" : \"end\";\n }\n else {\n // 両側の点が下にある\n return (dot < 0) ? \"split\" : \"start\";\n }\n }\n else {\n // 片方の点が上で片方の点が下\n return \"regular\";\n }\n }\n\n\n /**\n * @summary 前方エッジ\n *\n * @return {Vertex} 前方エッジの始点\n */\n getFrontEdge()\n {\n return this;\n }\n\n\n /**\n * @summary 後方エッジ\n *\n * @return {Vertex} 後方エッジの始点\n */\n getBackEdge()\n {\n return this.prev;\n }\n\n\n /**\n * @summary 頂点の局所右は多角形の内側か?\n *\n * @desc\n * 頂点の局所的な右側は多角形の内側かどうかを返す。
\n * this は regular タイプの頂点でなければならない。
\n *\n * @return {boolean} 内側のとき true, そうでなければ false\n */\n isRightInner()\n {\n // 次の点のほうが下のとき、vertex の右が内側\n return Vertex.comparePositionY( this.next, this ) < 0;\n }\n\n\n /**\n * @summary 点の高さを比較\n *\n * @desc\n * v1 の点が v2 の点より低いとき -1, v1 の点が v2 の点より高いとき +1, 高さが同じとき 0 を返す。
\n *\n * @param {Vertex} v1 頂点 1\n * @param {Vertex} v2 頂点 2\n *\n * @return {number} 比較結果\n */\n static\n comparePositionY( v1, v2 )\n {\n if ( v1.y < v2.y || (v1.y == v2.y && v1.x > v2.x) ) {\n // v1 (<) v2\n return -1;\n }\n else if ( v1.y > v2.y || (v1.y == v2.y && v1.x < v2.x) ) {\n // v1 (>) v2\n return 1;\n }\n else {\n // v1 (=) v2\n return 0;\n }\n }\n\n\n /**\n * @summary 3 点は反時計回りか?\n *\n * @param {Vertex} v1 頂点 1\n * @param {Vertex} v2 頂点 2\n * @param {Vertex} v3 頂点 3\n *\n * @return {boolean} 反時計回りのとき true, そうでなければ false\n */\n static\n isCCW( v1, v2, v3 )\n {\n // a = v2 - v1\n let ax = v2.x - v1.x;\n let ay = v2.y - v1.y;\n\n // b = v3 - v1\n let bx = v3.x - v1.x;\n let by = v3.y - v1.y;\n\n // det( |a b| ) > 0\n return ax * by - ay * bx > 0;\n }\n\n}\n\n\n/**\n * @summary 多角形\n *\n * @private\n */\nclass Polygon {\n\n constructor()\n {\n this._first = null;\n }\n\n\n /**\n * @summary 多角形を生成\n *\n * @desc\n * coords は多角形の頂点の xy 座標の配列である。
\n * 座標系は x 軸が右方向、y 軸が上方向を想定している。
\n * indices での頂点の順序は反時計回りである。
\n *\n * @param {number[]} coords 座標配列\n * @param {number[]} indices 多角形の頂点インデックス配列 (indices.length >= 3)\n *\n * @return {Polygon} Polygon インスタンス\n */\n static\n create( coords, indices )\n {\n let poly = new Polygon();\n\n let id = indices[0];\n let base = 2*id;\n let vertex = new Vertex( id, coords[base], coords[base + 1] );\n\n poly._first = vertex;\n\n for ( let i = 1; i < indices.length; ++i ) {\n let before = vertex;\n\n id = indices[i];\n base = 2*id;\n vertex = new Vertex( id, coords[base], coords[base + 1] );\n\n before.next = vertex;\n vertex.prev = before;\n }\n\n // ここで vertex は最後の頂点\n poly._first.prev = vertex;\n vertex.next = poly._first;\n\n poly._updateVertices();\n return poly;\n }\n\n\n /**\n * @summary 多角形を生成\n *\n * @param {Vertex} first 最初の頂点\n *\n * @return {Polygon} Polygon インスタンス\n */\n static\n createByVertex( first )\n {\n let poly = new Polygon();\n\n poly._first = first;\n\n poly._updateVertices();\n return poly;\n }\n\n\n /**\n * @summary 頂点数を取得\n *\n * @return {number} 頂点数\n */\n numVertices()\n {\n let count = 0;\n\n let vertex = this._first;\n ++count;\n\n let end = this._first;\n for ( vertex = vertex.next; vertex !== end; vertex = vertex.next ) {\n ++count;\n }\n\n return count;\n }\n\n\n /**\n * @summary すべての頂点の配列を取得\n *\n * @return {Vertex[]} すべての頂点の配列\n */\n getVertices()\n {\n let array = [];\n\n let vertex = this._first;\n array.push( vertex );\n\n let end = this._first;\n for ( vertex = vertex.next; vertex !== end; vertex = vertex.next ) {\n array.push( vertex );\n }\n\n return array;\n }\n\n\n /**\n * @summary 頂点を更新\n *\n * @private\n */\n _updateVertices()\n {\n let vertex = this._first;\n vertex.polygon = this;\n\n let end = this._first;\n for ( vertex = vertex.next; vertex !== end; vertex = vertex.next ) {\n vertex.polygon = this;\n }\n }\n\n}\n\n\n/**\n * @summary 単調多角形に分割するためのエッジ管理\n *\n * @classdesc\n *
追加されるエッジの局所右は常に多角形の内側になる。
\n *\n * @private\n */\nclass EdgeManager {\n\n /**\n */\n constructor()\n {\n this._edges = new Map();\n }\n\n\n /**\n * @summary エッジを追加\n *\n * @param {Vertex} edge 追加するエッジ\n * @param {Vertex} helper 初期ヘルパー頂点\n */\n addEdge( edge, helper )\n {\n this._edges.set( edge, { helper: helper } );\n }\n\n\n /**\n * @summary エッジを削除\n *\n * @param {Vertex} edge 削除するエッジ\n */\n removeEdge( edge )\n {\n this._edges.delete( edge );\n }\n\n\n /**\n * @summary エッジにヘルパーを設定\n *\n * @param {Vertex} edge 対象エッジ\n * @param {Vertex} helper ヘルパー頂点\n */\n setHelper( edge, helper )\n {\n this._edges.get( edge ).helper = helper;\n }\n\n\n /**\n * @summary エッジのヘルパーを取得\n *\n * @param {Vertex} edge 対象エッジ\n *\n * @return {Vertex} ヘルパー頂点\n */\n getHelper( edge )\n {\n return this._edges.get( edge ).helper;\n }\n\n\n /**\n * @summary 頂点の左側で最も近いエッジを検索\n *\n * @desc\n * vertex を左に (多角形の内側を) 移動しながら最初に突き当たるエッジを取得する。\n * そのようなエッジが常に存在することが前提になっている。
\n *\n * vertex の局所左は常に多角形の内側になる。
\n *\n * @param {Vertex} vertex 対象頂点\n *\n * @return {Vertex} エッジ\n */\n findNearestLeftEdge( vertex )\n {\n // このメソッドが呼び出された時点での前提状態:\n // - どちらかの端点が vertex のエッジは存在しない\n // - 両端の y 座標が同じエッジは存在しない\n // - すべてのエッジは vertex を通る水平線と交差する\n\n // TODO:\n // このメソッドは多角形の頂点数 n に対して計算量は最悪 O(n), 平均は不明\n // 最悪の場合、ポリゴン分割全体の計算量が O(n^2) になってしまう\n // そのため、このめメソッド単体で最悪 O(log n) または平均 O(log n) 以下にしたい\n\n let nearest_edge;\n let min_distance = Number.MAX_VALUE;\n\n let vx = vertex.x;\n let vy = vertex.y;\n\n for ( let [edge,] of this._edges ) {\n // エッジ始点 p\n let px = edge.x;\n let py = edge.y;\n\n // エッジ終点 q\n let qx = edge.next.x;\n let qy = edge.next.y;\n\n // エッジの x 座標勾配\n let gx = (qx - px) / (qy - py);\n\n // 水平線とエッジの交点の x 座標\n let rx = px + (vy - py) * gx;\n\n // vertex と交点 r との符号付き距離 (正のとき r は vertex の左側)\n let distance = vx - rx;\n\n if ( distance > 0 ) {\n if ( distance < min_distance ) {\n // 近いエッジを更新\n min_distance = distance;\n nearest_edge = edge;\n }\n }\n }\n\n if ( nearest_edge === undefined ) {\n // 想定に反して左のエッジが見つからなかった\n throw new Error( \"Probably a degenerate polygon\" );\n }\n\n return nearest_edge;\n }\n\n}\n\n\n/**\n * @summary 対角線\n *\n * @private\n */\nclass Diagonal {\n\n /**\n * @param {Vertex} v1 対角線の端点1\n * @param {Vertex} v2 対角線の端点2\n */\n constructor( v1, v2 )\n {\n this.v1 = v1;\n this.v2 = v2;\n }\n\n}\n\n\n/**\n * @summary 対角線管理\n *\n * @private\n */\nclass DiagonalManager {\n\n /**\n * @param {Set.} polygons 多角形集合\n */\n constructor( polygons )\n {\n this._polygons = polygons;\n this._diagonals = [];\n this._dmap = new DiagonalMap();\n }\n\n\n /**\n * @brief 対角線を追加\n *\n * @param {Vertex} v1 対角線の端点1\n * @param {Vertex} v2 対角線の端点2\n */\n addDiagonal( v1, v2 )\n {\n let diagonal = new Diagonal( v1, v2 );\n\n this._diagonals.push( diagonal );\n this._dmap.addDiagonal( diagonal );\n }\n\n\n /**\n * @brief すべての多角形を対角線で分割\n */\n splitPolygons()\n {\n // 対角線の順序をランダムにして、分割方向が偏らないようにする\n // これにより頂点数 n に対して計算量 O(n log n) が期待できる\n this._shuffleArray( this._diagonals );\n\n // 個々の対角線で多角形を分割\n while ( this._diagonals.length > 0 ) {\n let diagonal = this._diagonals.pop();\n this._dmap.removeDiagonal( diagonal );\n\n let v1 = diagonal.v1;\n let v2 = diagonal.v2;\n let [v1a, v2a] = this._splitPolygonHalf( v1, v2 );\n let [v2b, v1b] = this._splitPolygonHalf( v2, v1 );\n\n let p1 = v1.polygon;\n let p2 = v2.polygon;\n\n if ( p1 === p2 ) {\n // diagonal は多角形 p1 内の対角線\n this._polygons.delete( p1 );\n this._polygons.add( Polygon.createByVertex( v1a ) );\n this._polygons.add( Polygon.createByVertex( v1b ) );\n }\n else {\n // diagonal は多角形 p1, p2 間の対角線\n this._polygons.delete( p1 );\n this._polygons.delete( p2 );\n this._polygons.add( Polygon.createByVertex( v1a ) );\n }\n\n // 対角線の端点を新しく生成された端点に置き換える\n this._replaceVertexInDiagonals( v1, v1a, v1b );\n this._replaceVertexInDiagonals( v2, v2a, v2b );\n }\n }\n\n\n /**\n * @summary 多角形分割の半分の処理\n *\n * @param {Vertex} sv 開始点\n * @param {Vertex} ev 終了点\n *\n * @return {Vertex[]} [新 sv, 新 ev]\n *\n * @private\n */\n _splitPolygonHalf( sv, ev )\n {\n let sc = sv.clone();\n let ec = ev.clone();\n\n sc.prev = sv.prev;\n sc.next = ec;\n sv.prev.next = sc;\n\n ec.prev = sc;\n ec.next = ev.next;\n ev.next.prev = ec;\n\n return [sc, ec];\n }\n\n\n /**\n * @summary 対角線の端点の置き換え\n *\n * @desc\n * 端点の1つが vo の対角線のその端点を va または vb に置き換える。
\n * 候補端点 va, vb のうち、置き換えると対角線が多角形の内側になる方を選ぶ。
\n * 一方が内側で、一方が外側になるはず。
\n *\n * @param {Vertex} vo 元の端点\n * @param {Vertex} va 候補端点 a\n * @param {Vertex} vb 候補端点 b\n *\n * @private\n */\n _replaceVertexInDiagonals( vo, va, vb )\n {\n // 一旦 this._dmap から対角線を抜き取って、端点を書き換えてから戻す\n for ( let diagonal of this._dmap.removeDiagonals( vo ) ) {\n // vo の反対側の端点\n let vo_oppo = (diagonal.v1 === vo) ? diagonal.v2 : diagonal.v1;\n // 対角線の端点の書き換え\n diagonal.v1 = this._testDiagonal( va, vo_oppo ) ? va : vb;\n diagonal.v2 = vo_oppo;\n // 対角線を戻す\n this._dmap.addDiagonal( diagonal );\n }\n }\n\n\n /**\n * @summary 対角線テスト\n *\n * 端点が v1, v2 の線分は多角形の (v1 の) 内側に入るか?
\n *\n * @param {Vertex} v1 端点1\n * @param {Vertex} v2 端点2\n *\n * @return {boolean} 内側に入るとき true, それ以外のとき false\n *\n * @private\n */\n _testDiagonal( v1, v2 )\n {\n // a: v1 から順方向のベクトル\n let ax = v1.next.x - v1.x;\n let ay = v1.next.y - v1.y;\n\n // b: v1 から逆方向のベクトル\n let bx = v1.prev.x - v1.x;\n let by = v1.prev.y - v1.y;\n\n // c: v1 から v2 のベクトル\n let cx = v2.x - v1.x;\n let cy = v2.y - v1.y;\n\n // cross(a) . c > 0\n let aflag = (ax*cy - cx*ay > 0);\n\n // cross(b) . c < 0\n let bflag = (bx*cy - cx*by < 0);\n\n // |ax bx|\n // |ay by|\n let det = ax*by - bx*ay;\n\n return (det >= 0) ? (aflag && bflag) : (aflag || bflag);\n }\n\n\n /**\n * @summary 配列の並びをランダム化\n *\n * @param {array} array 処理対象の配列\n *\n * @private\n */\n _shuffleArray( array )\n {\n // Fisher-Yates のシャッフル\n // TODO: 用途上、再現性のある乱数を使ってもよいし、そのほうが何かあった時に原因をつかみやすい\n\n for ( let i = array.length - 1; i > 0; --i ) {\n let j = Math.floor( Math.random() * (i + 1) ); // 0 <= j <= i\n\n // array[i] と array[j] を交換\n let temp = array[i];\n array[i] = array[j];\n array[j] = temp;\n }\n }\n\n}\n\n\n/**\n * @summary 辞書: 対角線頂点 → 対角線リスト\n *\n * @classdesc\n * 対角線の頂点から、その頂点を端点とする対角線の配列を取得する。
\n * 1 つの頂点に対して対角線は最大 3 本なので、追加、削除、検索の計算量は多角形の頂点数 n に対して O(1) である。
\n *\n * @memberof DiagonalManager\n * @private\n */\nclass DiagonalMap {\n\n constructor()\n {\n // 辞書を生成\n // key: Vertex\n // value: Diagonal[]\n this._map = new Map();\n }\n\n\n /**\n * @summary 対角線を登録\n *\n * @param {Diagonal} diagonal 追加する対角線\n */\n addDiagonal( diagonal )\n {\n this._addDiagonalByVertex( diagonal.v1, diagonal );\n this._addDiagonalByVertex( diagonal.v2, diagonal );\n }\n\n\n /**\n * @summary 対角線の登録を解除\n *\n * @param {Diagonal} diagonal 削除する対角線\n */\n removeDiagonal( diagonal )\n {\n this._removeDiagonalByVertex( diagonal.v1, diagonal );\n this._removeDiagonalByVertex( diagonal.v2, diagonal );\n }\n\n\n /**\n * @summary vertex を端点とする、すべの対角線の登録を解除\n *\n * @param {Vertex} vertex 対角線の端点\n *\n * @return {Diagonal[]} 登録を解除された対角線の配列\n */\n removeDiagonals( vertex )\n {\n let diagonals = this._map.get( vertex );\n\n if ( diagonals !== undefined ) {\n // 辞書から対角線を削除\n let cloned = diagonals.slice();\n for ( let diag of cloned ) {\n this.removeDiagonal( diag );\n }\n return cloned;\n }\n else {\n // 存在しなかった\n return [];\n }\n }\n\n\n /**\n * @summary 対角線を登録\n *\n * @param {Vertex} vertex どちらかの端点\n * @param {Diagonal} diagonal 追加する対角線\n *\n * @private\n */\n _addDiagonalByVertex( vertex, diagonal )\n {\n let array = this._map.get( vertex );\n\n if ( array === undefined ) {\n // vertex に対する最初の対角線\n this._map.set( vertex, [diagonal] );\n }\n else {\n // vertex に対する2つ目以降の対角線\n\n if ( array.indexOf( diagonal ) != -1 ) {\n // 対角線は二重登録されないはずが...\n throw new Error( \"Unexpected\" );\n }\n if ( array.length < 1 || array.length > 2 ) {\n // 同一頂点の対角線は最大3本のはずが...\n throw new Error( \"Unexpected\" );\n }\n\n array.push( diagonal );\n }\n }\n\n\n /**\n * @summary 対角線を削除\n *\n * @param {Vertex} vertex どちらかの端点\n * @param {Diagonal} diagonal 削除する対角線\n *\n * @private\n */\n _removeDiagonalByVertex( vertex, diagonal )\n {\n let array = this._map.get( vertex );\n if ( array === undefined ) {\n // 存在するはずが...\n throw new Error( \"Unexpected\" );\n }\n\n let index = array.indexOf( diagonal );\n if ( index == -1 ) {\n // 存在するはずが...\n throw new Error( \"Unexpected\" );\n }\n\n // 配列から diagonal を削除\n array.splice( index, 1 );\n\n // 配列が空になったら配列を削除\n if ( array.length == 0 ) {\n this._map.delete( vertex );\n }\n }\n\n}\n\n\n/**\n * @summary スタック\n *\n * @private\n */\nclass Stack {\n\n constructor()\n {\n this._array = new Array();\n }\n\n get size() { return this._array.length; }\n\n push( item )\n {\n this._array.push( item );\n }\n\n pop()\n {\n return this._array.pop();\n }\n\n get top()\n {\n let a = this._array;\n return (a.length > 0) ? a[a.length - 1] : null;\n }\n\n}\n\n\nexport default Triangulator;\n","var global = require('../internals/global');\n\nvar globalIsFinite = global.isFinite;\n\n// `Number.isFinite` method\n// https://tc39.github.io/ecma262/#sec-number.isfinite\nmodule.exports = Number.isFinite || function isFinite(it) {\n return typeof it == 'number' && globalIsFinite(it);\n};\n","var $ = require('../internals/export');\nvar numberIsFinite = require('../internals/number-is-finite');\n\n// `Number.isFinite` method\n// https://tc39.github.io/ecma262/#sec-number.isfinite\n$({ target: 'Number', stat: true }, { isFinite: numberIsFinite });\n","/**\n * @summary 凸多角形\n *\n * @memberof mapray\n *\n * @private\n */\nclass ConvexPolygon {\n\n /**\n * 3 点またはそれ以上の頂点座標を反時計回りで指定する。
\n *\n * @param {iterable.} coords 頂点座標の並び x0, y0, x1, y1, ...\n */\n constructor( coords )\n {\n this._vertices = Float64Array.from( coords );\n this._num_vertices = this._vertices.length / 2;\n }\n\n\n /**\n * @summary 凸多角形の頂点数\n *\n * @type {number}\n *\n * @readonly\n */\n get num_vertices()\n {\n return this._num_vertices;\n }\n\n\n /**\n * @summary 凸多角形の頂点座標配列\n *\n * @desc\n * 反時計回りで格納された頂点座標の配列 [x0, y0, x1, y1, ...] を返す。
\n *
返された配列の内容は this に対して変更操作が行われるまで有効である。
\n *\n * @type {number[]}\n *\n * @readonly\n */\n get vertices()\n {\n return this._vertices;\n }\n\n\n /**\n * @summary 矩形から凸多角形を生成\n *\n * @param {number} x_min x 座標の最小値\n * @param {number} y_min y 座標の最小値\n * @param {number} x_max x 座標の最大値\n * @param {number} y_max y 座標の最大値\n *\n * @return {mapray.ConvexPolygon} 凸多角形\n */\n static\n createByRectangle( x_min, y_min, x_max, y_max )\n {\n // 入力座標配列\n const coords = [\n x_min, y_min,\n x_max, y_min,\n x_max, y_max,\n x_min, y_max\n ];\n\n // 凸多角形を生成\n return new ConvexPolygon( coords );\n }\n\n\n /**\n * @summary 妥当性を検査\n *\n * @desc\n *
this が妥当な凸多角形かどうかを確かめる。
\n *\n * @return {boolean} this が妥当なとき true, それ以外のとき false\n */\n isValid()\n {\n if ( this._num_vertices < 3 ) {\n // 3 頂点未満の多角形は非対応\n return false;\n }\n\n // 座標が有効な数値であることをチェック\n for ( let i = 0; i < this._num_vertices; ++i ) {\n let x = this._vertices[2*i ];\n let y = this._vertices[2*i + 1];\n if ( !Number.isFinite( x ) || !Number.isFinite( y ) ) {\n // 有効な数値ではない座標が含まれる\n return false;\n }\n }\n\n // 長さ 0 の稜線が無いことをチェック\n for ( let ei = 0; ei < this._num_vertices; ++ei ) {\n let si = (ei != 0) ? ei - 1 : this._num_vertices - 1;\n\n let sx = this._vertices[2*si ];\n let sy = this._vertices[2*si + 1];\n\n let ex = this._vertices[2*ei ];\n let ey = this._vertices[2*ei + 1];\n\n if ( sx == ex && sy == ey ) {\n // 同一位置の隣接頂点が含まれる\n return false;\n }\n }\n\n // 内角が 0 でない、または 180 度より大きくないことをチェック\n for ( let oi = 0; oi < this._num_vertices; ++oi ) {\n let ox = this._vertices[2*oi ];\n let oy = this._vertices[2*oi + 1];\n\n // 前方への方向ベクトル\n let fi = (oi == this._num_vertices - 1) ? 0 : oi + 1;\n let fx = this._vertices[2*fi ] - ox;\n let fy = this._vertices[2*fi + 1] - oy;\n\n // 後方への方向ベクトル\n let bi = (oi == 0) ? this._num_vertices - 1 : oi - 1;\n let bx = this._vertices[2*bi ] - ox;\n let by = this._vertices[2*bi + 1] - oy;\n\n // 面積と内積\n let det = fx * by - bx * fy;\n let dot = fx * bx + fy * by;\n\n if ( det < 0 || (det == 0 && dot > 0) ) {\n // 内角 θ は 0 < θ <= 180 ではない\n return false;\n }\n }\n\n return true;\n }\n\n\n /**\n * @summary 交差凸多角形を取得\n *\n * @desc\n * 凸多角形 this と凸多角形 polygon が交差すれば、交差部分の凸多角形を返す。
\n * this と polygon が交差しないときは null を返す。
\n * this が polygon を包含するときは polygon を返す可能性がある。
\n *\n * @param {ConvexPolygon} polygon 凸多角形\n *\n * @return {?ConvexPolygon} 交差凸多角形、ただし交差しないときは null\n *\n * @throws Error 凸多角形の切り取りに失敗\n */\n getIntersection( polygon )\n {\n try {\n return this._clip_by_polygon( polygon );\n }\n catch ( e ) {\n throw new Error( \"ConvexPolygon#getIntersection failed\" );\n }\n }\n\n\n /**\n * @summary 凸多角形同士に交差があるかどうか?\n *\n * @desc\n * 凸多角形 this と凸多角形 polygon が交差するかどうかを返す。
\n *\n * @param {ConvexPolygon} polygon 凸多角形\n *\n * @return {boolean} 交差するとき true, 交差しないとき false\n *\n * @throws Error 交差の確認に失敗\n */\n hasIntersection( polygon )\n {\n try {\n return this._clip_by_polygon( polygon ) !== null;\n }\n catch ( e ) {\n throw new Error( \"ConvexPolygon#hasIntersection failed\" );\n }\n }\n\n\n /**\n * @summary 凸多角形を包含するか?\n *\n * @desc\n * 凸多角形 this は凸多角形 polygon を包含するかどうかを返す。
\n *\n * @param {ConvexPolygon} polygon 凸多角形\n *\n * @return {boolean} this が polygon を包含するとき true, それ以外は false\n */\n includes( polygon )\n {\n for ( let ei = 0; ei < this._num_vertices; ++ei ) {\n let si = (ei != 0) ? ei - 1 : this._num_vertices - 1;\n\n // 直線上の 1 点\n let px = this._vertices[2*si ];\n let py = this._vertices[2*si + 1];\n\n // 直線の内側方向\n let nx = py - this._vertices[2*ei + 1];\n let ny = this._vertices[2*ei] - px;\n\n // 判定数値\n let dval = px*nx + py*ny;\n\n // this の si-ei 稜線の\n for ( let i = 0; i < polygon._num_vertices; ++i ) {\n // polygon の 1 点\n let qx = polygon._vertices[2*i ];\n let qy = polygon._vertices[2*i + 1];\n\n if ( qx*nx + qy*ny < dval ) {\n // polygon の 1 点が this の外側\n // polygon は this の完全に内側ではない\n return false;\n }\n }\n }\n\n // polygon は this の完全に内側である\n return true;\n }\n\n\n /**\n * @summary 凸多角形により凸多角形を切り取る\n *\n * @desc\n * 凸多角形 this により凸多角形 polygon を切り取った凸多角形を返す。
\n * this が polygon を包含するときは polygon を返す。
\n * this と polygon が交差しないときは null を返す。
\n *\n * @param {ConvexPolygon} polygon 凸多角形\n *\n * @return {?ConvexPolygon} 凸多角形または null\n *\n * @throws Error 凸多角形の切り取りに失敗\n *\n * @private\n */\n _clip_by_polygon( polygon )\n {\n let current = polygon;\n\n for ( let ei = 0; ei < this._num_vertices; ++ei ) {\n let si = (ei != 0) ? ei - 1 : this._num_vertices - 1;\n\n // 直線上の 1 点\n let px = this._vertices[2*si ];\n let py = this._vertices[2*si + 1];\n\n // 直線の内側方向\n let nx = py - this._vertices[2*ei + 1];\n let ny = this._vertices[2*ei] - px;\n\n // 判定数値\n let dval = px*nx + py*ny;\n\n // 半空間による切り取り\n current = current._clip_by_halfspace( nx, ny, dval );\n if ( current === null ) {\n // this と polygon は交差しない\n break;\n }\n }\n\n return current;\n }\n\n\n /**\n * @summary 半空間により凸多角形を切り取る\n *\n * @desc\n * 半空間により凸多角形 this を切り取ったり、その凸多角形を返す。
\n * 半空間が this を内包するときは this を返す。
\n * 半空間と this が交差しないときは null を返す。
\n *\n * @param {number} nx 半空間の内側方向の x 座標\n * @param {number} ny 半空間の内側方向の y 座標\n * @param {number} dval 判定用数値\n *\n * @return {?ConvexPolygon} 凸多角形または null\n *\n * @throws Error 半空間による切り取りに失敗\n *\n * @private\n */\n _clip_by_halfspace( nx, ny, dval )\n {\n // 半空間の境界線からの距離範囲\n let dist_min = Number.MAX_VALUE;\n let dist_max = -Number.MAX_VALUE;\n\n for ( let i = 0; i < this._num_vertices; ++i ) {\n // 頂点座標\n let px = this._vertices[2*i ];\n let py = this._vertices[2*i + 1];\n\n // dist == 0 半空間の境界線上\n // dist > 0 半空間の内側\n // dist < 0 半空間の外側\n let dist = px*nx + py*ny - dval;\n\n // 最大最小を更新\n dist_min = Math.min( dist_min, dist );\n dist_max = Math.max( dist_max, dist );\n }\n\n if ( dist_min >= 0 ) {\n // 半空間は this を内包する\n return this;\n }\n\n if ( dist_max <= 0 ) {\n // 半空間 と this は交差しない\n return null;\n }\n\n // ここで dist_min < 0 < dist_max なので、半空間の境界線は凸多角形\n // this の内部を通過している (接していない)\n\n return this._clip_by_crossed_halfspace( nx, ny, dval );\n }\n\n\n /**\n * @summary 半空間により凸多角形を切り取る (一部交差前提)\n *\n * @desc\n * 半空間により凸多角形 this を切り取ったり、その凸多角形を返す。
\n * このメソッドは凸多角形 this の境界線と半空間の境界線が異なる\n * 2 点で交差していることが前提になっている。
\n *\n * @param {number} nx 半空間の内側方向の x 座標\n * @param {number} ny 半空間の内側方向の y 座標\n * @param {number} dval 判定用数値\n *\n * @return {ConvexPolygon} 凸多角形\n *\n * @throws Error 半空間による切り取りに失敗\n *\n * @private\n */\n _clip_by_crossed_halfspace( nx, ny, dval )\n {\n let [ce0, ce1] = this._get_cross_edges_by_crossed_halfspace_boundary( nx, ny, dval );\n\n let polygon = [];\n\n // 最初の頂点\n polygon.push( ce0.qx );\n polygon.push( ce0.qy );\n\n // 中間頂点のインデックス\n let first_i = ce0.ei; // 最初の中間頂点\n let last_i = ce1.ei; // 最後の中間頂点の次\n\n // 中間の頂点\n for ( let i = first_i; i != last_i; i = (i + 1) % this._num_vertices ) {\n polygon.push( this._vertices[2*i ] );\n polygon.push( this._vertices[2*i + 1] );\n }\n\n // 最後の頂点\n polygon.push( ce1.qx );\n polygon.push( ce1.qy );\n\n return new ConvexPolygon( polygon );\n }\n\n\n /**\n * @summary 半空間境界線と交差する2稜線のデータを取得 (2点交差前提)\n *\n * @desc\n * 2要素の配列を返す。
\n * 最初の要素は切り取りにより前方が残される稜線のデータである。
\n * 次の要素は切り取りにより後方が残される稜線のデータである。
\n *\n * 配列の各要素のオブジェクトプロパティは次のようになる。
\n * \n * ei: 境界線と交差した稜線の終点インデックス\n * qx: 境界線と稜線が交差した位置の x 座標\n * qy: 境界線と稜線が交差した位置の y 座標\n *
\n *\n * @param {number} nx 半空間の内側方向の x 座標\n * @param {number} ny 半空間の内側方向の y 座標\n * @param {number} dval 判定用数値\n *\n * @return {object[]} 2稜線の交差データ\n *\n * @throws Error 2点の交差が見つからなかった\n *\n * @private\n */\n _get_cross_edges_by_crossed_halfspace_boundary( nx, ny, dval )\n {\n let cross_edges = new Array( 2 );\n\n for ( let si = 0, ce_count = 0; ce_count < 2; ++si ) {\n if ( si == this._num_vertices ) {\n // 妥当でない凸多角形、数値計算誤差、非数値の混入などで\n // 2点の交差が見つからず、無限ループになる可能性がある\n // それを避けるため、すべての稜線を調べ終わったら例外で\n // 強制的に終了する\n throw new Error( \"cross edges could not be found\" );\n }\n\n let ei = (si + 1) % this._num_vertices;\n\n // 稜線の始点位置\n let sx = this._vertices[2*si ];\n let sy = this._vertices[2*si + 1];\n\n // 稜線の終点位置\n let ex = this._vertices[2*ei ];\n let ey = this._vertices[2*ei + 1];\n\n // 境界線からの距離\n let sd = sx*nx + sy*ny - dval;\n let ed = ex*nx + ey*ny - dval;\n\n // 半空間境界線と si-ei 稜線の交差があればデータを追加\n if ( (sd <= 0 && 0 < ed) || (ed <= 0 && 0 < sd) ) {\n let t = sd / (sd - ed);\n let qx = sx + (ex - sx) * t;\n let qy = sy + (ey - sy) * t;\n\n cross_edges[(sd < ed) ? 0 : 1] = { ei, qx, qy };\n\n ++ce_count;\n }\n }\n\n return cross_edges;\n }\n\n}\n\n\nexport default ConvexPolygon;\n","import Entity from \"./Entity\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport PolygonMaterial from \"./PolygonMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport GeoRegion from \"./GeoRegion\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport Triangulator from \"./Triangulator\";\nimport QAreaManager from \"./QAreaManager\";\nimport ConvexPolygon from \"./ConvexPolygon\";\nimport AreaUtil from \"./AreaUtil\";\nimport Type from \"./animation/Type\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary 多角形エンティティ\n * @memberof mapray\n * @extends mapray.Entity\n */\nclass PolygonEntity extends Entity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n this._extruded_height = 0.0;\n this._color = GeoMath.createVector3( [1, 1, 1] );\n this._opacity = 1.0;\n\n // 頂点管理\n this._boundaries = []; // Boundary のリスト\n this._position = null; // 中央付近の GeoPoint\n\n // this._producer\n // this._is_flake_mode\n if ( this.altitude_mode === AltitudeMode.CLAMP ) {\n this._producer = new FlakePrimitiveProducer( this );\n this._is_flake_mode = true;\n }\n else {\n this._producer = new PrimitiveProducer( this );\n this._is_flake_mode = false;\n }\n\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * @summary 押し出し量(0より大きい値)\n * @type {number}\n */\n set extruded_height( value )\n {\n var prev = this._extruded_height;\n\n if ( prev !== value ) {\n this._extruded_height = value;\n this._producer.onChangeExtruded();\n }\n }\n\n\n /**\n * @summary 押し出し量\n * @type {number}\n */\n get extruded_height() { return this._extruded_height; }\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return (!this._is_flake_mode) ? this._producer : null;\n }\n\n\n /**\n * @override\n */\n getFlakePrimitiveProducer()\n {\n return (this._is_flake_mode) ? this._producer : null;\n }\n\n\n /**\n * @override\n */\n onChangeAltitudeMode( prev_mode )\n {\n if ( this.altitude_mode === AltitudeMode.CLAMP ) {\n this._producer = new FlakePrimitiveProducer( this );\n this._is_flake_mode = true;\n }\n else {\n this._producer = new PrimitiveProducer( this );\n this._is_flake_mode = false;\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector3 = Type.find( \"vector3\" );\n\n // パラメータ名: color\n // パラメータ型: vector3\n // 色\n block.addEntry( \"color\", [vector3], null, value => {\n this.setColor( value );\n } );\n \n // パラメータ名: opacity\n // パラメータ型: number\n // 不透明度\n block.addEntry( \"opacity\", [number], null, value => {\n this.setOpacity( value );\n } );\n \n // パラメータ名: height\n // パラメータ型: number\n // 線の太さ\n block.addEntry( \"height\", [number], null, value => {\n this.setExtrudedHeight( value );\n } );\n }\n\n\n /**\n * @summary 基本色を設定\n * @param {mapray.Vector3} color 基本色\n */\n setColor( color )\n {\n if ( this._color[0] !== color[0] ||\n this._color[1] !== color[1] ||\n this._color[2] !== color[2] ) {\n GeoMath.copyVector3( color, this._color );\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 不透明度を設定\n * @param {number} opacity 不透明度\n */\n setOpacity( opacity )\n {\n if ( this._opacity !== opacity ) {\n this._opacity = opacity;\n this._producer.onChangeProperty();\n }\n }\n\n\n /**\n * @summary 押し出し量を設定\n * @param {number} opacity 押し出し量\n */\n setExtrudedHeight( height )\n {\n this.extruded_height = height;\n }\n\n\n /**\n * @summary 外側境界を追加\n *\n * @desc\n * points は [lon_0, lat_0, alt_0, lon_1, lat_1, alt_1, ...] のような形式で配列を与える。
\n *\n * @param {number[]} points 頂点の配列\n */\n addOuterBoundary( points )\n {\n this._addBoundary( points, false );\n }\n\n\n /**\n * @summary 内側境界を追加\n *\n * @desc\n * points は [lon_0, lat_0, alt_0, lon_1, lat_1, alt_1, ...] のような形式で配列を与える。
\n *\n * @param {number[]} points 頂点の配列\n */\n addInnerBoundary( points )\n {\n this._addBoundary( points, true );\n }\n\n\n /**\n * @summary すべての頂点のバウンディングを算出\n *\n * @override\n * @return {mapray.GeoRegion} バウンディング情報を持ったGeoRegion\n */\n getBounds()\n {\n const region = new GeoRegion();\n for ( let bo of this._boundaries ) {\n region.addPointsAsArray( bo.points );\n }\n return region;\n }\n\n /**\n * @summary 境界を追加\n *\n * @desc\n * addOuterBoundary(), addInnerBoundary() の実装である。
\n *\n * @param {number[]} points 頂点の配列\n *\n * @private\n */\n _addBoundary( points, is_inner )\n {\n this._boundaries.push( new Boundary( points, is_inner ) );\n this._position = null;\n\n // 境界の変更を通知\n this._producer.onChangeBoundary();\n }\n\n\n /**\n * @summary 専用マテリアルを取得\n * @private\n */\n _getMaterial( render_target )\n {\n var scene = this.scene;\n if ( render_target === RenderTarget.SCENE ) {\n if ( !scene._PolygonEntity_material ) {\n // scene にマテリアルをキャッシュ\n scene._PolygonEntity_material = new PolygonMaterial( scene.glenv );\n }\n return scene._PolygonEntity_material;\n }\n else if (render_target === RenderTarget.RID) {\n if ( !scene._PolygonEntity_material_pick ) {\n // scene にマテリアルをキャッシュ\n scene._PolygonEntity_material_pick = new PolygonMaterial( scene.glenv, { ridMaterial: true } );\n }\n return scene._PolygonEntity_material_pick;\n }\n }\n\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n // json.boundaries\n for ( let boundary of json.boundaries ) {\n if ( boundary.type == \"inner\" ) {\n this.addInnerBoundary( boundary.points );\n }\n else {\n this.addOuterBoundary( boundary.points );\n }\n }\n\n // json.extruded_height\n if ( json.extruded_height !== undefined ) {\n this.extruded_height = json.extruded_height;\n }\n\n // json.color\n // .opacity\n if ( json.color !== undefined ) GeoMath.copyVector3( json.color, this._color );\n if ( json.opacity !== undefined ) this._opacity = json.opacity;\n }\n\n\n /**\n * @summary 中央位置を取得\n *\n * @desc\n * 中央位置を計算して返す。多角形が存在しないときは null を返す。
\n *\n * 中央位置が変化する可能性があるときは this._position にを null を設定すること。
\n *\n * \n * 入力: this._boundaries\n *
\n *\n * @return {mapray.GeoPoint} 中央位置 (高度は 0) または null\n *\n * @private\n */\n _getPosition()\n {\n if ( this._position !== null ) {\n // キャッシュさている値を返す\n return this._position;\n }\n\n if ( this._boundaries.length == 0 ) {\n // 多角形が存在しない\n return null;\n }\n\n var min_lon = Number.MAX_VALUE;\n var max_lon = -Number.MAX_VALUE;\n var min_lat = Number.MAX_VALUE;\n var max_lat = -Number.MAX_VALUE;\n\n for ( let bo of this._boundaries ) {\n let count = bo.num_points;\n let points = bo.points;\n\n for ( let i = 0; i < count; ++i ) {\n var lon = points[3*i ];\n var lat = points[3*i + 1];\n\n if ( lon < min_lon ) min_lon = lon;\n if ( lon > max_lon ) max_lon = lon;\n if ( lat < min_lat ) min_lat = lat;\n if ( lat > max_lat ) max_lat = lat;\n }\n }\n\n this._position = new GeoPoint( (min_lon + max_lon) / 2,\n (min_lat + max_lat) / 2 );\n\n return this._position;\n }\n\n\n /**\n * @summary すべての境界の頂点数の合計を取得\n *\n * @private\n */\n _countNumPointsOnBoundaries()\n {\n let num_points = 0;\n\n for ( let bo of this._boundaries ) {\n num_points += bo.num_points;\n }\n\n return num_points;\n }\n\n\n /**\n * @summary 結合された境界点列を取得\n *\n * @return {Float64Array} 結合された境界点列\n */\n _getCombinedBoundaryPoints()\n {\n let points = new Float64Array( 3 * this._countNumPointsOnBoundaries() );\n let offset = 0;\n\n for ( let bo of this._boundaries ) {\n points.set( bo.points, offset );\n offset += 3 * bo.num_points;\n }\n\n return points;\n }\n\n\n /**\n * @summary 結合された 2D 境界点列を取得 (高度なし)\n *\n * @return {Float64Array} 結合された 2D 境界点列\n */\n _getCombinedBoundary2DPoints()\n {\n let dst_points = new Float64Array( 2 * this._countNumPointsOnBoundaries() );\n let di = 0;\n\n for ( let bo of this._boundaries ) {\n let src_size = 3 * bo.num_points;\n let src_points = bo.points;\n for ( let si = 0; si < src_size; si += 3 ) {\n dst_points[di++] = src_points[si ];\n dst_points[di++] = src_points[si + 1];\n }\n }\n\n return dst_points;\n }\n\n\n /**\n * @summary 三角形リストを生成\n *\n * @desc\n * this.entity._boundaries を三角形に変換してリストを返す。ただし変換に失敗したときは null を返す。
\n *\n * @return {Uint32Array} 三角形リストまたは null\n *\n * @private\n */\n _createTriangles()\n {\n let src_points = this._getCombinedBoundary2DPoints();\n let num_src_points = this._countNumPointsOnBoundaries();\n\n // 境界を登録\n let triangulator = new Triangulator( src_points, 0, 2, num_src_points );\n let index = 0;\n\n for ( let bo of this._boundaries ) {\n let num_indices = bo.num_points;\n let indices = new Uint32Array( num_indices );\n for ( let i = 0; i < num_indices; ++i ) {\n indices[i] = index++;\n }\n triangulator.addBoundary( indices );\n }\n\n try {\n // 変換を実行\n return triangulator.run();\n }\n catch ( e ) {\n // 変換に失敗\n console.error( e.message );\n return null;\n }\n }\n\n}\n\n\n/**\n * @summary PolygonEntity の PrimitiveProducer\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n\n /**\n * @param {mapray.PolygonEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._status = Status.INVALID;\n this._triangles = null; // 三角形リスト (Uint32Array)\n\n // プリミティブの要素\n this._transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._pivot = GeoMath.createVector3();\n this._bbox = [GeoMath.createVector3(),\n GeoMath.createVector3()];\n this._properties = {\n color: GeoMath.createVector3f(),\n opacity: 1.0,\n lighting: false\n };\n\n // プリミティブ\n var primitive = new Primitive( entity.glenv, null, entity._getMaterial( RenderTarget.SCENE ), this._transform );\n primitive.pivot = this._pivot;\n primitive.bbox = this._bbox;\n primitive.properties = this._properties;\n\n this._primitive = primitive;\n\n var pickPrimitive = new Primitive( entity.glenv, null, entity._getMaterial( RenderTarget.RID ), this._transform );\n pickPrimitive.pivot = this._pivot;\n pickPrimitive.bbox = this._bbox;\n pickPrimitive.properties = this._properties;\n\n this._pickPrimitive = pickPrimitive;\n }\n\n\n /**\n * @override\n */\n needsElevation()\n {\n const owner = this.entity;\n return owner.altitude_mode !== AltitudeMode.ABSOLUTE;\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n let owner = this.entity;\n\n if ( this._status === Status.INVALID ) {\n // 多角形なし、または三角形に変換できなかったとき\n return [];\n }\n\n // 正常な多角形のとき\n\n var region = new EntityRegion();\n\n for ( let bo of owner._boundaries ) {\n region.addPoints( bo.points, 0, 3, bo.num_points );\n }\n\n region.addPoint( owner._getPosition() );\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n if ( this._status === Status.NORMAL ) {\n this._status = Status.MESH_DIRTY;\n }\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n if ( this._status === Status.INVALID ) {\n // 多角形なし、または三角形に変換できなかったとき\n return [];\n }\n else if ( this._status === Status.TRIANGLE_DIRTY ) {\n this._triangles = this.entity._createTriangles();\n if ( this._triangles === null ) {\n // 多角形の三角形化に失敗\n this._primitive.mesh = null;\n this._pickPrimitive.mesh = null;\n this._status = Status.INVALID;\n return [];\n }\n this._updatePrimitiveMesh();\n }\n else if ( this._status === Status.MESH_DIRTY ) {\n this._updatePrimitiveMesh();\n }\n\n this._updatePrimitiveProperties();\n\n this._status = Status.NORMAL;\n return stage.getRenderTarget() === RenderTarget.SCENE ? [this._primitive] : [this._pickPrimitive];\n }\n\n\n /**\n * @summary 押し出しモードが変更されたことを通知\n */\n onChangeExtruded()\n {\n if ( this._status === Status.NORMAL ) {\n this._status = Status.MESH_DIRTY;\n }\n }\n\n\n /**\n * @summary プロパティが変更されたことを通知\n */\n onChangeProperty()\n {\n // することなし\n }\n\n\n /**\n * @summary 境界が変更されたことを通知\n */\n onChangeBoundary()\n {\n this._status = Status.TRIANGLE_DIRTY;\n this._triangles = null;\n this.needToCreateRegions();\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * 入力:\n * this.entity\n * this._triangles\n * 出力:\n * this._transform\n * this._pivot\n * this._bbox\n * this._primitive.mesh\n *\n * @private\n */\n _updatePrimitiveMesh()\n {\n var cb_data = new BoundaryConbiner( this.entity );\n\n // プリミティブの更新\n // primitive.transform\n // primitive.pivot\n // primitive.bbox\n this._updateTransformPivotBBox( cb_data );\n\n // メッシュ生成\n var mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_normal\", size: 3 }\n ],\n vertices: this._createVertices( cb_data ),\n indices: this._createIndices( cb_data )\n };\n var mesh = new Mesh( this.entity.scene.glenv, mesh_data );\n\n // メッシュ設定\n this._primitive.mesh = mesh;\n this._pickPrimitive.mesh = mesh;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * \n * 出力:\n * this._transform\n * this._pivot\n * this._bbox\n *
\n *\n * @param {BoundaryConbiner} cb_data 入力データ\n *\n * @private\n */\n _updateTransformPivotBBox( cb_data )\n {\n // 変換行列の更新\n let transform = this._transform;\n transform[12] = cb_data.origin[0];\n transform[13] = cb_data.origin[1];\n transform[14] = cb_data.origin[2];\n\n // 統計\n let xmin = Number.MAX_VALUE;\n let ymin = Number.MAX_VALUE;\n let zmin = Number.MAX_VALUE;\n\n let xmax = -Number.MAX_VALUE;\n let ymax = -Number.MAX_VALUE;\n let zmax = -Number.MAX_VALUE;\n\n // [cb_data.upper, cb_data.lower]\n let points_array = [cb_data.upper];\n if ( cb_data.lower ) {\n points_array.push( cb_data.lower );\n }\n\n for ( let j = 0; j < points_array.length; ++j ) {\n let points = points_array[j];\n for ( let i = 0; i < cb_data.num_points; ++i ) {\n let b = 3 * i;\n let x = points[b ];\n let y = points[b + 1];\n let z = points[b + 2];\n\n if ( x < xmin ) { xmin = x; }\n if ( y < ymin ) { ymin = y; }\n if ( z < zmin ) { zmin = z; }\n\n if ( x > xmax ) { xmax = x; }\n if ( y > ymax ) { ymax = y; }\n if ( z > zmax ) { zmax = z; }\n }\n }\n\n // 中心点\n let pivot = this._pivot;\n pivot[0] = (xmin + xmax) / 2;\n pivot[1] = (ymin + ymax) / 2;\n pivot[2] = (zmin + zmax) / 2;\n\n // 境界箱\n let bbox = this._bbox;\n let bmin = bbox[0];\n let bmax = bbox[1];\n bmin[0] = xmin;\n bmin[1] = ymin;\n bmin[2] = zmin;\n bmax[0] = xmax;\n bmax[1] = ymax;\n bmax[2] = zmax;\n }\n\n\n /**\n * @summary 頂点配列の生成\n *\n * @desc\n * 生成される形式は [Px, Py, Pz, Nx, Ny, Nz, ...] のような形で、それぞれの座標はローカル座標系になる。\n * 配列の頂点データは 2 つの領域で分かれ、上面ポリゴンの頂点配列(S1) → 側面ポリゴンの頂点配列(S2) の順序で格納される。\n * ただし cb_data.lower == null のとき、配列は S1 部分しか設定されない。\n *\n * S1 は cb_data.upper に対応する頂点データが同じ順序で格納される。\n *\n * S2 は cb_data.num_points 個の四角形に対する頂点データが順番に並んでいる。\n * 各四角形の頂点データは 左下、右下、左上、右上 の順序で格納されている。\n *\n * 入力: this.entity._boundaries\n *\n * @param {BoundaryConbiner} cb_data 入力データ\n *\n * @return {Float32Array} Mesh 用の頂点配列\n *\n * @private\n */\n _createVertices( cb_data )\n {\n const fpv = 6; // 1頂点データあたりの float 数\n\n const s1_num_floats = fpv * cb_data.num_points; // 上面のデータサイズ\n const s2_num_floats = cb_data.lower ? fpv * (4*cb_data.num_points) : 0; // 側面のデータサイズ\n const s3_num_floats = cb_data.lower ? s1_num_floats : 0; // 底面のデータサイズ\n\n let vertices = new Float32Array( s1_num_floats + s2_num_floats + s3_num_floats );\n\n // 上面の法線を取得\n let unormal = GeoMath.normalize3( cb_data.origin, GeoMath.createVector3() );\n\n // 上面の頂点データ\n for ( let i = 0; i < cb_data.num_points; ++i ) {\n let b = 3 * i;\n let px = cb_data.upper[b];\n let py = cb_data.upper[b + 1];\n let pz = cb_data.upper[b + 2];\n\n let vi = fpv * i;\n vertices[vi ] = px; // a_position.x\n vertices[vi + 1] = py; // a_position.y\n vertices[vi + 2] = pz; // a_position.z\n setVec3ToArray( unormal, vertices, vi + 3 ); // a_normal\n }\n\n // 側面の頂点データ\n if ( cb_data.lower ) {\n let p00 = GeoMath.createVector3(); // 左下位置\n let p10 = GeoMath.createVector3(); // 右下位置\n let p01 = GeoMath.createVector3(); // 左上位置\n let p11 = GeoMath.createVector3(); // 右上位置\n let snormal = GeoMath.createVector3(); // 側面の法線\n\n let beg_i = 0; // bo の最初の頂点のインデックス\n\n for ( let bo of this.entity._boundaries ) {\n let end_i = beg_i + bo.num_points; // bo の最後の頂点のインデックス + 1\n\n for ( let i = beg_i; i < end_i; ++i ) {\n let i0 = i;\n let i1 = (i + 1 < end_i) ? i + 1 : beg_i;\n\n // 四隅の位置を取得\n let b0 = 3 * i0;\n let b1 = 3 * i1;\n setArrayToVec3( cb_data.lower, b0, p00 );\n setArrayToVec3( cb_data.lower, b1, p10 );\n setArrayToVec3( cb_data.upper, b0, p01 );\n setArrayToVec3( cb_data.upper, b1, p11 );\n\n // 側面の法線を取得\n setTriangleNormal( p00, p10, p01, snormal );\n\n // 四隅の頂点データを設定\n let vi = s1_num_floats + 4*fpv*i;\n setVec3ToArray( p00, vertices, vi ); // a_position\n setVec3ToArray( snormal, vertices, vi + 3 ); // a_normal\n vi += fpv;\n setVec3ToArray( p10, vertices, vi ); // a_position\n setVec3ToArray( snormal, vertices, vi + 3 ); // a_normal\n vi += fpv;\n setVec3ToArray( p01, vertices, vi ); // a_position\n setVec3ToArray( snormal, vertices, vi + 3 ); // a_normal\n vi += fpv;\n setVec3ToArray( p11, vertices, vi ); // a_position\n setVec3ToArray( snormal, vertices, vi + 3 ); // a_normal\n }\n\n beg_i = end_i;\n }\n }\n\n if ( cb_data.lower ) {\n const bnormal = GeoMath.scale3( -1.0, unormal, GeoMath.createVector3() );\n\n // 底面の頂点データ\n for ( let i = 0; i < cb_data.num_points; ++i ) {\n let b = 3 * i;\n let px = cb_data.lower[b];\n let py = cb_data.lower[b + 1];\n let pz = cb_data.lower[b + 2];\n\n let vi = s1_num_floats + s2_num_floats + fpv * i;\n vertices[vi ] = px; // a_position.x\n vertices[vi + 1] = py; // a_position.y\n vertices[vi + 2] = pz; // a_position.z\n setVec3ToArray( bnormal, vertices, vi + 3 ); // a_normal\n }\n }\n\n return vertices;\n }\n\n\n /**\n * @summary インデックス配列の生成\n *\n * 入力: this._triangles\n *\n * @param {BoundaryConbiner} cb_data 入力データ\n *\n * @return {Uint32Array} インデックス配列\n *\n * @private\n */\n _createIndices( cb_data )\n {\n // 頂点の並びは _createVertices() を参照\n\n let num_upper_triangles = this._triangles.length / 3;\n let num_side_triangles = cb_data.lower ? 2 * cb_data.num_points : 0;\n let num_bottom_triangles = cb_data.lower ? num_upper_triangles : 0;\n\n let indices = new Uint32Array( 3 * (num_upper_triangles + num_side_triangles + num_bottom_triangles) );\n\n // 前半に上面のポリゴンを設定\n indices.set( this._triangles );\n\n // 側面のポリゴンを設定\n if ( cb_data.lower ) {\n let num_quads = cb_data.num_points;\n let ioffset = 3 * num_upper_triangles; // indices 内の現在の四角形のオフセット\n let voffset = cb_data.num_points; // 頂点配列内の現在の四角形のオフセット\n\n for ( let i = 0; i < num_quads; ++i, ioffset += 6, voffset += 4 ) {\n // 左下三角形\n indices[ioffset ] = voffset;\n indices[ioffset + 1] = voffset + 1;\n indices[ioffset + 2] = voffset + 2;\n // 右上三角形\n indices[ioffset + 3] = voffset + 2;\n indices[ioffset + 4] = voffset + 1;\n indices[ioffset + 5] = voffset + 3;\n }\n }\n\n // 底面のポリゴンを設定\n if ( cb_data.lower ) {\n const len = this._triangles.length / 3;\n const voffset = cb_data.num_points + 4 * cb_data.num_points;\n for ( let i = 0; i < len; ++i ) {\n indices[ (num_upper_triangles + num_side_triangles + i) * 3 + 0 ] = this._triangles[ i * 3 + 0 ] + voffset;\n indices[ (num_upper_triangles + num_side_triangles + i) * 3 + 1 ] = this._triangles[ i * 3 + 2 ] + voffset;\n indices[ (num_upper_triangles + num_side_triangles + i) * 3 + 2 ] = this._triangles[ i * 3 + 1 ] + voffset;\n }\n }\n\n return indices;\n }\n\n\n /**\n * @summary プリミティブのプロパティを更新\n *\n * 入力: this.entity\n * 出力: this._properties\n *\n * @private\n */\n _updatePrimitiveProperties()\n {\n let owner = this.entity;\n let props = this._properties;\n\n GeoMath.copyVector3( owner._color, props.color );\n props.opacity = owner._opacity;\n props.lighting = this.extruded_height !== 0.0;\n }\n\n}\n\n\n/**\n * @summary PolygonEntity の FlakePrimitiveProducer\n *\n * @private\n */\nclass FlakePrimitiveProducer extends Entity.FlakePrimitiveProducer {\n\n /**\n * @param {mapray.PolygonEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._material_map = Object.keys(RenderTarget).reduce((map, key) => {\n const render_target = RenderTarget[key];\n map.set( render_target, entity._getMaterial( render_target ) );\n return map;\n }, new Map());\n this._properties = null;\n this._area_manager = new PolygonAreaManager( entity );\n }\n\n\n /**\n * @override\n */\n getAreaStatus( area )\n {\n return this._area_manager.getAreaStatus( area );\n }\n\n\n /**\n * @override\n */\n createMesh( area, dpows, dem )\n {\n // ConvexPolygon の配列、または Entity.AreaStatus.FULL\n let polygons = this._area_manager.getAreaContent( area );\n\n let msize = Math.PI * Math.pow( 2, 1 - area.z );\n let x_min = area.x * msize - Math.PI;\n let y_min = Math.PI - (area.y + 1) * msize;\n\n let div_x = 1 << dpows[0];\n let div_y = 1 << dpows[1];\n\n // サブメッシュの配列を生成\n let submeshes = this._createSubmeshes( x_min, y_min,\n x_min + msize, y_min + msize,\n div_x, div_y,\n polygons );\n\n // メッシュ生成\n let mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_normal\", size: 3 }\n ],\n vertices: this._createVertices( submeshes, area, dem ),\n indices: this._createIndices( submeshes )\n };\n\n return new Mesh( this.entity.scene.glenv, mesh_data );\n }\n\n\n /**\n * @override\n */\n getMaterialAndProperties( stage )\n {\n if ( this._properties === null ) {\n let entity = this.entity;\n\n this._properties = {\n color: GeoMath.createVector3f( entity._color ),\n opacity: entity._opacity,\n lighting: false\n };\n }\n\n return {\n material: this._material_map.get( stage.getRenderTarget() ),\n properties: this._properties\n };\n }\n\n\n /**\n * @summary 押し出しモードが変更されたことを通知\n */\n onChangeExtruded()\n {\n // flake_mode なので押し出しモードは関係ない\n }\n\n\n /**\n * @summary プロパティが変更されたことを通知\n */\n onChangeProperty()\n {\n this._properties = null;\n }\n\n\n /**\n * @summary 境界が変更されたことを通知\n */\n onChangeBoundary()\n {\n this._area_manager.notifyForUpdateContent();\n this.notifyForUpdate();\n }\n\n\n /**\n * @summary 頂点配列を生成\n *\n * @param {iterable.} submeshes\n * @param {mapray.Area} area\n * @param {mapray.DemBinary} dem\n *\n * @return {Float32Array}\n *\n * @private\n */\n _createVertices( submeshes, area, dem )\n {\n let origin = AreaUtil.getCenter( area, GeoMath.createVector3() );\n let sampler = dem.newSampler( area );\n\n // 頂点配列を生成\n let num_vertices = 0;\n for ( let smesh of submeshes ) {\n num_vertices += smesh.getNumVertices();\n }\n let vertices = new Float32Array( 6 * num_vertices );\n\n // 頂点配列に座標を書き込む\n let offset = 0;\n for ( let smesh of submeshes ) {\n offset = smesh.addVertices( origin, sampler, vertices, offset );\n }\n\n return vertices;\n }\n\n\n /**\n * @summary インデックス配列を生成\n *\n * @param {iterable.} submeshes\n *\n * @return {Uint32Array}\n *\n * @private\n */\n _createIndices( submeshes )\n {\n // インデックス配列を生成\n let num_triangles = 0;\n for ( let smesh of submeshes ) {\n num_triangles += smesh.getNumTriangles();\n }\n let indices = new Uint32Array( 3 * num_triangles );\n\n // インデックス配列にインデックスを書き込む\n let voffset = 0;\n let ioffset = 0;\n for ( let smesh of submeshes ) {\n ioffset = smesh.addIndices( voffset, indices, ioffset );\n voffset += smesh.getNumVertices();\n }\n\n return indices;\n }\n\n\n /**\n * @summary サブメッシュの配列を生成\n *\n * polygons は領域と交差する ConvexPolygon の配列である。ただし領域が多角形で覆われているときは\n * Entity.AreaStatus.FULL になる場合がある。
\n *\n * @param {number} x_min 領域の最小 x 座標\n * @param {number} y_min 領域の最小 y 座標\n * @param {number} x_max 領域の最大 x 座標\n * @param {number} y_max 領域の最大 y 座標\n * @param {number} div_x 領域の x 方向の分割数\n * @param {number} div_y 領域の y 方向の分割数\n * @param {iterable.|mapray.Entity.AreaStatus} polygons\n *\n * @return {iterable.} サブメッシュの配列\n *\n * @private\n */\n _createSubmeshes( x_min, y_min, x_max, y_max, div_x, div_y, polygons )\n {\n if ( polygons === Entity.AreaStatus.FULL ) {\n // 領域内は多角形に覆われている\n return [ new RectSubmesh( x_min, y_min, x_max, y_max, div_x, div_y ) ];\n }\n else if ( polygons.length == 0 ) {\n // 領域内に多角形は無い\n return [];\n }\n else if ( div_x == 1 && div_y == 1 ) {\n // これ以上分割できないので切り取り多角形を返す\n let t1 = [x_min, y_min, x_max, y_min, x_min, y_max]; // 左下三角形\n let t2 = [x_min, y_max, x_max, y_min, x_max, y_max]; // 右上三角形\n\n let m1 = this._create_clipped_polygons_submeshes( t1, polygons );\n let m2 = this._create_clipped_polygons_submeshes( t2, polygons );\n return m1.concat( m2 );\n }\n else {\n if ( div_x >= div_y ) {\n // 左右分割\n let msize = (x_max - x_min) / 2;\n let div_w = div_x / 2;\n\n let m1 = this._create_submeshes_sp( x_min, y_min, x_min + msize, y_max, div_w, div_y, polygons );\n let m2 = this._create_submeshes_sp( x_min + msize, y_min, x_max, y_max, div_w, div_y, polygons );\n return m1.concat( m2 );\n }\n else {\n // 上下分割\n let msize = (y_max - y_min) / 2;\n let div_w = div_y / 2;\n\n let m1 = this._create_submeshes_sp( x_min, y_min, x_max, y_min + msize, div_x, div_w, polygons );\n let m2 = this._create_submeshes_sp( x_min, y_min + msize, x_max, y_max, div_x, div_w, polygons );\n return m1.concat( m2 );\n }\n }\n }\n\n\n /**\n * @summary サブメッシュの配列を生成\n *\n * @desc\n * _createSubmeshes() との違いは polygons に Entity.AreaStatus.FULL を指定できない。\n * また、polygons には領域の外側の多角形が含まれている可能性がある。
\n *\n * @param {number} x_min 領域の最小 x 座標\n * @param {number} y_min 領域の最小 y 座標\n * @param {number} x_max 領域の最大 x 座標\n * @param {number} y_max 領域の最大 y 座標\n * @param {number} div_x 領域の x 方向の分割数\n * @param {number} div_y 領域の y 方向の分割数\n * @param {iterable.} polygons\n *\n * @return {Submesh[]} サブメッシュの配列\n *\n * @private\n */\n _create_submeshes_sp( x_min, y_min, x_max, y_max, div_x, div_y, polygons )\n {\n // 領域を凸多角形に変換\n let area_rect = ConvexPolygon.createByRectangle( x_min, y_min, x_max, y_max );\n\n let selected_polygons = [];\n\n for ( let polygon of polygons ) {\n if ( polygon.includes( area_rect ) ) {\n // polygon は area_rect を覆う\n // つまり area_rect は全体の多角形に覆われている\n selected_polygons = Entity.AreaStatus.FULL;\n break;\n }\n\n try {\n if ( area_rect.hasIntersection( polygon ) ) {\n // 領域と交差しているので polygon 追加\n selected_polygons.push( polygon );\n }\n }\n catch ( e ) {\n // polygon の交差判定に失敗したときは polygon は無いことにする\n }\n }\n\n return this._createSubmeshes( x_min, y_min, x_max, y_max, div_x, div_y, selected_polygons );\n }\n\n\n /**\n * @summary 凸多角形のサブメッシュの配列を生成\n * \n * @desc\n * area_triangle の三角形で src_polygons の凸多角形を切り取り、それらの切り取られた凸多角形に対応する\n * PolygonsSubmesh インスタンスの配列を生成する。
\n *\n * arit = Area Right Isosceles Triangle (領域直角二等辺三角形)\n *\n * @param {number[]} arit_coords 領域の三角形座標配列 (左下または右上の三角形)\n * @param {iterable.} src_polygons 切り取り対象の凸多角形の配列\n *\n * @return {PolygonsSubmesh[]} PolygonsSubmesh の配列\n *\n * @private\n */\n _create_clipped_polygons_submeshes( arit_coords, src_polygons )\n {\n let area_polygon = new ConvexPolygon( arit_coords );\n\n let clipped_polygons = [];\n\n for ( let polygon of src_polygons ) {\n try {\n let clipped = area_polygon.getIntersection( polygon );\n if ( clipped !== null ) {\n clipped_polygons.push( clipped );\n }\n }\n catch ( e ) {\n // polygon の切り抜きに失敗したときは polygon の切り抜きは返さない\n }\n }\n\n if ( clipped_polygons.length > 0 ) {\n return [ new PolygonsSubmesh( arit_coords, clipped_polygons ) ];\n }\n else {\n return [];\n }\n }\n\n}\n\n\n/**\n * @summary 多角形の境界\n *\n * @classdesc\n * 多角形の1つの境界を表現する。
\n * 外側境界のときは反時計回り、内側境界のときは時計回りで格納される。
\n *\n * @private\n */\nclass Boundary {\n\n /**\n * @desc\n * points は addOuterBoundary(), addInnerBoundary() と同じ形式である。
\n *\n * @param {number[]} points 境界の頂点データ\n * @param {boolean} is_inner 内側境界か?\n */\n constructor( points, is_inner )\n {\n let num_points = Math.floor( points.length / 3 );\n\n this._points = new Float64Array( 3 * num_points );\n this._num_points = num_points;\n\n let is_ccw = Boundary.isCCW( points, num_points );\n\n let si;\n let si_step;\n\n if ( (!is_inner && is_ccw) || (is_inner && !is_ccw) ) {\n // 順方向\n si = 0;\n si_step = 3;\n }\n else {\n // 逆方向\n si = 3 * (num_points - 1);\n si_step = -3;\n }\n\n // 内部の配列にコピー\n for ( let i = 0; i < num_points; ++i ) {\n this._points[3*i ] = points[si ];\n this._points[3*i + 1] = points[si + 1];\n this._points[3*i + 2] = points[si + 2];\n si += si_step;\n }\n }\n\n\n /**\n * @summary 頂点座標の配列\n * @type {number[]}\n * @readonly\n */\n get points() { return this._points; }\n\n\n /**\n * @summary 頂点数\n * @type {number}\n * @readonly\n */\n get num_points() { return this._num_points; }\n\n\n /**\n * @summary 境界は反時計回りか?\n *\n * @param {number[]} points 境界の頂点データ\n *\n * @return {boolean} 反時計回りのとき true, それ以外のとき false\n */\n static\n isCCW( points, num_points )\n {\n // 頂上の点、同じ高さなら左側優先\n let top_i;\n let top_x = -Number.MAX_VALUE;\n let top_y = -Number.MAX_VALUE;\n\n for ( let i = 0; i < num_points; ++i ) {\n let x = points[3*i ];\n let y = points[3*i + 1];\n if ( (y > top_y) || (y == top_y && x < top_x)) {\n top_i = i;\n top_x = x;\n top_y = y;\n }\n }\n\n // top の前方の点\n let next_i = (top_i == num_points - 1) ? 0 : top_i + 1;\n let next_x = points[3*next_i ];\n let next_y = points[3*next_i + 1];\n\n // top の後方の点\n let prev_i = (top_i == 0) ? num_points - 1 : top_i - 1;\n let prev_x = points[3*prev_i ];\n let prev_y = points[3*prev_i + 1];\n\n // prev と next は top より下または同じ高さだが、少なくともどちらか一方は top より下になる\n // またエッジは交差しないことが前提なので、2 つのエッジの内角は 0 度より大きく 180 度未満になる\n // したがって a, b の行列式が正のとき反時計回り、それ以外のとき時計回り\n let ax = next_x - top_x;\n let ay = next_y - top_y;\n let bx = prev_x - top_x;\n let by = prev_y - top_y;\n\n return ax*by - bx*ay > 0;\n }\n\n}\n\n\n/**\n * @summary 境界線データを結合\n *\n * @classdesc\n * pe._bounaries に対応する上頂点と底頂点の LOCS 平坦化配列を取得する。
\n * pe._extruded_height === 0 のときは lower に null を設定する。
\n *\n * \n * プロパティ:\n * origin: Vector3 // LOCS の原点位置 (GOCS)\n * num_points: number // upper の頂点数\n * upper: Float64Array // 上頂点 (LOCS, 順序は pe._bounaries.points の連結)\n * lower: Float64Array // 底頂点 (LOCS, 順序は upper と同じ, nullable)\n *
\n *\n * @private\n */\nclass BoundaryConbiner {\n\n /**\n * @desc\n * \n * 入力:\n * pe.viewer\n * pe.altitude_mode\n * pe._extruded_height\n * pe._bounaries\n *
\n *\n * @param {mapray.PolygonEntity} pe 呼び出し側のポリゴンエンティティ\n */\n constructor( pe )\n {\n /*\n pe._extruded_height !== 0 == 0 \n \n --- _.-*---*._ _.-*---*._ \n upper_points *-_ _-* *-_ _-*\n --- | *----* | *----* \n | | | | \n --- | | | | \n lower_points *-_| |_-* (null) \n --- *----* \n */\n let viewer = pe.scene.viewer;\n let altitude_mode = pe.altitude_mode;\n\n let src_points = pe._getCombinedBoundaryPoints();\n let num_points = pe._countNumPointsOnBoundaries();\n\n let base_points = Float64Array.from( src_points );\n\n if ( altitude_mode === AltitudeMode.RELATIVE ) {\n let elevation = viewer.getExistingElevation( pe._getPosition() );\n for ( let i = 0; i < num_points; ++i ) {\n let ai = 3 * i + 2;\n base_points[ai] += elevation;\n }\n }\n\n let upper_points = null;\n let lower_points = null;\n if ( pe._extruded_height !== 0 ) {\n if ( altitude_mode === AltitudeMode.CLAMP ) {\n upper_points = base_points;\n lower_points = Float64Array.from( src_points );\n for ( let i = 0; i < num_points; ++i ) {\n let ai = 3 * i + 2;\n lower_points[ai] = 0;\n }\n }\n else { // altitude_mode !== AltitudeMode.ABSOLUTE || altitude_mode !== AltitudeMode.RELATIVE\n lower_points = base_points;\n upper_points = Float64Array.from( src_points );\n for ( let i = 0; i < num_points; ++i ) {\n let ai = 3 * i + 2;\n upper_points[ai] = lower_points[ai] + pe._extruded_height;\n }\n }\n }\n else {\n upper_points = base_points;\n }\n\n let origin = pe._getPosition().getAsGocs( GeoMath.createVector3() );\n\n // LOCS 平坦化配列\n let upper_ocs_points = GeoPoint.toGocsArray( upper_points, num_points,\n new Float64Array( 3 * num_points ) );\n for ( let i = 0; i < num_points; ++i ) {\n let d = 3 * i;\n upper_ocs_points[d ] -= origin[0];\n upper_ocs_points[d + 1] -= origin[1];\n upper_ocs_points[d + 2] -= origin[2];\n }\n\n let lower_ocs_points = null;\n if ( lower_points ) {\n // ASSERT: lower_points != null\n lower_ocs_points = GeoPoint.toGocsArray( lower_points, num_points,\n new Float64Array( 3 * num_points ) );\n for ( let i = 0; i < num_points; ++i ) {\n let d = 3 * i;\n lower_ocs_points[d ] -= origin[0];\n lower_ocs_points[d + 1] -= origin[1];\n lower_ocs_points[d + 2] -= origin[2];\n }\n }\n\n // プロパティを設定\n this.origin = origin;\n this.num_points = num_points;\n this.upper = upper_ocs_points;\n this.lower = lower_ocs_points;\n }\n\n}\n\n\n/**\n * @summary 多角形の領域管理\n *\n * @private\n */\nclass PolygonAreaManager extends QAreaManager {\n\n /**\n * @param {mapray.PolygonEntity} entity 管理対象のエンティティ\n */\n constructor( entity )\n {\n super();\n\n this._entity = entity;\n }\n\n\n /**\n * @override\n */\n getInitialContent()\n {\n let src_indices = this._entity._createTriangles() || [];\n let num_src_indices = src_indices.length;\n\n let src_coords = this._entity._getCombinedBoundary2DPoints();\n let content = []; // ConvexPolygon の配列\n\n for ( let si = 0; si < num_src_indices; si += 3 ) {\n let i0 = src_indices[si ];\n let i1 = src_indices[si + 1];\n let i2 = src_indices[si + 2];\n this._add_polygon_to_array( src_coords, i0, i1, i2, content );\n }\n\n return content;\n }\n\n\n /**\n * @override\n */\n createAreaContent( min_x, min_y, msize, parent_content )\n {\n // 単位球メルカトルでの領域に変換\n const x_area_min = Math.PI * min_x;\n const y_area_min = Math.PI * min_y;\n const x_area_max = Math.PI * (min_x + msize);\n const y_area_max = Math.PI * (min_y + msize);\n\n // 領域を凸多角形に変換\n const area_rect = ConvexPolygon.createByRectangle( x_area_min, y_area_min,\n x_area_max, y_area_max );\n\n let content = []; // ConvexPolygon の配列\n\n for ( let polygon of parent_content ) {\n\n if ( polygon.includes( area_rect ) ) {\n // polygon は area_rect を覆う\n // つまり area_rect は全体の多角形に覆われている\n return Entity.AreaStatus.FULL;\n }\n\n try {\n if ( area_rect.hasIntersection( polygon ) ) {\n // 領域と交差しているので polygon 追加\n content.push( polygon );\n }\n }\n catch ( e ) {\n // polygon の交差判定に失敗したときは polygon は無いことにする\n }\n }\n\n return (content.length > 0) ? content : Entity.AreaStatus.EMPTY;\n }\n\n\n /**\n * @summary 三角形を凸多角形として追加\n *\n * @param {number[]} src_coords 入力頂点の座標配列 (経緯度)\n * @param {number} si0 三角形の頂点 0\n * @param {number} si1 三角形の頂点 1\n * @param {number} si2 三角形の頂点 2\n * @param {mapray.ConvexPolygon[]} dst_polygons 出力先の ConvexPolygon 配列\n *\n * @private\n */\n _add_polygon_to_array( src_coords, si0, si1, si2, dst_polygons )\n {\n const Degree = GeoMath.DEGREE;\n const RAngle = Math.PI / 2; // 直角\n const TwoPI = 2 * Math.PI; // 2π\n\n // 三角形の頂点座標配列 (単位球メルカトル座標系) を作成\n let vertices = [];\n let mx_min_1 = Number.MAX_VALUE; // オフセット処理前の最小 mx 座標\n\n for ( let si of [si0, si1, si2] ) {\n let lon = src_coords[2*si ] * Degree;\n let lat = src_coords[2*si + 1] * Degree;\n\n if ( Math.abs( lat ) >= RAngle ) {\n // 緯度の絶対値が RAngle 以上の頂点が存在する三角形は除外\n // ※ まだ検討していないので、とりあえずの処置\n return;\n }\n\n let mx = lon;\n let my = GeoMath.invGudermannian( lat );\n\n vertices.push( mx );\n vertices.push( my );\n\n mx_min_1 = Math.min( mx, mx_min_1 );\n }\n\n // mx_min_2: mx 座標が mx_min_1 だった頂点のオフセット後の mx 座標\n let mx_min_2 = mx_min_1 - TwoPI * (Math.floor( (mx_min_1 - Math.PI) / TwoPI ) + 1);\n if ( mx_min_2 < -Math.PI || mx_min_2 >= Math.PI ) {\n // 数値計算誤差により稀に区間からはみ出る可能性があるので\n mx_min_2 = -Math.PI;\n }\n // Assert: -Math.PI <= mx_min_2 < Math.PI\n\n // mx 座標にオフセットを適用\n let mx_max_2 = -Number.MAX_VALUE; // オフセット後の最大 mx 座標\n\n for ( let i = 0; i < 3; ++i ) {\n let ix = 2 * i;\n let mx_1 = vertices[ix]; // オフセット前の mx 座標\n\n // mx_2: オフセット後の mx 座標\n let dx_1 = mx_1 - mx_min_1; // Assert: dx_1 >= 0\n let mx_2 = mx_min_2 + dx_1; // Assert: mx_2 >= mx_min_2\n // Assert: (mx_1 == mx_min_1) ⇒ (mx_2 == mx_min_2)\n\n vertices[ix] = mx_2;\n\n mx_max_2 = Math.max( mx_2, mx_max_2 );\n }\n\n // オフセットを適用した三角形を加える\n dst_polygons.push( new ConvexPolygon( vertices ) );\n\n // 三角形が 180 度子午線をまたぐとき\n // 360 度左にずらした三角形をもう1つ加える\n if ( mx_max_2 > Math.PI ) {\n for ( let i = 0; i < 3; ++i ) {\n let ix = 2 * i;\n let mx_2 = vertices[ix]; // オフセット後の mx 座標\n let mx_3 = mx_2 - TwoPI; // 360 度左にずらした mx 座標\n vertices[ix] = mx_3;\n }\n\n dst_polygons.push( new ConvexPolygon( vertices ) );\n }\n }\n\n}\n\n\n/**\n * @summary サブメッシュ\n *\n * @private\n */\nclass Submesh {\n\n /**\n */\n constructor()\n {\n }\n\n\n /**\n * @summary 頂点数を取得\n *\n * @return {number} 頂点数\n *\n * @abstract\n */\n getNumVertices()\n {\n throw \"\";\n }\n\n\n /**\n * @summary 三角形数を取得\n *\n * @return {number} 三角形数\n *\n * @abstract\n */\n getNumTriangles()\n {\n throw \"\";\n }\n\n\n /**\n * @summary 頂点配列に頂点データを書き込む\n *\n * @param {mapray.Vector3} origin 座標系の原点 (GOCS)\n * @param {mapray.Sampler} sampler DEM サンプラー\n * @param {number[]} vertices 書き込み先の配列\n * @param {number} offset 書き込み開始インデックス\n *\n * @return {number} offset + 書き込んだ要素数\n *\n * @abstract\n */\n addVertices( origin, sampler, vertices, offset )\n {\n throw \"\";\n }\n\n\n /**\n * @summary インデックス配列にインデックスを書き込む\n *\n * @param {number} voffset this 用頂点の先頭の頂点インデックス\n * @param {number[]} indices 書き込み先の配列\n * @param {number} ioffset 書き込み開始インデックス\n *\n * @return {number} ioffset + 書き込んだ要素数\n *\n * @abstract\n */\n addIndices( voffset, indices, ioffset )\n {\n throw \"\";\n }\n\n}\n\n\n/**\n * @summary 矩形サブメッシュ\n *\n * @private\n */\nclass RectSubmesh extends Submesh {\n\n /**\n * @param {number} x_min\n * @param {number} y_min\n * @param {number} x_max\n * @param {number} y_max\n * @param {number} div_x\n * @param {number} div_y\n */\n constructor( x_min, y_min, x_max, y_max, div_x, div_y )\n {\n super();\n\n this._x_min = x_min;\n this._y_min = y_min;\n this._x_max = x_max;\n this._y_max = y_max;\n this._div_x = div_x;\n this._div_y = div_y;\n }\n\n\n /**\n * @override\n */\n getNumVertices()\n {\n return (this._div_x + 1) * (this._div_y + 1);\n }\n\n\n /**\n * @override\n */\n getNumTriangles()\n {\n return 2 * this._div_x * this._div_y;\n }\n\n\n /**\n * @override\n */\n addVertices( origin, sampler, vertices, offset )\n {\n // 刻み幅\n let mx_step = (this._x_max - this._x_min) / this._div_x;\n let my_step = (this._y_max - this._y_min) / this._div_y;\n\n let end_iu = this._div_x + 1;\n let end_iv = this._div_y + 1;\n\n let index = offset;\n\n for ( let iv = 0, my = this._y_min; iv < end_iv; ++iv, my += my_step ) {\n let ey = Math.exp( my );\n let ey2 = ey * ey;\n let sinφ = (ey2 - 1) / (ey2 + 1);\n let cosφ = 2 * ey / (ey2 + 1);\n for ( let iu = 0, mx = this._x_min; iu < end_iu; ++iu, mx += mx_step ) {\n let sinλ = Math.sin( mx );\n let cosλ = Math.cos( mx );\n\n let height = sampler.sample( mx, my );\n let radius = GeoMath.EARTH_RADIUS + height;\n\n // 法線 (GOCS)\n let nx = cosφ * cosλ;\n let ny = cosφ * sinλ;\n let nz = sinφ;\n\n // 位置 (GOCS)\n let gx = radius * nx;\n let gy = radius * ny;\n let gz = radius * nz;\n\n vertices[index++] = gx - origin[0]; // x\n vertices[index++] = gy - origin[1]; // y\n vertices[index++] = gz - origin[2]; // z\n vertices[index++] = nx; // nx\n vertices[index++] = ny; // ny\n vertices[index++] = nz; // nz\n }\n }\n\n return index;\n }\n\n\n /**\n * @override\n */\n addIndices( voffset, indices, ioffset )\n {\n let div_x = this._div_x;\n let div_y = this._div_y;\n\n let index = ioffset;\n\n for ( let y = 0; y < div_y; ++y ) {\n for ( let x = 0; x < div_x; ++x ) {\n var i00 = voffset + (div_x + 1) * y + x; // 左下頂点\n var i10 = i00 + 1; // 右下頂点\n var i01 = i00 + div_x + 1; // 左上頂点\n var i11 = i01 + 1; // 右上頂点\n\n // 左下三角形\n indices[index++] = i00;\n indices[index++] = i10;\n indices[index++] = i01;\n\n // 右上三角形\n indices[index++] = i01;\n indices[index++] = i10;\n indices[index++] = i11;\n }\n }\n\n return index;\n }\n\n}\n\n\n/**\n * @summary 凸多角形集合サブメッシュ\n *\n * @private\n */\nclass PolygonsSubmesh extends Submesh {\n\n /**\n * this の生存中はパラメータのオブジェクトを変更しないこと。\n *\n * @param {number[]} arit_coords 領域の三角形座標配列 (左下または右上の三角形)\n * @param {iterable.} polygons arit_coords の上にある凸多角形集合\n */\n constructor( arit_coords, polygons )\n {\n super();\n\n this._arit_coords = arit_coords;\n this._polygons = polygons;\n\n this._num_vertices = 0;\n this._num_triangles = 0;\n\n for ( let polygon of polygons ) {\n this._num_vertices += polygon.num_vertices;\n this._num_triangles += polygon.num_vertices - 2;\n }\n }\n\n\n /**\n * @override\n */\n getNumVertices()\n {\n return this._num_vertices;\n }\n\n\n /**\n * @override\n */\n getNumTriangles()\n {\n return this._num_triangles;\n }\n\n\n /**\n * @override\n */\n addVertices( origin, sampler, vertices, offset )\n {\n let plane = this._get_elevation_plane( sampler );\n\n let index = offset;\n\n for ( let polygon of this._polygons ) {\n index = this._add_polygon_vertices( polygon, plane, origin, vertices, index );\n }\n\n return index;\n }\n\n\n /**\n * @override\n */\n addIndices( voffset, indices, ioffset )\n {\n let iofs_next = ioffset;\n let vofs_next = voffset;\n\n for ( let polygon of this._polygons ) {\n iofs_next = this._add_polygon_indices( polygon, vofs_next, indices, iofs_next );\n vofs_next += polygon.num_vertices;\n }\n\n return iofs_next;\n }\n\n\n /**\n * @summary 凸多角形の頂点を追加\n *\n * @param {mapray.ConvexPolygon} polygon 凸多角形\n * @param {number[]} plane 平面係数\n * @param {mapray.Vector3} origin 座標系の原点 (GOCS)\n * @param {number[]} vertices 書き込み先の配列\n * @param {number} offset 書き込み開始インデックス\n *\n * @return {number} offset + 書き込んだ要素数\n *\n * @private\n */\n _add_polygon_vertices( polygon, plane, origin, vertices, offset )\n {\n let index = offset;\n\n let num_vertices = polygon.num_vertices;\n let src_vertices = polygon.vertices;\n\n for ( let vi = 0; vi < num_vertices; ++vi ) {\n let mx = src_vertices[2*vi ];\n let my = src_vertices[2*vi + 1];\n\n let ey = Math.exp( my );\n let ey2 = ey * ey;\n\n let sinλ = Math.sin( mx );\n let cosλ = Math.cos( mx );\n let sinφ = (ey2 - 1) / (ey2 + 1);\n let cosφ = 2 * ey / (ey2 + 1);\n\n // mx*plane[0] + my*plane[1] + height*plane[2] + plane[3] == 0\n let height = -(mx*plane[0] + my*plane[1] + plane[3]) / plane[2];\n let radius = GeoMath.EARTH_RADIUS + height;\n\n // 法線 (GOCS)\n let nx = cosφ * cosλ;\n let ny = cosφ * sinλ;\n let nz = sinφ;\n\n // 位置 (GOCS)\n let gx = radius * nx;\n let gy = radius * ny;\n let gz = radius * nz;\n\n vertices[index++] = gx - origin[0]; // x\n vertices[index++] = gy - origin[1]; // y\n vertices[index++] = gz - origin[2]; // z\n vertices[index++] = nx; // nx\n vertices[index++] = ny; // ny\n vertices[index++] = nz; // nz\n }\n\n return index;\n }\n\n\n /**\n * @summary 凸多角形のインデックスを追加\n *\n * @param {mapray.ConvexPolygon} polygon 凸多角形\n * @param {number} voffset this 用頂点の先頭の頂点インデックス\n * @param {number[]} indices 書き込み先の配列\n * @param {number} ioffset 書き込み開始インデックス\n *\n * @return {number} ioffset + 書き込んだ要素数\n *\n * @private\n */\n _add_polygon_indices( polygon, voffset, indices, ioffset )\n {\n let index = ioffset;\n\n let num_triangles = polygon.num_vertices - 2;\n\n for ( let i = 1; i <= num_triangles; ++i ) {\n indices[index++] = voffset;\n indices[index++] = voffset + i;\n indices[index++] = voffset + i + 1;\n }\n\n return index;\n }\n\n\n /**\n * @summary 平面ベースで標高を計算するための係数を取得\n *\n * @param {mapray.Sampler} sampler\n *\n * @return {number[]} 平面係数 [x, y, z, w]\n *\n * @private\n */\n _get_elevation_plane( sampler )\n {\n let coords = this._arit_coords;\n\n // 三角形の頂点の高さを取得\n let z_coords = new Array( 3 );\n\n for ( let i = 0; i < 3; ++i ) {\n let mx = coords[2*i ];\n let my = coords[2*i + 1];\n z_coords[i] = sampler.sample( mx, my );\n }\n\n let ox = coords[0];\n let oy = coords[1];\n let oz = z_coords[0];\n\n let x1 = coords[2] - ox;\n let y1 = coords[3] - oy;\n let z1 = z_coords[1] - oz;\n\n let x2 = coords[4] - ox;\n let y2 = coords[5] - oy;\n let z2 = z_coords[2] - oz;\n\n // [nx, ny, nz] = [x1, y1, z1] x [x2, y2, z2]\n let nx = y1*z2 - z1*y2;\n let ny = z1*x2 - x1*z2;\n let nz = x1*y2 - y1*x2;\n\n return [nx, ny, nz, -ox*nx - oy*ny - oz*nz];\n }\n\n}\n\n\n/**\n * @summary 配列からベクトルを設定\n *\n * array[index] から vec に設定する。\n *\n * @private\n */\nfunction\nsetArrayToVec3( array, index, vec )\n{\n vec[0] = array[index];\n vec[1] = array[index + 1];\n vec[2] = array[index + 2];\n}\n\n\n/**\n * @summary 配列からベクトルを設定\n *\n * vec から array[index] に設定する。\n *\n * @private\n */\nfunction\nsetVec3ToArray( vec, array, index )\n{\n array[index] = vec[0];\n array[index + 1] = vec[1];\n array[index + 2] = vec[2];\n}\n\n\n/**\n * @summary 3頂点から正規化法線ベクトルを設定\n * @private\n */\nfunction\nsetTriangleNormal( p0, p1, p2, normal )\n{\n for ( let i = 0; i < 3; ++i ) {\n temp_normal_ax[i] = p1[i] - p0[i];\n temp_normal_ay[i] = p2[i] - p0[i];\n }\n\n GeoMath.cross3( temp_normal_ax, temp_normal_ay, normal );\n GeoMath.normalize3( normal, normal );\n\n return normal;\n}\n\n\nvar temp_normal_ax = GeoMath.createVector3();\nvar temp_normal_ay = GeoMath.createVector3();\n\n\n/**\n * @summary 内部ステータス\n * @enum {object}\n * @constant\n * @private\n */\nvar Status = {\n\n INVALID: { id: \"INVALID\" },\n NORMAL: { id: \"NORMAL\" },\n TRIANGLE_DIRTY: { id: \"TRIANGLE_DIRTY\" },\n MESH_DIRTY: { id: \"MESH_DIRTY\" }\n\n};\n\n\nexport default PolygonEntity;\n","var createTypedArrayConstructor = require('../internals/typed-array-constructor');\n\n// `Int8Array` constructor\n// https://tc39.github.io/ecma262/#sec-typedarray-objects\ncreateTypedArrayConstructor('Int8', function (init) {\n return function Int8Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","/**\n * glTF の bufferView に対応\n * @memberof mapray.gltf\n * @private\n */\nclass BufferView {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index バッファビュー索引\n */\n constructor( ctx, index )\n {\n // glTF の bufferView オブジェクト (specification/2.0/schema/bufferView.schema.json)\n var jbufferView = ctx.gjson.bufferViews[index];\n\n this._buffer = ctx.findBuffer( jbufferView.buffer );\n\n this._byteLength = jbufferView.byteLength;\n this._target = jbufferView.target; // ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER\n\n this._byteOffset = (jbufferView.byteOffset !== undefined) ? jbufferView.byteOffset : 0;\n this._byteStride = (jbufferView.byteStride !== undefined) ? jbufferView.byteStride : 0;\n }\n\n\n /**\n * 参照する gltf.Buffer インスタンスを取得\n * @type {mapray.gltf.Buffer}\n * @readonly\n */\n get buffer() { return this._buffer; }\n\n\n /**\n * ファッファ先頭からのバイトオフセット\n * @type {number}\n * @readonly\n */\n get byteOffset() { return this._byteOffset; }\n\n\n /**\n * インタリーブのバイトストライド\n * @type {number}\n * @readonly\n */\n get byteStride() { return this._byteStride; }\n\n\n /**\n * バッファ分割用の再構築処理\n *\n * @param {mapray.gltf.Buffer} buffer 部分バッファ\n */\n rebuildBySplitter( buffer )\n {\n // 部分バッファ全体を参照するようにする\n this._buffer = buffer;\n this._byteLength = buffer.byteLength;\n this._byteOffset = 0;\n }\n\n}\n\n\nexport default BufferView;\n","import BufferView from \"./BufferView\";\n\n\n/**\n * glTF の accessor に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Accessor {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index アクセサ索引\n */\n constructor( ctx, index )\n {\n // glTF の accessor オブジェクト (specification/2.0/schema/accessor.schema.json)\n var jaccessor = ctx.gjson.accessors[index];\n\n this._type = jaccessor.type; // 文字列: SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4\n this._componentType = jaccessor.componentType; // GL_UNSIGNED_INT ...\n this._count = jaccessor.count; // >= 1\n\n this._bufferView = new BufferView( ctx, jaccessor.bufferView );\n\n this._byteOffset = (jaccessor.byteOffset !== undefined) ? jaccessor.byteOffset : 0;\n this._normalized = jaccessor.normalized || false;\n\n this._min = jaccessor.min ? jaccessor.min.slice() : null;\n this._max = jaccessor.max ? jaccessor.max.slice() : null;\n\n this._index = index;\n }\n\n\n /**\n * 対応する glTF オブジェクトでの索引を取得\n * @type {number}\n * @readonly\n */\n get index() { return this._index; }\n\n\n /**\n * 参照する gltf.BufferView インスタンスを取得\n * @type {mapray.gltf.BufferView}\n * @readonly\n */\n get bufferView() { return this._bufferView; }\n\n\n /**\n * データの型を取得\n * @type {string}\n * @readonly\n */\n get type() { return this._type; }\n\n\n /**\n * データ要素の型を取得\n * @type {number}\n * @readonly\n */\n get componentType() { return this._componentType; }\n\n\n /**\n * データの個数を取得\n * @type {number}\n * @readonly\n */\n get count() { return this._count; }\n\n\n /**\n * バッファビュー先頭からのバイトオフセットを取得\n * @type {number}\n * @readonly\n */\n get byteOffset() { return this._byteOffset; }\n\n\n /**\n * 正規化するか?\n * @type {boolean}\n * @readonly\n */\n get normalized() { return this._normalized; }\n\n\n /**\n * 座標の最小値\n * @type {?number[]}\n * @readonly\n */\n get min() { return this._min; }\n\n\n /**\n * 座標の最大値\n * @type {?number[]}\n * @readonly\n */\n get max() { return this._max; }\n\n\n /**\n * バッファ内でのデータ範囲を取得\n *\n * @return {object} データ範囲 = { first: 先頭オフセット, last: 末尾オフセット + 1 }\n */\n getRangeInBuffer()\n {\n var view = this._bufferView;\n var compo_size = Accessor._ComponentData[this._componentType].bytes;\n\n var data_size = compo_size * Accessor._NumComponents[this._type]; // データのバイト数\n var stride = (view.byteStride == 0) ? data_size : view.byteStride; // ストライド\n\n var first_offset = this._byteOffset + view.byteOffset; // バッファ内での先頭オフセット\n return {\n first: first_offset,\n last: first_offset + stride * (this._count - 1) + data_size // バッファ内での末尾オフセット + 1\n };\n }\n\n\n /**\n * バイトオーダーを修正\n *\n * @param {mapray.BitVector} modmap 修正マップ\n */\n modifyByteOrder( modmap )\n {\n var compo_data = Accessor._ComponentData[this._componentType];\n\n var compo_bytes = compo_data.bytes;\n if ( compo_bytes == 1 ) {\n return; // 1 バイト要素はバイトオーダーがないので処理しない\n }\n\n var num_compos = Accessor._NumComponents[this._type]; // 属性の要素数\n var byte_offset = this._byteOffset + this._bufferView.byteOffset; // バッファ先頭からのバイト数\n var compo_stride = (this._bufferView.byteStride == 0) ? // 要素単位のストライド\n num_compos : this._bufferView.byteStride / compo_bytes;\n\n var arraybuffer = this._bufferView.buffer.binary;\n var dataview = new DataView( arraybuffer, byte_offset );\n var typedarray = new compo_data.typedarray( arraybuffer, byte_offset );\n var getCompo = compo_data.getcompo; // DataView データ取得関数\n\n var compo_shorts = compo_bytes / 2; // 1 要素の short 数\n var short_offset = byte_offset / 2; // バッファ先頭からの short 数\n var short_stride = compo_stride * compo_shorts; // short 単位のストライド\n\n for ( var i = 0; i < this._count; ++i ) {\n var short_base_index = short_offset + i * short_stride;\n var compo_base_index = i * compo_stride;\n for ( var c = 0; c < num_compos; ++c ) {\n var short_index = short_base_index + c * compo_shorts;\n if ( modmap.getBit( short_index ) ) {\n continue; // すでに修正済みの要素なのでスキップ\n }\n\n var compo_index = compo_base_index + c;\n // リトルエンディアン (glTF 仕様) を想定して要素を読み込む\n var value = getCompo.call( dataview, compo_index * compo_bytes, true );\n // ネイティブエンディアン (WebGL 仕様) で要素を書き戻す\n typedarray[compo_index] = value;\n\n modmap.setBit( short_index, true );\n }\n }\n }\n\n\n /**\n * 範囲チェック\n *\n * @param {number} first バッファに対する開始位置\n * @param {number} last バッファに対する終了位置 + 1\n * @return {boolean} 最初のバイトが範囲に含まれるか?\n */\n isIncluded( first, last )\n {\n var byte_offset = this._byteOffset + this._bufferView.byteOffset;\n return (first <= byte_offset) && (byte_offset < last);\n }\n\n\n /**\n * バッファ分割用の再構築処理\n *\n * @param {mapray.gltf.Buffer} buffer 部分バッファ\n * @param {number} first 元バッファに対する buffer の開始位置\n */\n rebuildBySplitter( buffer, first )\n {\n this._index = -1;\n\n // 元バッファ先頭からのデータのバイト位置\n var old_byte_offset = this._byteOffset + this._bufferView.byteOffset;\n\n // 部分バッファ先頭からのデータのバイト位置\n this._byteOffset = old_byte_offset - first;\n\n // bufferView を部分バッファと一致するように更新\n this._bufferView.rebuildBySplitter( buffer );\n }\n\n}\n\n\nAccessor._NumComponents = {\n 'SCALAR': 1,\n 'VEC2': 2,\n 'VEC3': 3,\n 'VEC4': 4,\n 'MAT2': 4,\n 'MAT3': 9,\n 'MAT4': 16\n};\n\n\nAccessor._ComponentData = {\n 5120: { bytes: 1, getcompo: DataView.prototype.getInt8, typedarray: Int8Array }, // BYTE\n 5121: { bytes: 1, getcompo: DataView.prototype.getUint8, typedarray: Uint8Array }, // UNSIGNED_BYTE\n 5122: { bytes: 2, getcompo: DataView.prototype.getInt16, typedarray: Int16Array }, // SHORT\n 5123: { bytes: 2, getcompo: DataView.prototype.getUint16, typedarray: Uint16Array }, // UNSIGNED_SHORT\n 5125: { bytes: 4, getcompo: DataView.prototype.getUint32, typedarray: Uint32Array }, // UNSIGNED_INT\n 5126: { bytes: 4, getcompo: DataView.prototype.getFloat32, typedarray: Float32Array } // FLOAT\n};\n\n\nexport default Accessor;\n","import TextureInfo from \"./TextureInfo\";\nimport NormalTextureInfo from \"./NormalTextureInfo\";\nimport OcclusionTextureInfo from \"./OcclusionTextureInfo\";\nimport CommonData from \"./CommonData\";\n\n\n/**\n * glTF の material に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Material {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index マテリアル索引\n */\n constructor( ctx, index )\n {\n const jmaterial = ctx.gjson.materials[index];\n this._commonData = new CommonData( jmaterial, ctx );\n\n this._pbrMetallicRoughness = {\n baseColorFactor: [1.0, 1.0, 1.0, 1.0],\n baseColorTexture: null,\n metallicFactor: 1.0,\n roughnessFactor: 1.0,\n metallicRoughnessTexture: null\n };\n\n this._doubleSided = false;\n this._alphaMode = \"OPAQUE\";\n this._alphaCutoff = 0.5;\n this._emissiveFactor = [0.0, 0.0, 0.0];\n this._emissiveTexture = null;\n this._normalTexture = null;\n this._occlusionTexture = null;\n\n // glTF の material オブジェクト (specification/2.0/schema/material.schema.json)\n this._setupPbrMetallicRoughness( jmaterial, ctx );\n this._setupGenericParameters( jmaterial, ctx );\n }\n\n\n /**\n * glTF オブジェクトの共通データ\n *\n * @type {mapray.gltf.CommonData}\n * @readonly\n */\n get commonData() { return this._commonData; }\n\n\n /**\n * MetallicRoughness PBR パラメータ\n * @type {object}\n * @readonly\n */\n get pbrMetallicRoughness() { return this._pbrMetallicRoughness; }\n\n\n /**\n * 両面レンダリングの有無\n * @type {boolean}\n * @readonly\n */\n get doubleSided() { return this._doubleSided; }\n\n\n /**\n * αモード\n * @type {string}\n * @readonly\n */\n get alphaMode() { return this._alphaMode; }\n\n\n /**\n * αカットオフ\n * @type {number}\n * @readonly\n */\n get alphaCutoff() { return this._alphaCutoff; }\n\n\n /**\n * 自己発光係数\n * @type {mapray.Vector3}\n * @readonly\n */\n get emissiveFactor() { return this._emissiveFactor; }\n\n\n /**\n * 自己発光テクスチャ\n * @type {?mapray.gltf.TextureInfo}\n * @readonly\n */\n get emissiveTexture() { return this._emissiveTexture; }\n\n\n /**\n * 法線テクスチャ\n * @type {?mapray.gltf.NormalTextureInfo}\n * @readonly\n */\n get normalTexture() { return this._normalTexture; }\n\n\n /**\n * 遮蔽テクスチャ\n * @type {?mapray.gltf.OcclusionTextureInfo}\n * @readonly\n */\n get occlusionTexture() { return this._occlusionTexture; }\n\n\n /**\n * this._pbrMetallicRoughness を設定\n *\n * @param {object} jmaterial glTF の material オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupPbrMetallicRoughness( jmaterial, ctx )\n {\n if ( jmaterial.pbrMetallicRoughness === undefined ) {\n return;\n }\n\n // glTF の pbrMetallicRoughness オブジェクト (specification/2.0/schema/material.pbrMetallicRoughness.schema.json)\n var src = jmaterial.pbrMetallicRoughness;\n var dst = this._pbrMetallicRoughness;\n\n if ( src.baseColorFactor !== undefined ) {\n dst.baseColorFactor = src.baseColorFactor.slice();\n }\n\n if ( src.baseColorTexture !== undefined ) {\n dst.baseColorTexture = new TextureInfo( src.baseColorTexture, ctx );\n ctx.addTextureInfo( dst.baseColorTexture );\n }\n\n if ( src.metallicFactor !== undefined ) {\n dst.metallicFactor = src.metallicFactor;\n }\n\n if ( src.roughnessFactor !== undefined ) {\n dst.roughnessFactor = src.roughnessFactor;\n }\n\n if ( src.metallicRoughnessTexture !== undefined ) {\n dst.metallicRoughnessTexture = new TextureInfo( src.metallicRoughnessTexture, ctx );\n ctx.addTextureInfo( dst.metallicRoughnessTexture );\n }\n }\n\n\n /**\n * this._doubleSided などを設定\n *\n * @param {object} jmaterial glTF の material オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupGenericParameters( jmaterial, ctx )\n {\n if ( jmaterial.doubleSided !== undefined ) {\n this._doubleSided = jmaterial.doubleSided;\n }\n\n if ( jmaterial.alphaMode !== undefined ) {\n this._alphaMode = jmaterial.alphaMode;\n }\n\n if ( jmaterial.alphaCutoff !== undefined ) {\n this._alphaCutoff = jmaterial.alphaCutoff;\n }\n\n if ( jmaterial.emissiveFactor !== undefined ) {\n this._emissiveFactor = jmaterial.emissiveFactor.slice();\n }\n\n if ( jmaterial.emissiveTexture !== undefined ) {\n this._emissiveTexture = new TextureInfo( jmaterial.emissiveTexture, ctx );\n ctx.addTextureInfo( this._emissiveTexture );\n }\n\n if ( jmaterial.normalTexture !== undefined ) {\n this._normalTexture = new NormalTextureInfo( jmaterial.normalTexture, ctx );\n ctx.addTextureInfo( this._normalTexture );\n }\n\n if ( jmaterial.occlusionTexture !== undefined ) {\n this._occlusionTexture = new OcclusionTextureInfo( jmaterial.occlusionTexture, ctx );\n ctx.addTextureInfo( this._occlusionTexture );\n }\n }\n\n}\n\n\nexport default Material;\n","import Accessor from \"./Accessor\";\nimport Material from \"./Material\";\n\n\n/**\n * glTF の primitive に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Primitive {\n\n /**\n * 初期化\n * @param {object} jprimitive glTF の primitive オブジェクト (specification/2.0/schema/mesh.primitive.schema.json)\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n */\n constructor( jprimitive, ctx )\n {\n this._mode = (jprimitive.mode !== undefined) ? jprimitive.mode : 4;\n this._attributes = {};\n this._indices = null;\n this._material = null;\n\n this._setupAttributes( jprimitive.attributes, ctx );\n this._setupIndices( jprimitive, ctx );\n this._setupMaterial( jprimitive, ctx );\n }\n\n\n /**\n * プリミティブモード\n *\n * @type {number}\n * @readonly\n */\n get mode() { return this._mode; }\n\n\n /**\n * @summary 頂点属性の辞書\n *\n * 頂点属性名から Accessor を引く辞書
\n *\n * @type {object}\n * @readonly\n */\n get attributes() { return this._attributes; }\n\n\n /**\n * インデックス\n *\n * @type {?mapray.gltf.Accessor}\n * @readonly\n */\n get indices() { return this._indices; }\n\n\n /**\n * マテリアル\n *\n * @type {?mapray.gltf.Material}\n * @readonly\n */\n get material() { return this._material; }\n\n\n /**\n * this._attributes を設定\n *\n * @param {object} jattributes glTF の primitive/attributes オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupAttributes( jattributes, ctx )\n {\n for ( var name in jattributes ) {\n var accessor = new Accessor( ctx, jattributes[name] );\n this._attributes[name] = accessor;\n ctx.addAccessor( accessor, \"ATTRIBUTE\" );\n }\n }\n\n\n /**\n * this._indices を設定\n *\n * @param {object} jprimitive glTF の primitive オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupIndices( jprimitive, ctx )\n {\n if ( jprimitive.indices !== undefined ) {\n var accessor = new Accessor( ctx, jprimitive.indices );\n this._indices = accessor;\n ctx.addAccessor( accessor, \"INDEX\" );\n }\n }\n\n\n /**\n * this._material を設定\n *\n * @param {object} jprimitive glTF の primitive オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupMaterial( jprimitive, ctx )\n {\n if ( jprimitive.material !== undefined ) {\n this._material = new Material( ctx, jprimitive.material );\n }\n }\n\n}\n\n\nexport default Primitive;\n","import Primitive from \"./Primitive\";\n\n\n/**\n * glTF の mesh に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Mesh {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index メッシュ索引\n */\n constructor( ctx, index )\n {\n this._primitives = [];\n\n // glTF の mesh オブジェクト (specification/2.0/schema/mesh.schema.json)\n var jmesh = ctx.gjson.meshes[index];\n\n var jprimitives = jmesh.primitives;\n for ( var i = 0; i < jprimitives.length; ++i ) {\n // glTF の primitive オブジェクト (specification/2.0/schema/mesh.primitive.schema.json)\n var jprimitive = jprimitives[i];\n this._primitives.push( new Primitive( jprimitive, ctx ) );\n }\n }\n\n\n /**\n * プリミティブの配列を取得\n *\n * @type {mapray.gltf.Primitive[]}\n * @readonly\n */\n get primitives() { return this._primitives; }\n\n}\n\n\nexport default Mesh;\n","import GeoMath from \"../GeoMath\";\nimport Mesh from \"./Mesh\";\nimport CommonData from \"./CommonData\";\n\n\n/**\n * glTF の node に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Node {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index ノード索引\n */\n constructor( ctx, index )\n {\n // glTF の node オブジェクト (specification/2.0/schema/node.schema.json)\n var jnode = ctx.gjson.nodes[index];\n\n this._commonData = new CommonData( jnode, ctx );\n\n this._children = [];\n this._matrix = null;\n this._mesh = null;\n\n this._setupChildren( jnode, ctx );\n this._setupMatrix( jnode );\n this._setupMesh( jnode, ctx );\n }\n\n\n /**\n * glTF オブジェクトの共通データ\n *\n * @type {mapray.gltf.CommonData}\n * @readonly\n */\n get commonData() { return this._commonData; }\n\n\n /**\n * 子ノードの配列を取得\n * @type {mapray.gltf.Node[]}\n * @readonly\n */\n get children() { return this._children; }\n\n\n /**\n * 変換行列を取得\n * @type {?mapray.Matrix}\n * @readonly\n */\n get matrix() { return this._matrix; }\n\n\n /**\n * メッシュを取得\n * @type {?mapray.gltf.Mesh}\n * @readonly\n */\n get mesh() { return this._mesh; }\n\n\n /**\n * this._children を設定\n *\n * @param {object} jnode glTF の node オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupChildren( jnode, ctx )\n {\n var children = jnode.children;\n if ( children === undefined ) return;\n\n for ( var i = 0; i < children.length; ++i ) {\n var index = children[i];\n this._children.push( new Node( ctx, index ) );\n }\n }\n\n\n /**\n * this._matrix を設定\n *\n * @param {object} jnode glTF の node オブジェクト\n * @private\n */\n _setupMatrix( jnode )\n {\n if ( jnode.matrix ) {\n // 行列指定\n this._matrix = GeoMath.createMatrix( jnode.matrix );\n }\n else if ( jnode.scale || jnode.rotation || jnode.translation ) {\n // SQT 指定\n var [sx, sy, sz] = jnode.scale || [1, 1, 1];\n var [qx, qy, qz, qw] = jnode.rotation || [0, 0, 0, 1];\n var [tx, ty, tz] = jnode.translation || [0, 0, 0];\n\n // [ 1 - 2y^2 - 2z^2, 2x y - 2w z, 2x z + 2w y ]\n // rotation[x, y, z, w] = [ 2x y + 2w z, 1 - 2x^2 - 2z^2, 2y z - 2w x ]\n // [ 2x z - 2w y, 2y z + 2w x, 1 - 2x^2 - 2y^2 ]\n\n this._matrix = GeoMath.createMatrix( [\n (1 - 2*(qy*qy + qz*qz))*sx,\n 2*(qx*qy + qz*qw)*sx,\n 2*(qx*qz - qy*qw)*sx,\n 0,\n 2*(qx*qy - qz*qw)*sy,\n (1 - 2*(qx*qx + qz*qz))*sy,\n 2*(qx*qw + qy*qz)*sy,\n 0,\n 2*(qy*qw + qx*qz)*sz,\n 2*(qy*qz - qx*qw)*sz,\n (1 - 2*(qx*qx + qy*qy))*sz,\n 0,\n tx,\n ty,\n tz,\n 1\n ] );\n }\n }\n\n\n /**\n * this._mesh を設定\n *\n * @param {object} jnode glTF の node オブジェクト\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @private\n */\n _setupMesh( jnode, ctx )\n {\n var index = jnode.mesh;\n if ( index === undefined ) return; // メッシュなしのノード\n\n this._mesh = new Mesh( ctx, index );\n }\n\n}\n\n\nexport default Node;\n","import Node from \"./Node\";\nimport CommonData from \"./CommonData\";\n\n\n/**\n * @summary glTF scene\n *\n * @classdesc\n * glTF の scene に対応するオブジェクトである。
\n *\n * @memberof mapray.gltf\n * @private\n * @see https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/schema/scene.schema.json\n */\nclass Scene {\n\n /**\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index シーン索引\n */\n constructor( ctx, index )\n {\n // glTF の scene オブジェクト\n const jscene = ctx.gjson.scenes[index];\n\n this._commonData = new CommonData( jscene, ctx );\n\n this._root_nodes = [];\n\n for ( const node_index of jscene.nodes || [] ) {\n this._root_nodes.push( new Node( ctx, node_index ) );\n }\n }\n\n\n /**\n * glTF オブジェクトの共通データ\n *\n * @type {mapray.gltf.CommonData}\n * @readonly\n */\n get commonData() { return this._commonData; }\n\n\n /**\n * 最上位ノードの配列\n *\n * @type {mapray.gltf.Node[]}\n * @readonly\n */\n get root_nodes()\n {\n return this._root_nodes;\n }\n\n\n /**\n * @summary シーン名を取得\n *\n * シーン名が存在すればシーン名の文字列を返し、存在しなければ null を返す。
\n *\n * @type {?string}\n * @readonly\n */\n get name() { return this._commonData.getName(); }\n\n}\n\n\nexport default Scene;\n","/**\n * バッファの分割を補助\n *\n * @memberof mapray.gltf\n * @private\n */\nclass BufferSplitter {\n\n /**\n */\n constructor()\n {\n // 処理しやすいように最初と最後はにダミーの断片を置く\n\n var frag0 = new Fragment( -2, -1 );\n var frag1 = new Fragment( 2**32 + 1, 2**32 + 2 );\n\n frag0.next = frag1;\n frag1.prev = frag0;\n\n this._fragments = frag0;\n }\n\n\n /**\n * 分割を更新\n *\n * @param {mapray.gltf.Accessor} accessor バッファを参照するアクセサ\n */\n update( accessor )\n {\n const range = accessor.getRangeInBuffer();\n this._updateByRange( { first: BufferSplitter._floor4( range.first ), last: range.last } );\n }\n\n\n /**\n * 分割の更新を終了\n *\n * @param {mapray.gltf.Buffer} buffer 分割されるバッファ\n */\n close( buffer )\n {\n // 先頭のダミーを削除\n this._fragments = this._fragments.next;\n\n // 部分バッファを設定\n for ( let frag = this._fragments ;; frag = frag.next ) {\n if ( frag.next === null ) {\n // 最後のダミーを削除\n frag.prev.next = null;\n break;\n }\n frag.buffer = buffer.createSubBuffer( frag.first, frag.last );\n }\n }\n\n\n /**\n * アクセサを部分バッファで再構築\n *\n * @param {mapray.gltf.Accessor} accessor 再構築するアクセサ\n */\n rebuildAccessor( accessor )\n {\n for ( let frag = this._fragments; frag !== null; frag = frag.next ) {\n if ( accessor.isIncluded( frag.first, frag.last ) ) {\n accessor.rebuildBySplitter( frag.buffer, frag.first );\n break;\n }\n }\n }\n\n\n /**\n * 分割を更新\n *\n * @param {object} range Accessor の範囲\n * @private\n */\n _updateByRange( range )\n {\n for ( let frag = this._fragments; frag.next !== null; ) {\n if ( frag.isInside( range ) ) {\n // frag 断片と frag.next 断片の間に新しい range 断片を挿入\n let frag0 = frag;\n let frag1 = frag.next;\n let fragx = new Fragment( range.first, range.last );\n frag0.next = fragx;\n frag1.prev = fragx;\n fragx.prev = frag0;\n fragx.next = frag1;\n break;\n }\n else if ( frag.isTouch( range ) ) {\n // range に frag を統合し、frag を削除し、frag.prev から始める\n let frag0 = frag.prev;\n let frag1 = frag.next;\n frag0.next = frag1;\n frag1.prev = frag0;\n\n range = frag.mergeRange( range );\n frag = frag0;\n }\n else {\n frag = frag.next;\n }\n }\n }\n\n\n /**\n * 4 の倍数に切り下げ\n *\n * @param {number} value 切り下げる値\n * @return {number} value を 4 の倍数に切り下げた整数\n * @private\n */\n static\n _floor4( value )\n {\n return 4 * Math.floor( value / 4 );\n }\n\n}\n\n\n/**\n * バッファの断片\n *\n * @memberof mapray.gltf.BufferSplitter\n * @private\n */\nclass Fragment {\n\n /**\n * @param {number} first 先頭オフセット\n * @param {number} last 末尾オフセット + 1\n */\n constructor( first, last )\n {\n this.first = first;\n this.last = last;\n this.buffer = null; // 部分バッファ\n this.prev = null;\n this.next = null;\n }\n\n\n /**\n * range は frag と frag.next の間の内側か?\n *\n * @param {object} range\n * @return {boolean}\n * @private\n */\n isInside( range )\n {\n return (this.last < range.first) && (range.last < this.next.first);\n }\n\n\n /**\n * range は frag と接触しているか?\n *\n * @param {object} range\n * @return {boolean}\n */\n isTouch( range )\n {\n return (this.last >= range.first) && (range.last >= this.first);\n }\n\n\n /**\n * this と range を結合した range を取得\n *\n * @param {object} range\n * @return {object}\n */\n mergeRange( range )\n {\n return {\n first: Math.min( this.first, range.first ),\n last: Math.max( this.last, range.last )\n };\n }\n\n}\n\n\nexport default BufferSplitter;\n","/**\n * @summary ビット配列\n *\n * @memberof mapray\n * @private\n */\nclass BitVector {\n\n /**\n * 初期値はすべてのビットが false である。\n *\n * @param {number} length ビット数\n */\n constructor( length )\n {\n this._length = length;\n this._array = new Uint32Array( Math.ceil( length / 32 ) );\n }\n\n\n /**\n * @summary ビット数\n * @type {number}\n * @readonly\n */\n get length()\n {\n return this._length;\n }\n\n\n /**\n * @summary ビットを設定\n *\n * @param {number} index インデックス\n * @param {boolean} value 値\n */\n setBit( index, value )\n {\n var uint32_index = Math.floor( index / 32 );\n var uint32_value = this._array[uint32_index];\n var uint32_mask = 1 << (index % 32);\n\n this._array[uint32_index] = value ? (uint32_value | uint32_mask) : (uint32_value & ~uint32_mask);\n }\n\n\n /**\n * @summary ビットを取得\n *\n * @param {number} index インデックス\n * @return {boolean} 値\n */\n getBit( index )\n {\n var uint32_index = Math.floor( index / 32 );\n var uint32_value = this._array[uint32_index];\n var uint32_mask = 1 << (index % 32);\n\n return (uint32_value & uint32_mask) != 0;\n }\n\n}\n\n\nexport default BitVector;\n","import BufferSplitter from \"./BufferSplitter\";\nimport BitVector from \"../BitVector\";\n\n\n/**\n * コンテキストでの Buffer 管理アイテム\n *\n * @memberof mapray.gltf\n * @private\n */\nclass BufferEntry {\n\n /**\n * @param {mapray.gltf.Buffer} buffer バッファ\n */\n constructor( buffer )\n {\n this._buffer = buffer;\n this._attrib_accessors = [];\n this._index_accessors = [];\n }\n\n\n /**\n * 管理対象のバッファを取得\n *\n * @type {mapray.gltf.Buffer}\n * @readonly\n */\n get buffer() { return this._buffer; }\n\n\n /**\n * 頂点属性で使われている Accessor インスタンスを追加\n */\n addAttributeAccessor( accessor )\n {\n this._attrib_accessors.push( accessor );\n }\n\n\n /**\n * インデックスで使われている Accessor インスタンスを追加\n */\n addIndexAccessor( accessor )\n {\n this._index_accessors.push( accessor );\n }\n\n\n /**\n * バイナリをマシンのバイトオーダーに合わせて書き換え\n */\n rewriteByteOrder()\n {\n var modmap = new BitVector( Math.ceil( this._buffer.byteLength / 2 ) );\n\n for ( const accessor of this._getUnitedOriginalAccessors() ) {\n accessor.modifyByteOrder( modmap );\n }\n }\n\n\n /**\n * バッファを分割し、Accessor を再構築\n */\n splitBufferAndRebuildAccessors()\n {\n this._splitBufferAndRebuildAccessors( this._attrib_accessors );\n this._splitBufferAndRebuildAccessors( this._index_accessors );\n }\n\n\n /**\n * バッファを分割し、Accessor を再構築\n *\n * @param {iterable.} accessors 入力 Accessor 反復子\n */\n _splitBufferAndRebuildAccessors( accessors )\n {\n var splitter = new BufferSplitter();\n\n for ( const accessor of BufferEntry._getOriginalAccessors( accessors ) ) {\n splitter.update( accessor );\n }\n\n splitter.close( this._buffer );\n\n for ( const accessor of accessors ) {\n splitter.rebuildAccessor( accessor );\n }\n }\n\n\n /**\n * バッファを参照ている原初 Accessor の反復子\n *\n * @return {iterable.} 原初 Accessor 反復子\n * @private\n */\n _getUnitedOriginalAccessors()\n {\n return BufferEntry._getOriginalAccessors( this._attrib_accessors.concat( this._index_accessors ) );\n }\n\n\n /**\n * 原初 Accessor の反復子を取得\n *\n * @param {iterable.} accessors 入力 Accessor 反復子\n * @return {iterable.} 原初 Accessor 反復子\n * @private\n */\n static\n _getOriginalAccessors( accessors )\n {\n var orig_accessors = new Map();\n\n for ( const accessor of accessors ) {\n const key = accessor.index;\n if ( !orig_accessors.has( key ) ) {\n orig_accessors.set( key, accessor );\n }\n }\n\n return orig_accessors.values();\n }\n\n}\n\n\nexport default BufferEntry;\n","/**\n * コンテキストでの Image 管理アイテム\n *\n * @memberof mapray.gltf\n * @private\n */\nclass ImageEntry {\n\n /**\n * @param {mapray.gltf.Image} image イメージ\n */\n constructor( image )\n {\n this._image = image;\n this._texinfo_objects = [];\n }\n\n\n /**\n * イメージを取得\n * @type {mapray.gltf.Texture}\n * @readonly\n */\n get image() { return this._image; }\n\n\n /**\n * TextureInfo インスタンスを追加\n *\n * @param {mapray.gltf.TextureInfo} info 追加する TextureInfo インスタンス\n */\n addTextureInfo( info )\n {\n this._texinfo_objects.push( info );\n }\n\n\n /**\n * テクスチャ情報を再構築\n */\n rebuildTextureInfo()\n {\n var texinfo_objects = this._texinfo_objects;\n\n if ( texinfo_objects.length <= 1 ) {\n // イメージが複数の TextureInfo から参照されないので\n // 何も変更しない\n return;\n }\n\n // この画像を使っている代表テクスチャ\n var representative_texture = texinfo_objects[0].texture;\n\n // この画像を使っている (テクスチャ情報内の) テクスチャを\n // 代表テクスチャに置き換える\n for ( var i = 1; i < texinfo_objects.length; ++i ) {\n texinfo_objects[i].texture = representative_texture;\n }\n }\n\n}\n\n\nexport default ImageEntry;\n","/**\n * glTF の buffer に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Buffer {\n\n /**\n * @param {mapray.gltf.Context} [ctx] 読み込みコンテキスト\n * @param {number} [index] buffers 索引\n */\n constructor( ctx, index )\n {\n if ( ctx === undefined ) {\n // 引数なし構築 (特殊インスタンス用)\n this._index = -1;\n this._byteLength = 0;\n this._uri = null;\n this._binary = null;\n }\n else {\n this._index = index;\n\n // glTF の buffer オブジェクト (specification/2.0/schema/buffer.schema.json)\n var jbuffer = ctx.gjson.buffers[index];\n\n this._byteLength = jbuffer.byteLength;\n\n if ( jbuffer.uri !== undefined ) {\n ctx.onStartLoadBuffer();\n ctx.loadBinary( jbuffer.uri )\n .then( buffer => {\n // バイナリデータの取得に成功\n this._binary = buffer;\n ctx.onFinishLoadBuffer();\n } )\n .catch( error => {\n // バイナリデータの取得に失敗\n console.error( error );\n ctx.onFinishLoadBuffer( error );\n } );\n }\n else {\n // todo: GLB-stored Buffer\n this._uri = null;\n this._binary = null;\n }\n }\n }\n\n\n /**\n * 対応する glTF オブジェクトでの索引を取得\n * @type {number}\n * @readonly\n */\n get index() { return this._index; }\n\n\n /**\n * バイナリデータ\n * @type {ArrayBuffer}\n * @readonly\n */\n get binary() { return this._binary; }\n\n\n /**\n * バイナリデータのバイト数を取得\n * @type {number}\n * @readonly\n */\n get byteLength() { return this._byteLength; }\n\n\n /**\n * 部分バッファを生成\n *\n * @param {number} first 最初のバイト位置\n * @param {number} last 最後のバイト位置 + 1\n * @return {mapray.gltf.Buffer} 部分バッファ\n */\n createSubBuffer( first, last )\n {\n var subBuffer = new Buffer();\n\n subBuffer._byteLength = last - first;\n subBuffer._binary = this._binary.slice( first, last );\n\n return subBuffer;\n }\n\n}\n\n\nexport default Buffer;\n","import BufferView from \"./BufferView\";\n\n\n/**\n * glTF の image に対応\n * @memberof mapray.gltf\n * @private\n */\nclass Image {\n\n /**\n * 初期化\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {number} index images 索引\n */\n constructor( ctx, index )\n {\n this._index = index;\n\n // glTF の image オブジェクト (specification/2.0/schema/image.schema.json)\n var jimage = ctx.gjson.images[index];\n\n if ( jimage.uri !== undefined ) {\n ctx.onStartLoadImage();\n ctx.loadImage( jimage.uri )\n .then( image => {\n this._image = image;\n ctx.onFinishLoadImage();\n } )\n .catch( error => {\n ctx.onFinishLoadImage( error );\n } );\n }\n else if ( jimage.bufferView !== undefined ) {\n this._bufferView = new BufferView( ctx, jimage.bufferView );\n }\n\n // mimeType は \"image/jpeg\" または \"image/png\" で bufferView のときは必須\n // uri のときは任意であるが mimeType が指定されているとき、タイプは mimeType と解釈する\n this._mimeType = jimage.mimeType;\n\n this._image = null;\n }\n\n\n /**\n * 対応する glTF オブジェクトでの索引を取得\n * @type {number}\n * @readonly\n */\n get index() { return this._index; }\n\n\n /**\n * 画像データ\n * @type {HTMLImageElement}\n * @readonly\n */\n get image() { return this._image; }\n\n}\n\n\nexport default Image;\n","import Content from \"./Content\";\nimport Scene from \"./Scene\";\nimport BufferEntry from \"./BufferEntry\";\nimport ImageEntry from \"./ImageEntry\";\nimport Buffer from \"./Buffer\";\nimport Image from \"./Image\";\n\n\n/**\n * glTF 読込みコンテキスト\n *\n * @memberof mapray.gltf\n * @private\n */\nclass Context {\n\n /**\n * @param {object} body Tool.load() の同名パラメータを参照\n * @param {object} [options] Tool.load() の同名パラメータを参照\n */\n constructor( body, options )\n {\n var opts = options || {};\n\n this._gjson = body;\n this._base_resource = opts.base_resource;\n this._binary_type = opts.binary_type;\n this._image_type = opts.image_type;\n this._supported_extensions = opts.supported_extensions || [];\n\n this._resolve = null; // Promise の resolve() 関数\n this._reject = null; // Promise の reject() 関数\n\n this._used_extensions = new Set(); // コンテンツが使用する拡張機能名の集合\n this._scenes = [];\n this._default_scene_index = -1;\n\n this._buffer_entries = []; // 共有用バッファの管理 (疎配列)\n this._image_entries = []; // 共有用イメージの管理 (疎配列)\n\n this._body_finished = false; // body の解析を終えたか?\n this._load_count = 0; // 現在リクエスト中のオブジェクト数\n this._load_error = null; // エラーが発生したときのエラーオブジェクト\n this._settled = false; // Promise の状態が Fulfilled または Rejected か?\n }\n\n\n /**\n * @summary glTF の読込みと解析\n *\n * @return {Promise} 読込み Promise (mapray.gltf.Content)\n */\n load()\n {\n return new Promise( (resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n\n // glTF バージョンを確認\n var version = this._loadVersion();\n if ( version.major < 2 ) {\n reject( new Error( \"glTF version error\" ) );\n return;\n }\n\n // コンテンツに必須の拡張機能をサポートしているかを確認\n const supported_ext = this._getSupportedExtensionNames();\n for ( const required_ext of this._enumRequiredExtensionNames() ) {\n if ( !supported_ext.has( required_ext ) ) {\n reject( new Error( \"glTF extension error: \" + required_ext ) );\n return;\n }\n }\n\n this._loadExtensionsUsed();\n this._loadScenes();\n this._loadDefaultSceneIndex();\n this._onFinishLoadBody();\n } );\n }\n\n\n /**\n * glTF バージョンを解析\n *\n * @return {object} { major: major_version, minor: minor_version }\n * @private\n */\n _loadVersion()\n {\n // asset.schema\n\n var asset = this._gjson.asset; // 必須\n var version = asset.version; // 必須\n\n var version_array = /^(\\d+)\\.(\\d+)/.exec( version );\n var major_version = Number( version_array[1] );\n var minor_version = Number( version_array[2] );\n\n return { major: major_version,\n minor: minor_version };\n }\n\n\n /**\n * @summary 必須の拡張機能の名前を列挙\n *\n * @desc\n * glTF アセットから必須の拡張機能を解析して、\n * その拡張機能の名前を列挙する。
\n *\n * @return {iterable.} 拡張機能名の列挙\n *\n * @private\n */\n _enumRequiredExtensionNames()\n {\n return this._gjson.extensionsRequired || [];\n }\n\n\n /**\n * @summary 対応可能な拡張機能の名前の集合を取得\n *\n * @desc\n * glTF のローダーとクライアントが対応できる拡張機能の\n * 名前の集合を取得する。
\n *\n * @return {Set.} 拡張機能名の集合\n *\n * @private\n */\n _getSupportedExtensionNames()\n {\n // ローダー自身が対応できる拡張機能\n // ※ 今のところサポートできる拡張機能はない\n const supported_extensions_by_loader = [];\n\n // ローダーを呼び出す側が対応できる拡張機能\n const supported_extensions_by_client = this._supported_extensions;\n\n return new Set( supported_extensions_by_loader.concat( supported_extensions_by_client ) );\n }\n\n\n /**\n * @summary コンテンツが使用する拡張機能を読み込む\n *\n * @desc\n * extensionsUsed プロパティを読み込み this._used_extensions を設定する。
\n *\n * @private\n */\n _loadExtensionsUsed()\n {\n this._used_extensions = new Set( this._gjson.extensionsUsed || [] );\n }\n\n\n /**\n * @summary すべてのシーンを読み込む\n *\n * シーンを読み込み、オブジェクトを this._scenes の配列に設定する。
\n *\n * @private\n */\n _loadScenes()\n {\n const num_scenes = (this._gjson.scenes || []).length;\n const scenes = [];\n\n for ( let index = 0; index < num_scenes; ++index ) {\n scenes.push( new Scene( this, index ) );\n }\n\n this._scenes = scenes;\n }\n\n\n /**\n * @summary 既定シーンの索引を読み込む\n *\n * 既定のシーン索引を解析し、this._default_scene_index に設定する。
\n *\n * @private\n */\n _loadDefaultSceneIndex()\n {\n if ( typeof this._gjson.scene == 'number' ) {\n this._default_scene_index = this._gjson.scene;\n }\n }\n\n\n /**\n * glTF 最上位オブジェクト\n * @type {object}\n * @readonly\n */\n get gjson() { return this._gjson; }\n\n\n /**\n * @summary 拡張機能の抽出\n *\n * @desc\n * 拡張機能固有オブジェクト extensions から extensionsUsed\n * に存在するものだけを抽出する。
\n *\n * @param {object} extensions\n *\n * @return {object}\n */\n extractUsedExtensions( extensions )\n {\n let dict = {};\n\n for ( let key in extensions ) {\n if ( this._used_extensions.has( key ) ) {\n dict[key] = extensions[key];\n }\n }\n\n return dict;\n }\n\n\n /**\n * バッファデータの読み込みを開始\n * @param {mapray.gltf.Context} ctx 読み込みコンテキスト\n * @param {string} url バッファデータの URL\n * @private\n */\n loadBinary( path )\n {\n return this._base_resource.loadSubResource( path, { type: this._binary_type } );\n }\n\n\n loadImage( path )\n {\n return this._base_resource.loadSubResource( path, { type: this._image_type } );\n }\n\n\n /**\n * バッファを検索\n * @param {number} index バッファ索引\n * @return {mapray.gltf.Buffer} gltf.Buffer オブジェクト\n */\n findBuffer( index )\n {\n if ( this._buffer_entries[index] === undefined ) {\n this._buffer_entries[index] = new BufferEntry( new Buffer( this, index ) );\n }\n\n return this._buffer_entries[index].buffer;\n }\n\n\n /**\n * イメージを検索\n * @param {number} index イメージ索引\n * @return {mapray.gltf.Image} gltf.Image オブジェクト\n */\n findImage( index )\n {\n if ( this._image_entries[index] === undefined ) {\n this._image_entries[index] = new ImageEntry( new Image( this, index ) );\n }\n\n return this._image_entries[index].image;\n }\n\n\n /**\n * gltf.Accessor を追加\n *\n * @param {mapray.gltf.Accessor} accessor アクセサオブジェクト\n * @param {string} usage 用途 (\"ATTRIBUTE\" | \"INDEX\")\n */\n addAccessor( accessor, usage )\n {\n var entry = this._buffer_entries[accessor.bufferView.buffer.index];\n\n switch ( usage ) {\n case \"ATTRIBUTE\":\n entry.addAttributeAccessor( accessor );\n break;\n case \"INDEX\":\n entry.addIndexAccessor( accessor );\n break;\n }\n }\n\n\n /**\n * gltf.TextureInfo を追加\n *\n * @param {mapray.gltf.TextureInfo} info テクスチャ情報\n */\n addTextureInfo( info )\n {\n var image = info.texture.source;\n var entry = this._image_entries[image.index];\n entry.addTextureInfo( info );\n }\n\n\n /**\n * バイナリを読み込み始めたときの処理\n */\n onStartLoadBuffer()\n {\n this._load_count += 1;\n }\n\n\n /**\n * バイナリを読み込み終わったときの処理\n *\n * @param {Error} [error] 失敗したときのエラーオブジェクト\n */\n onFinishLoadBuffer( error )\n {\n if ( error ) {\n this._load_error = error;\n }\n this._load_count -= 1;\n this._onFinishLoadSomething();\n }\n\n\n /**\n * 画像を読み込み始めたときの処理\n */\n onStartLoadImage()\n {\n this._load_count += 1;\n }\n\n\n /**\n * 画像を読み込み終わったときの処理\n *\n * @param {Error} [error] 失敗したときのエラーオブジェクト\n */\n onFinishLoadImage( error )\n {\n if ( error ) {\n this._load_error = error;\n }\n this._load_count -= 1;\n this._onFinishLoadSomething();\n }\n\n\n /**\n * glTF 本体を読み込み終わったときの処理\n * @private\n */\n _onFinishLoadBody()\n {\n this._body_finished = true;\n this._onFinishLoadSomething();\n }\n\n\n /**\n * 何かを読み込み終わったときの処理\n * @private\n */\n _onFinishLoadSomething()\n {\n if ( this._settled ) {\n // すでに Promise に結果を設定しているので何もしない\n }\n else if ( this._load_error !== null ) {\n // どこかで失敗した\n this._reject( this._load_error );\n this._settled = true;\n }\n else if ( this._body_finished && (this._load_count == 0) ) {\n // 外部ファイルも含めて、すべて読み込み終わった\n this._rewriteBuffersForByteOrder();\n this._splitBuffersAndRebuildAccessors();\n this._rebuildTextureInfo();\n this._resolve( new Content( this, this._scenes, this._default_scene_index ) );\n this._settled = true;\n }\n }\n\n\n /**\n * すべてのバッファのバイトオーダーを書き換える\n * @private\n */\n _rewriteBuffersForByteOrder()\n {\n for ( const entry of this._buffer_entries ) {\n if ( entry !== undefined ) {\n entry.rewriteByteOrder();\n }\n }\n }\n\n\n /**\n * バッファを分割し、Accessor を再構築\n * @private\n */\n _splitBuffersAndRebuildAccessors()\n {\n for ( const entry of this._buffer_entries ) {\n if ( entry !== undefined ) {\n entry.splitBufferAndRebuildAccessors();\n }\n }\n }\n\n\n /**\n * テクスチャ情報を再構築\n * @private\n */\n _rebuildTextureInfo()\n {\n for ( const entry of this._image_entries ) {\n if ( entry !== undefined ) {\n entry.rebuildTextureInfo();\n }\n }\n }\n\n}\n\n\n\nexport default Context;\n","import Context from \"./Context\";\n\n\n/**\n * glTF 関連のツール\n *\n * @memberof mapray.gltf\n * @private\n */\nclass Tool {\n\n /**\n * @summary glTF データを解析してオブジェクトを構築\n *\n * @param {object} body データの本体 (JSON オブジェクト)\n * @param {object} [options] オプション集合\n * @param {string} [options.base_resouece] 基底となるリソース\n * @param {any} [options.binary_type] バイナリタイプ\n * @param {any} [options.image_type] イメージタイプ\n * @param {string[]} [options.supported_extensions] ローダーを呼び出す側が対応できる glTF 拡張機能のリスト\n * @return {Promise} 読込み Promise (mapray.gltf.Content)\n */\n static\n load( body, options )\n {\n const context = new Context( body, options );\n return context.load();\n }\n\n}\n\n\nexport default Tool;\n","import Loader from \"./Loader\"\nimport GeoMath from \"./GeoMath\";\nimport Orientation from \"./Orientation\";\nimport CredentialMode from \"./CredentialMode\";\nimport ModelContainer from \"./ModelContainer\";\nimport MarkerLineEntity from \"./MarkerLineEntity\";\nimport PathEntity from \"./PathEntity\";\nimport TextEntity from \"./TextEntity\";\nimport ModelEntity from \"./ModelEntity\";\nimport PolygonEntity from \"./PolygonEntity\";\nimport GltfTool from \"./gltf/Tool\";\nimport Resource, { URLResource, ResourceType } from \"./Resource\";\n\n/**\n * @summary シーンの読み込み\n * @memberof mapray\n */\nclass SceneLoader extends Loader {\n\n /**\n * @desc\n * url で指定したシーンデータの読み込みを開始し、scene にエンティティを構築する。
\n * 読み込みが終了したとき options.callback を呼び出す。
\n * @param {mapray.Scene} scene 読み込み先のシーン\n * @param {string} resource シーンリソース\n * @param {object} [options] オプション集合\n * @param {mapray.Loader.TransformCallback} [options.transform] リソース要求変換関数\n * @param {mapray.Loader.EntityCallback} [options.onEntity] エンティティコールバック\n * @param {mapray.SceneLoader.FinishCallback} [options.callback] 終了コールバック関数\n */\n constructor( scene, resource, options={} )\n {\n if ( resource instanceof Resource ) {\n // OK\n }\n else if ( typeof resource === \"string\" ) {\n resource = new URLResource(resource, {\n type: \"json\",\n transform: options.transform\n });\n }\n else {\n throw new Error( \"Unsupported Resource: \" + resource );\n }\n\n super( scene, resource, {\n onEntity: options.onEntity,\n onLoad: options.callback\n } );\n\n this._glenv = scene.glenv;\n this._references = {};\n this._finished = false;\n }\n\n\n\n /**\n * @summary オブジェクト参照を取得\n * @desc\n * 注意: シーンの読み込みが終了したことを確認してからこのメソッドを呼び出すこと。
\n * @param {string} id 識別子\n * @return {?(mapray.ModelContainer|mapray.Entity)} オブジェクト\n */\n getReference( id )\n {\n var ref = this._references[id];\n return (ref !== undefined) ? ref : null;\n }\n\n\n /**\n * @summary オブジェクト参照を設定\n * @desc\n * オブジェクト item を識別子 id で参照できるように this に設定する。
\n * @param {string} id 識別子\n * @param {mapray.ModelContainer|mapray.Entity} item オブジェクト\n * @private\n */\n _setReference( id, item )\n {\n // 参照を設定\n this._references[id] = item;\n }\n\n\n /**\n * @summary 読み込み処理の実態。継承クラスによって実装される。\n * @private\n */\n _load()\n {\n return (\n this._resource.load( { type: ResourceType.JSON } )\n .then( oscene => {\n // JSON データの取得に成功\n this._check_cancel();\n return this._load_object( oscene );\n } )\n );\n }\n\n\n /**\n * JSON シーンオブジェクトを解析\n * @private\n */\n _load_object( oscene )\n {\n return (\n Promise.resolve()\n .then( () => {\n return this._load_model_register( oscene );\n } )\n .then( () => {\n return this._postload_object( oscene );\n })\n );\n }\n\n\n /**\n * 残りのオブジェクトを読み込む\n * @private\n */\n _postload_object( oscene )\n {\n if ( this.status !== Loader.Status.LOADING ) return;\n this._load_entity_list( oscene );\n }\n\n\n /**\n * @private\n */\n _load_model_register( oscene )\n {\n var model_register = oscene[\"model_register\"];\n if ( !model_register ) return;\n\n var keys = Object.keys( model_register );\n var asyncTasks = [];\n for ( var i = 0; i < keys.length; ++i ) {\n var id = keys[i];\n var model = model_register[id];\n asyncTasks.push( this._load_model_container( oscene, id, model ) );\n }\n return Promise.all( asyncTasks );\n }\n\n\n /**\n * @private\n */\n _load_model_container( oscene, id, model )\n {\n var url = model.link;\n if ( !this._resource.resolveResourceSupported() ) return Promise.reject(new Error(\"Sub Resource is not supported\"));\n const gltf_resource = this._resource.resolveResource( url );\n return (\n gltf_resource.load( { type: ResourceType.JSON } )\n .then( json => {\n // モデルデータの取得に成功\n this._check_cancel();\n // データを解析して gltf.Content を構築\n return GltfTool.load( json, {\n base_resource: gltf_resource,\n binary_type: ResourceType.BINARY,\n image_type: ResourceType.IMAGE,\n supported_extensions: ModelContainer.getSupportedExtensions_glTF()\n } );\n } )\n .then( content => {\n // モデルデータの構築に成功\n var container = new ModelContainer( this._scene, content );\n if ( model.offset_transform ) {\n var matrix = SceneLoader.parseOffsetTransform( model.offset_transform );\n container.setOffsetTransform( matrix );\n }\n this._setReference( id, container );\n } )\n );\n }\n\n\n /**\n * @private\n */\n _load_entity_list( oscene )\n {\n var entity_list = oscene[\"entity_list\"];\n if ( !entity_list ) return;\n\n var scene = this._scene;\n\n for ( var i = 0; i < entity_list.length; ++i ) {\n var item = entity_list[i];\n var type = item.type;\n var entity = null;\n\n switch ( type ) {\n case \"markerline\":\n entity = new MarkerLineEntity( scene, { json: item, refs: this._references } );\n break;\n case \"path\":\n entity = new PathEntity( scene, { json: item, refs: this._references } );\n break;\n case \"text\":\n entity = new TextEntity( scene, { json: item, refs: this._references } );\n break;\n case \"model\":\n entity = new ModelEntity( scene, { json: item, refs: this._references } );\n break;\n case \"polygon\":\n entity = new PolygonEntity( scene, { json: item, refs: this._references } );\n break;\n default:\n console.error( \"mapray: unknown entity type: \" + type );\n break;\n }\n\n if ( entity ) {\n this._onEntity( this, entity, item );\n var id = item.id;\n if ( id ) {\n this._setReference( id, entity );\n }\n }\n }\n }\n\n\n\n /**\n * スキーマ のオブジェクトを解析\n *\n * @param {object} offset_transform オブジェクト\n * @return {mapray.Matrix} オフセット変換行列\n * @package\n */\n static\n parseOffsetTransform( offset_transform )\n {\n var ot = offset_transform;\n\n // \n var translate = ot.translate || [0, 0, 0];\n var orientation = new Orientation( ot.heading, ot.tilt, ot.roll );\n var scale = (ot.scale !== undefined) ? ot.scale : [1, 1, 1]; // \n if ( typeof scale == 'number' ) {\n // スケールをベクトルに正規化\n scale = [scale, scale, scale];\n }\n\n // scale -> orientation -> translate 順の変換\n var matrix = GeoMath.createMatrix();\n\n orientation.getTransformMatrix( scale, matrix );\n matrix[12] = translate[0];\n matrix[13] = translate[1];\n matrix[14] = translate[2];\n\n return matrix;\n }\n\n}\n\n\n/**\n * @summary 終了コールバック\n * @callback FinishCallback\n * @desc\n * シーンの読み込みが終了したときに呼び出される関数の型である。
\n * @param {mapray.SceneLoader} loader 読み込みを実行したローダー\n * @param {boolean} isSuccess 成功したとき true, 失敗したとき false\n * @memberof mapray.SceneLoader\n */\n\n\n\n\nSceneLoader._defaultHeaders = {};\n\n\n\nexport default SceneLoader;\n","/**\n * @summary 追加コンテナの表示制御\n *\n * @class ContainerController\n */\nclass ContainerController\n{\n /**\n * @summary コンストラクタ\n * @param {HTMLElement} container ルートコンテナ(Viewerクラスのcontainer_element)\n * @param {object} options 表示オプション\n * @param {boolean} options.visibility 表示・非表示\n * @param {ContainerPosition} options.position 表示位置\n * @memberof ContainerController\n */\n constructor( container, options )\n {\n this._visibility = ( options && options.visibility ) || true;\n this._position = ( options && options.position ) || ContainerPosition.TOP_LEFT;\n\n var container_element;\n if ( typeof container == \"string\" ) {\n // コンテナを ID 指定したとき\n container_element = document.getElementById( container );\n }\n else {\n // コンテナを直接要素で指定のとき\n container_element = container;\n }\n\n this._viewer_container = container_element;\n this._container = null;\n this._is_compact = false;\n\n var self = this;\n window.addEventListener(\"resize\", function() { self._sizeChanged(); }, false);\n }\n\n /**\n * @summary 表示・非表示の設定\n *\n * @param {boolean} visibility\n * @memberof ContainerController\n */\n setVisibility( visibility )\n {\n this._visibility = visibility;\n\n // 表示状態の更新\n this._setContainerVisibility();\n }\n\n /**\n * @summary 表示位置\n *\n * @param {ContainerPosition} position\n * @memberof ContainerController\n */\n setPosition( position )\n {\n this._position = position;\n\n // コンテナの再作成\n this._deleteContainer();\n this.createContainer();\n }\n\n /**\n * @summary コンテナの表示設定\n *\n * @memberof ContainerController\n */\n _setContainerVisibility()\n {\n if(this._container)\n {\n ( this._visibility ) ? this._container.style.visibility = \"visible\" : this._container.style.visibility = \"collapse\"\n }\n }\n\n /**\n * @summary インスタンスの破棄\n *\n * @memberof ContainerController\n */\n _destroy()\n {\n var self = this;\n window.removeEventListener( \"resize\", function () { self._sizeChanged(); }, false );\n\n this._deleteContainer();\n }\n\n /**\n * @summary 追加コンテナの削除\n *\n * @memberof ContainerController\n */\n _deleteContainer()\n {\n var parent_container = this._container.parentElement;\n parent_container.removeChild( this._container );\n this._container = null;\n }\n\n /**\n * @summary リサイズイベント\n *\n * @memberof ContainerController\n * @abstract\n */\n _sizeChanged()\n {\n\n }\n\n /**\n * @summary 追加コンテナの作成\n *\n * @memberof ContainerController\n * @abstract\n */\n createContainer()\n {\n }\n}\n\n/**\n * @summary ロゴ・著作権表示位置の列挙型\n * @enum {object}\n * @memberof ContainerController\n * @constant\n */\nvar ContainerPosition = {\n /** \n * 左上\n */\n TOP_LEFT: { id: \"top-left\" },\n\n /** \n * 右上 \n */\n TOP_RIGHT: { id: \"top-right\" },\n\n /**\n * 左下\n */\n BOTTOM_LEFT: { id: \"bottom-left\" },\n\n /**\n * 右下\n */\n BOTTOM_RIGHT: { id: \"bottom-right\" }\n};\n\n{\n ContainerController._compact_size = 500;\n\n ContainerController.ContainerPosition = ContainerPosition;\n}\n\nexport default ContainerController;\n","import ContainerController from \"./ContainerController\"\n\n/**\n * @summary ロゴの表示制御\n *\n * @class LogoController\n * @extends {mapray.ContainerController}\n */\nclass LogoController extends ContainerController\n{\n /**\n * @summary コンストラクタ\n * @param {HTMLElement} container ルートコンテナ(Viewerクラスのcontainer_element)\n * @param {object} options 表示オプション\n * @param {boolean} options.visibility 表示・非表示\n * @param {ContainerController.ContainerPosition} options.position 表示位置\n * @memberof LogoController\n */\n constructor( container, options )\n {\n super( container, options );\n \n this._position = ( options && options.position ) || ContainerController.ContainerPosition.BOTTOM_LEFT;\n }\n\n /**\n * @summary リサイズイベント\n *\n * @memberof LogoController\n */\n _sizeChanged()\n {\n if (this._container)\n {\n var sub_container = this._container.children[0];\n var parent_container = this._container.parentElement;\n \n if ( parent_container.parentElement.clientWidth < ContainerController._compact_size)\n {\n sub_container.classList.add( \"mapray-logo-compact\" )\n }\n else\n {\n sub_container.classList.remove( \"mapray-logo-compact\" )\n }\n }\n }\n\n /**\n * @summary 追加コンテナの作成\n *\n * @memberof LogoController\n */\n createContainer()\n {\n var name = \"control-\" + this._position.id;\n var parent_container = this._viewer_container.getElementsByClassName( name )[0];\n\n var main_container = document.createElement( \"div\" );\n main_container.className = \"control\";\n\n var sub_container = document.createElement( \"a\" );\n sub_container.className = \"mapray-logo\";\n sub_container.href = \"https://mapray.com\";\n sub_container.target = \"_blank\";\n\n main_container.appendChild( sub_container );\n this._container = main_container;\n\n parent_container.appendChild( this._container );\n\n this._sizeChanged();\n }\n \n}\n\nexport default LogoController;\n","import ContainerController from \"./ContainerController\"\n\n/**\n * @summary 著作権表示の表示制御\n *\n * @class AttributionController\n * @extends {mapray.ContainerController}\n */\nclass AttributionController extends ContainerController\n{\n /**\n * @summary コンストラクタ\n * @param {HTMLElement} container ルートコンテナ(Viewerクラスのcontainer_element)\n * @param {object} options 表示オプション\n * @param {boolean} options.isVisible 表示・非表示\n * @param {ContainerController.ContainerPosition} options.position 表示位置\n * @param {array} options.attributions 著作権リスト\n * @param {string} options.attributions.display 表示名\n * @param {string} options.attributions.link リンク\n * @memberof AttributionController\n */\n constructor( container, options )\n {\n super( container, options );\n this._position = ( options && options.position ) || ContainerController.ContainerPosition.BOTTOM_RIGHT;\n this._attributions = [];\n if ( options && options.attributions ) {\n this.copyAttributions(options.attributions);\n } else {\n this.copyAttributions(AttributionController._default_attribution);\n }\n }\n\n /**\n * @summary 著作権表示の追加\n *\n * @param {object} attribution 著作権表示オブジェクト\n * @param {string} attribution.display 表示名\n * @param {string} attribution.link リンク\n * @memberof AttributionController\n */\n addAttribution( attribution )\n {\n this._attributions.push( attribution );\n\n // コンテナの再作成\n this._deleteContainer();\n this.createContainer();\n }\n\n /**\n * @summary 著作権表示のリセット\n *\n * @memberof AttributionController\n */\n clearAttribution()\n {\n this._attributions = [];\n\n // コンテナの再作成\n this._deleteContainer();\n this.createContainer();\n }\n\n\n /**\n * @summary リサイズイベント\n *\n * @memberof AttributionController\n */\n _sizeChanged()\n {\n if ( this._container )\n {\n var parent_container = this._container.parentElement;\n\n if ( parent_container.parentElement.clientWidth < ContainerController._compact_size )\n {\n this._container.classList.add( \"mapray-attribution-compact\" )\n }\n else\n {\n this._container.classList.remove( \"mapray-attribution-compact\" )\n }\n }\n }\n\n /**\n * @summary 追加コンテナの作成\n *\n * @memberof AttributionController\n */\n createContainer()\n {\n var name = \"control-\" + this._position.id;\n var parent_container = this._viewer_container.getElementsByClassName( name )[0];\n\n var main_container = document.createElement( \"div\" );\n main_container.classList.add( \"control\" );\n main_container.classList.add( \"mapray-attribution\" );\n\n var sub_container = document.createElement( \"div\" );\n sub_container.classList.add( \"mapray-attribution-container\" );\n\n for (var attribution of this._attributions )\n {\n if ( attribution.display )\n {\n var attribution_container = document.createElement( \"a\" );\n if (attribution.link) {\n attribution_container.href = ( attribution.link );\n attribution_container.target = \"_blank\";\n }\n var text = document.createTextNode( attribution.display )\n attribution_container.appendChild( text );\n\n sub_container.appendChild( attribution_container )\n }\n }\n\n main_container.appendChild(sub_container);\n this._container = main_container;\n\n parent_container.appendChild( this._container );\n\n this._sizeChanged();\n }\n\n copyAttributions( src )\n {\n this._attributions = src.map(d => d);\n }\n}\n\n// クラス変数の定義\n{\n AttributionController._default_attribution = [\n {\n display: \"©Mapray\",\n link: \"https://mapray.com\"\n },\n {\n display: \"©JAXA\",\n link: \"http://www.jaxa.jp/\"\n },\n {\n display: \"測量法に基づく国土地理院長承認(複製)H30JHf626\",\n link: \"https://www.gsi.go.jp/kiban/index.html\"\n }\n];\n}\n\nexport default AttributionController;\n","import Camera from \"./Camera\";\nimport GLEnv from \"./GLEnv\";\nimport RenderStage, { PickStage } from \"./RenderStage\";\nimport StandardImageProvider from \"./StandardImageProvider\";\nimport StandardDemProvider from \"./StandardDemProvider\";\nimport LayerCollection from \"./LayerCollection\";\nimport Globe from \"./Globe\";\nimport PointCloudCollection from \"./PointCloudCollection\";\nimport TileTextureCache from \"./TileTextureCache\";\nimport NullRenderCallback from \"./NullRenderCallback\";\nimport GeoMath from \"./GeoMath\";\nimport Scene from \"./Scene\";\nimport SceneLoader from \"./SceneLoader\";\nimport EasyBindingBlock from \"./animation/EasyBindingBlock\";\n\n// マウス・Attribution開発\nimport LogoController from \"./LogoController\";\nimport AttributionController from \"./AttributionController\";\nimport ContainerController from \"./ContainerController\";\n\n/**\n * @summary 表示管理\n * @classdesc\n * mapray の表示を管理するクラスである。
\n * @memberof mapray\n */\nclass Viewer {\n\n /**\n * @param {string|Element} container コンテナ (ID または要素)\n * @param {object} [options] 生成オプション\n * @param {mapray.DemProvider} [options.dem_provider] DEM プロバイダ\n * @param {mapray.ImageProvider} [options.image_provider] 画像プロバイダ\n * @param {array} [options.layers] 地図レイヤー情報の配列\n * @param {boolean} [options.ground_visibility=true] 地表の可視性\n * @param {boolean} [options.entity_visibility=true] エンティティの可視性\n * @param {mapray.RenderCallback} [options.render_callback] レンダリングコールバック\n * @param {mapray.Viewer.RenderMode} [options.render_mode] レンダリングモード\n * @param {mapray.DebugStats} [options.debug_stats] デバッグ統計オブジェクト\n * @param {mapray.LogoController} [options.logo_controller] ロゴ表示制御オブジェクト\n * @param {mapray.AttributionController} [options.attribution_controller] 著作権表示制御オブジェクト\n */\n constructor( container, options )\n {\n var container_element;\n if ( typeof container == \"string\" ) {\n // コンテナを ID 指定したとき\n container_element = document.getElementById( container );\n }\n else {\n // コンテナを直接要素で指定のとき\n container_element = container;\n }\n\n var canvas = this._createCanvas( container_element );\n\n // インスタンス変数\n this._container_element = container_element;\n this._canvas_element = canvas;\n this._glenv = new GLEnv( canvas );\n this._camera = new Camera( canvas );\n this._animation = this._createAnimationBindingBlock();\n this._dem_provider = this._createDemProvider( options );\n this._image_provider = this._createImageProvider( options );\n this._layers = this._createLayerCollection( options );\n this._globe = new Globe( this._glenv, this._dem_provider );\n this._tile_texture_cache = new TileTextureCache( this._glenv, this._image_provider );\n this._scene = new Scene( this, this._glenv );\n this._ground_visibility = Viewer._getBoolOption( options, \"ground_visibility\", true );\n this._entity_visibility = Viewer._getBoolOption( options, \"entity_visibility\", true );\n this._render_mode = (options && options.render_mode) || RenderMode.SURFACE;\n this._debug_stats = (options && options.debug_stats) || null;\n this._point_cloud_collection = this._createPointCloudCollection( options );\n this._render_callback = this._createRenderCallback( options );\n this._frame_req_id = 0;\n this._previous_time = undefined;\n this._is_destroyed = false;\n this._sun_direction = GeoMath.createVector3( [ 0, 0, 1 ] );\n\n this._postProcesses = [];\n\n // マウス・Attribution開発\n this._logo_controller = ( options && options.logo_controller ) || new LogoController( this._container_element );\n this._attribution_controller = ( options && options.attribution_controller ) || new AttributionController( this._container_element );\n\n // ロゴ・著作権表示用コンテナの作成\n this._createLogoAttributionContainer()\n\n this._logo_controller.createContainer();\n this._attribution_controller.createContainer();\n\n // 最初のフレームの準備\n this._requestNextFrame();\n this._updateCanvasSize();\n }\n\n\n /**\n * @summary インスタンスを破棄\n *\n * @desc\n * 次の順番で処理を行い、インスタンスを破棄する。
\n *\n * \n * - アニメーションフレームを止める。(this.{@link mapray.Viewer#render_callback render_callback} の {@link mapray.RenderCallback#onUpdateFrame onUpdateFrame()} が呼び出されなくなる)
\n * - this.{@link mapray.Viewer#render_callback render_callback} の {@link mapray.RenderCallback#onStop onStop()} を呼び出す。({@link mapray.RenderCallback#onStart onStart()} がすでに呼び出されている場合)
\n * - {@link mapray.RenderCallback} インスタンスを this から切り離す。({@link mapray.RenderCallback#viewer} プロパティは null を返すようになる)
\n * - this.{@link mapray.Viewer#canvas_element canvas_element} を this.{@link mapray.Viewer#container_element container_element} から取り外す。(キャンバスは表示されなくなる)
\n * - データプロバイダのリクエスト、シーンデータのロードの取り消しを試みる。
\n *
\n *\n * このメソッドを呼び出した後は this に直接的または間接的にアクセスすることはできない。ただし {@link mapray.Viewer#destroy destroy()} の呼び出しは除く。
\n *\n * このメソッドは {@link mapray.RenderCallback} のメソッドから呼び出してはならない。
\n */\n destroy()\n {\n if ( this._is_destroyed ) {\n // すでに this は破棄済み\n return;\n }\n\n // フレームを止める\n if ( this._frame_req_id != 0 ) {\n window.maprayCancelAnimationFrame( this._frame_req_id );\n this._frame_req_id = 0;\n }\n\n // RenderCallback の取り外し\n this._render_callback.detach();\n this._render_callback = this._createRenderCallback(); // NullRenderCallback\n\n // キャンバスをコンテナから外す\n this._container_element.removeChild( this._canvas_element );\n\n // DemProvider のリクエストを取り消す\n this._globe.cancel();\n\n // ImageProvider のリクエストを取り消す\n this._tile_texture_cache.cancel();\n\n // 各レイヤーの のリクエストを取り消す\n this._layers.cancel();\n\n // 各 SceneLoader の読み込みを取り消す\n this._scene.cancelLoaders();\n\n // マウス・Attribution開発\n this._logo_controller._destroy();\n this._attribution_controller._destroy();\n this._attribution_controller = null;\n\n // ロゴ・著作権用コンテナの削除\n this._deleteLogoAttributionContainer();\n\n // 破棄確定\n this._is_destroyed = true;\n }\n\n\n /**\n * キャンバス要素を生成\n * @param {Element} container\n * @return {HTMLCanvasElement}\n * @private\n */\n _createCanvas( container )\n {\n var canvas = document.createElement( \"canvas\" );\n canvas.className = \"mapray-canvas\";\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n container.appendChild( canvas );\n return canvas;\n }\n\n\n /**\n * DemProvider を生成\n * @private\n */\n _createDemProvider( options )\n {\n if ( options && options.dem_provider )\n return options.dem_provider;\n else\n return new StandardDemProvider( \"/dem/\", \".bin\" );\n }\n\n\n /**\n * animation.BindingBlock を生成\n * @private\n */\n _createAnimationBindingBlock()\n {\n let abb = new EasyBindingBlock();\n abb.addDescendantUnbinder( () => { this._unbindDescendantAnimations(); } );\n return abb;\n }\n\n\n /**\n * ImageProvider を生成\n * @private\n */\n _createImageProvider( options )\n {\n if ( options && options.image_provider )\n return options.image_provider;\n else\n return new StandardImageProvider( \"http://cyberjapandata.gsi.go.jp/xyz/std/\", \".png\", 256, 0, 18 );\n }\n\n\n /**\n * LayerCollection を生成\n * @private\n */\n _createLayerCollection( options )\n {\n var layers = (options && options.layers) ? options.layers : {};\n return new LayerCollection( this, layers );\n }\n\n\n /**\n * PointCloudCollection を生成\n * @private\n */\n _createPointCloudCollection( options )\n {\n const point_cloud_providers = (options && options.point_cloud_providers) ? options.point_cloud_providers : {};\n return new PointCloudCollection( this._scene, point_cloud_providers );\n }\n\n /**\n * RenderCallback を生成\n * @private\n */\n _createRenderCallback( options )\n {\n var callback;\n if ( options && options.render_callback )\n callback = options.render_callback;\n else\n callback = new NullRenderCallback();\n\n callback.attach( this );\n\n return callback;\n }\n\n /**\n * @summary ロゴ・著作権表示用コンテナの作成\n *\n * @memberof Viewer\n */\n _createLogoAttributionContainer()\n {\n for ( var position of Viewer._positions )\n {\n var container = document.createElement( \"div\" );\n container.className = position\n this._container_element.appendChild( container );\n }\n }\n\n /**\n * @summary ロゴ・著作権表示用コンテナの削除\n *\n * @memberof Viewer\n */\n _deleteLogoAttributionContainer()\n {\n for ( var position of Viewer._positions )\n {\n var container = document.getElementById( position );\n\n if ( container ) { this._container_element.removeChild( position ); }\n }\n }\n\n /**\n * ブール値のオプションを取得\n * @private\n */\n static\n _getBoolOption( options, name, defaultValue )\n {\n return (options && (options[name] !== undefined)) ? options[name] : defaultValue;\n }\n\n\n /**\n * @summary コンテナ要素 (キャンバス要素を保有する)\n * @type {Element}\n * @readonly\n */\n get container_element() { return this._container_element; }\n\n\n /**\n * @summary キャンバス要素\n * @type {Element}\n * @readonly\n */\n get canvas_element() { return this._canvas_element; }\n\n\n /**\n * @summary アニメーションパラメータ設定\n * @type {mapray.animation.BindingBlock}\n * @readonly\n */\n get animation() { return this._animation; }\n\n\n /**\n * DEM データプロバイダ\n * @type {mapray.DemProvider}\n * @readonly\n */\n get dem_provider() { return this._dem_provider; }\n\n\n /**\n * @summary 画像プロバイダ\n * @type {mapray.ImageProvider}\n * @readonly\n */\n get image_provider() { return this._image_provider; }\n\n\n /**\n * @summary 地図レイヤー管理\n * @type {mapray.LayerCollection}\n * @readonly\n */\n get layers() { return this._layers; }\n\n\n /**\n * @summary 点群管理\n * @type {mapray.PointCloudCollection}\n * @readonly\n */\n get point_cloud_collection() { return this._point_cloud_collection; }\n\n\n /**\n * @summary レンダリングコールバック\n * @type {mapray.RenderCallback}\n * @readonly\n */\n get render_callback() { return this._render_callback; }\n\n\n /**\n * @summary レンダリングモード\n * @type {mapray.RenderMode}\n * @readonly\n */\n get render_mode() { return this._render_mode; }\n\n\n /**\n * @summary レンダリングモードを設定\n * @type {mapray.RenderMode}\n */\n set render_mode( val ) { this._render_mode = val; }\n\n\n /**\n * @summary デバッグ統計オブジェクト\n * @type {?mapray.DebugStats}\n * @readonly\n */\n get debug_stats() { return this._debug_stats; }\n\n\n /**\n * @summary カメラ\n * @type {mapray.Camera}\n * @readonly\n */\n get camera() { return this._camera; }\n\n\n /**\n * @summary モデルシーン\n * @type {mapray.Scene}\n * @readonly\n */\n get scene() { return this._scene; }\n\n\n /**\n * 内部的に実装で使用される WebGL レンダリングコンテキスト情報\n * @type {mapray.GLEnv}\n * @readonly\n * @package\n */\n get glenv() { return this._glenv; }\n\n\n /**\n * @type {mapray.Globe}\n * @readonly\n * @package\n */\n get globe() { return this._globe; }\n\n\n /**\n * 内部的に実装で使用される地図画像タイル管理\n * @type {mapray.TileTextureCache}\n * @readonly\n * @package\n */\n get tile_texture_cache() { return this._tile_texture_cache; }\n\n /**\n *\n * @type {mapray.LogoController}\n * @readonly\n * @memberof Viewer\n */\n get logo_controller() { return this._logo_controller; }\n\n /**\n *\n * @type {mapray.AttributionController}\n * @readonly\n * @memberof Viewer\n */\n get attribution_controller() { return this._attribution_controller; }\n\n\n /**\n * @summary 太陽ベクトル。非公開とする。APIでは、メモリー破壊が起こらない Viewer#getSunDirection を公開する。\n * @type {Vector3}\n * @private\n * @readonly\n * @memberof Viewer\n */\n get sun_direction() { return this._sun_direction; }\n\n\n /**\n * @summary 可視性を設定\n * @desc\n * target に属するオブジェクトを表示するかどうかを指定する。
\n * 可視性は Viewer の構築子の ground_visibility と entity_visibility オプションでも指定することができる。
\n *\n * @param {mapray.Viewer.Category} target 表示対象\n * @param {boolean} visibility 表示するとき true, 表示しないとき false\n *\n * @see {@link mapray.Viewer#getVisibility}\n */\n setVisibility( target, visibility )\n {\n switch ( target ) {\n case Category.GROUND:\n this._ground_visibility = visibility;\n break;\n case Category.ENTITY:\n this._entity_visibility = visibility;\n break;\n default:\n throw new Error( \"invalid target: \" + target );\n }\n }\n\n\n /**\n * @summary 可視性を取得\n * @desc\n * target に属するオブジェクトを表示するかどうかを取得する。
\n *\n * @param {mapray.Viewer.Category} target 表示対象\n * @return {boolean} 表示するとき true, 表示しないとき false\n *\n * @see {@link mapray.Viewer#setVisibility}\n */\n getVisibility( target, visibility )\n {\n switch ( target ) {\n case Category.GROUND:\n return this._ground_visibility;\n case Category.ENTITY:\n return this._entity_visibility;\n default:\n throw new Error( \"invalid target: \" + target );\n }\n }\n\n\n /**\n * @summary 指定位置の標高を取得\n * @desc\n * 緯度 lat, 経度 lon が示す場所の標高を返す。
\n * 現在メモリに存在する DEM データの中で最も正確度が高いデータから標高を計算する。
\n * さらに正確度が高い DEM データがサーバーに存在すれば、それを非同期に読み込む。そのため時間を置いてこのメソッドを呼び出すと、さらに正確な値が取得できることがある。
\n * @param {number} lat 緯度 (Degrees)\n * @param {number} lon 経度 (Degrees)\n * @return {number} 標高 (Meters)\n */\n getElevation( lat, lon )\n {\n // 正規化緯経度 (Degrees)\n var _lon = lon + 180 * Math.floor( (90 - lat) / 360 + Math.floor( (90 + lat) / 360 ) );\n var nlat = 90 - Math.abs( 90 - lat + 360 * Math.floor( (90 + lat) / 360 ) ); // 正規化緯度 [-90,90]\n var nlon = _lon - 360 - 360 * Math.floor( (_lon - 180) / 360 ); // 正規化緯度 [-180,180)\n\n // 単位球メルカトル座標\n var xm = nlon * GeoMath.DEGREE;\n var ym = GeoMath.invGudermannian( nlat * GeoMath.DEGREE );\n\n // 基底タイル座標 (左上(0, 0)、右下(1, 1))\n var dPI = 2 * Math.PI;\n var xt = xm / dPI + 0.5;\n var yt = 0.5 - ym / dPI;\n\n if ( yt < 0 || yt > 1 ) {\n // 緯度が Web メルカトルの範囲外 (極に近い)\n return 0;\n }\n\n // 正確度が最も高い DEM タイルの取得\n var globe = this._globe;\n var dem = globe.findHighestAccuracy( xt, yt );\n if ( dem === null ) {\n // まだ標高を取得することができない\n return 0;\n }\n\n // 標高をサンプル\n var ρ = globe.dem_provider.getResolutionPower();\n var size = 1 << ρ; // 2^ρ\n var pow = Math.pow( 2, dem.z ); // 2^ze\n var uf = size * (pow * xt - dem.x);\n var vf = size * (pow * yt - dem.y);\n var ui = GeoMath.clamp( Math.floor( uf ), 0, size - 1 );\n var vi = GeoMath.clamp( Math.floor( vf ), 0, size - 1 );\n\n var heights = dem.getHeights( ui, vi );\n var h00 = heights[0];\n var h10 = heights[1];\n var h01 = heights[2];\n var h11 = heights[3];\n\n // 標高を補間\n var s = uf - ui;\n var t = vf - vi;\n return (h00 * (1 - s) + h10 * s) * (1 - t) + (h01 * (1 - s) + h11 * s) * t;\n }\n\n\n /**\n * @summary 現行の標高を取得\n *\n * @desc\n * 現在メモリーにある最高精度の標高値を取得する。
\n * まだ DEM データが存在しない、または経度, 緯度が範囲外の場所は標高を 0 とする。
\n *\n * このメソッドは DEM のリクエストは発生しない。また DEM のキャッシュには影響を与えない。
\n *\n * 一般的に画面に表示されていない場所は標高の精度が低い。
\n *\n * @param {mapray.GeoPoint} position 位置 (高度は無視される)\n * @return {number} 標高\n *\n * @see mapray.Viewer#getExistingElevations\n */\n getExistingElevation( position )\n {\n const array = [position.longitude, position.latitude, 0];\n\n this._globe.getExistingElevations( 1, array, 0, 3, array, 2, 3 );\n\n return array[2];\n }\n\n\n /**\n * @summary 現行の標高 (複数) を取得\n *\n * @desc\n * 現在メモリーにある最高精度の標高値を一括で取得する。
\n * まだ DEM データが存在しない、または経度, 緯度が範囲外の場所は標高を 0 とする。
\n *\n * このメソッドは DEM のリクエストは発生しない。また DEM のキャッシュには影響を与えない。
\n *\n * 一般的に画面に表示されていない場所は標高の精度が低い。
\n *\n * @param {number} num_points 入出力データ数\n * @param {number[]} src_array 入力配列 (経度, 緯度, ...)\n * @param {number} src_offset 入力データの先頭インデックス\n * @param {number} src_stride 入力データのストライド\n * @param {number[]} dst_array 出力配列 (標高, ...)\n * @param {number} dst_offset 出力データの先頭インデックス\n * @param {number} dst_stride 出力データのストライド\n * @return {number[]} dst_array\n *\n * @see mapray.Viewer#getExistingElevation\n */\n getExistingElevations( num_points, src_array, src_offset, src_stride, dst_array, dst_offset, dst_stride )\n {\n return this._globe.getExistingElevations( num_points, src_array, src_offset, src_stride, dst_array, dst_offset, dst_stride );\n }\n\n\n /**\n * @summary レイと地表の交点を取得\n * @desc\n * ray と地表の最も近い交点を取得する。ただし交点が存在しない場合は null を返す。
\n * @param {mapray.Ray} ray レイ (GOCS)\n * @return {?mapray.Vector3} 交点または null\n */\n getRayIntersection( ray )\n {\n var globe = this._globe;\n\n if ( globe.status !== Globe.Status.READY ) {\n // Globe の準備ができていない\n return null;\n }\n\n var distance = globe.root_flake.findRayDistance( ray, Number.MAX_VALUE );\n if ( distance === Number.MAX_VALUE ) {\n // 交点が見つからなかった\n return null;\n }\n\n // P = Q + distance V\n var p = GeoMath.createVector3();\n var q = ray.position;\n var v = ray.direction;\n\n p[0] = q[0] + distance * v[0];\n p[1] = q[1] + distance * v[1];\n p[2] = q[2] + distance * v[2];\n\n return p;\n }\n\n\n /**\n * @summary Canvas画面のキャプチャ\n *\n * @param {object} options オプション\n * @return {blob} データ\n */\n async capture( options = { type: 'jpeg' } )\n {\n if ( !this._canvas_element ) {\n throw new Error('Canvas is null.');\n }\n\n const mimeType = options.type === 'png' ? 'image/png' : 'image/jpeg';\n\n return await new Promise( resolve => {\n this._postProcesses.push( () => {\n this._canvas_element.toBlob( resolve, mimeType );\n return false;\n });\n });\n }\n\n\n\n /**\n * 次のフレーム更新を要求する。\n * @private\n */\n _requestNextFrame()\n {\n this._frame_req_id = window.maprayRequestAnimationFrame( () => this._updateFrame() );\n }\n\n\n /**\n * フレーム更新のときに呼び出される。\n * @private\n * @see mapray.RenderStage\n */\n _updateFrame()\n {\n var delta_time = this._updateTime();\n this._requestNextFrame();\n\n this._updateCanvasSize();\n\n this._render_callback.onUpdateFrameInner( delta_time );\n\n if ( this._debug_stats !== null ) {\n this._debug_stats.clearStats();\n }\n\n var stage = new RenderStage( this );\n stage.render();\n\n this._postProcess();\n\n this._finishDebugStats();\n }\n\n\n /**\n * 現在のビューにおいて指定されたスクリーン位置の情報を取得します\n * @param {Vector2} screen_position スクリーン位置(キャンバス左上を原点としたピクセル座標)\n * @return {mapray.Viewer.PickResult} ピック結果\n */\n pick(screen_position) {\n const stage = new PickStage( this, screen_position );\n stage.render();\n return stage.pick_result;\n }\n\n\n /**\n * @summary 時間の更新\n * @return {number} 前フレームからの経過時間 (秒)\n * @private\n */\n _updateTime()\n {\n var now_time = window.maprayNow();\n var delta_time = (this._previous_time !== undefined) ? (now_time - this._previous_time) / 1000 : 0;\n this._previous_time = now_time;\n\n return delta_time;\n }\n\n\n /**\n * @summary Canvas サイズを更新\n * @private\n */\n _updateCanvasSize()\n {\n var canvas = this._canvas_element;\n\n // 要素のサイズとキャンバスのサイズを一致させる\n if ( canvas.width != canvas.clientWidth ) {\n canvas.width = canvas.clientWidth;\n }\n if ( canvas.height != canvas.clientHeight ) {\n canvas.height = canvas.clientHeight;\n }\n }\n\n\n /**\n * @summary ポストプロセスを実行\n * @private\n */\n _postProcess()\n {\n if ( this._postProcesses.length === 0 ) {\n return;\n }\n const nextProcesses = [];\n this._postProcesses.forEach( item => {\n if ( item() ) {\n nextProcesses.push( item );\n }\n });\n this._postProcesses = nextProcesses;\n }\n\n\n /**\n * @summary デバッグ統計の最終処理\n * @private\n */\n _finishDebugStats()\n {\n var stats = this._debug_stats;\n if ( stats === null ) {\n // 統計オブジェクトは指定されていない\n return;\n }\n\n // 統計値の取得\n stats.num_wait_reqs_dem = this._globe.getNumDemWaitingRequests();\n stats.num_wait_reqs_img = this._tile_texture_cache.getNumWaitingRequests();\n\n // 統計の更新を通知\n stats.onUpdate();\n }\n\n\n /**\n * EasyBindingBlock.DescendantUnbinder 処理\n *\n * @private\n */\n _unbindDescendantAnimations()\n {\n this._scene.animation.unbindAllRecursively();\n }\n\n\n /**\n * 太陽ベクトルの情報を設定します\n * @param {Vector3} direction 方向(GOCS 正規化されていること)\n */\n setSunDirection( direction )\n {\n GeoMath.copyVector3( direction, this._sun_direction );\n }\n\n\n /**\n * 太陽ベクトルの情報のコピーを取得します\n * @param {Vector3} dst 方向(GOCS 正規化されていること)\n * @return {Vector3} ベクトルのコピー(GOCS)\n */\n getSunDirection( dst )\n {\n return GeoMath.copyVector3( this._sun_direction, dst );\n }\n}\n\n\n/**\n * @summary ピック結果\n * @typedef {object} PickResult\n * @desc\n * 関数型 {@link mapray.Viewer.pick} の戻り値のオブジェクト構造である。
\n * @property {mapray.Vector3} [point] ピックした3次元位置\n * @property {mapray.Entity} [entity|undefined] ピックしたエンティティ(ピック位置にエンティティがない場合はundefined)\n * @memberof mapray.Viewer\n */\n\n\n/**\n * @summary 表示対象の列挙型\n * @desc\n * {@link mapray.Viewer#setVisibility} と {@link mapray.Viewer#getVisibility} メソッドの target 引数に指定する値の型である。
\n * @enum {object}\n * @memberof mapray.Viewer\n * @constant\n */\nvar Category = {\n\n /**\n * 地表 (レイヤーも含む)\n */\n GROUND: { id: \"GROUND\" },\n\n\n /**\n * エンティティ\n */\n ENTITY: { id: \"ENTITY\" }\n\n};\n\n\n/**\n * @summary レンダリングモードの列挙型\n * @desc\n * {@link mapray.Viewer} の構築子の options.render_mode パラメータ、または {@link mapray.Viewer#render_mode} プロパティに指定する値の型である。\n * @enum {object}\n * @memberof mapray.Viewer\n * @constant\n */\nvar RenderMode = {\n\n /**\n * ポリゴン面 (既定値)\n */\n SURFACE: { id: \"SURFACE\" },\n\n\n /**\n * ワイヤーフレーム\n */\n WIREFRAME: { id: \"WIREFRAME\" }\n\n};\n\n// クラス定数の定義\n{\n Viewer.Category = Category;\n Viewer.RenderMode = RenderMode;\n\n // マウス・Attribution開発\n Viewer.ContainerPosition = ContainerController.ContainerPosition;\n\n // ロゴ・著作権表示用コンテナ名称\n Viewer._positions = [\"control-top-left\", \"control-top-right\", \"control-bottom-left\", \"control-bottom-right\"];\n}\n\n\nexport default Viewer;\n","import DemProvider from \"./DemProvider\";\n\n\n/**\n * @summary クラウド DEM プロバイダ\n * @memberof mapray\n * @extends mapray.DemProvider\n */\nclass CloudDemProvider extends DemProvider {\n\n /**\n * @param {string} api_key API キーの文字列\n */\n constructor( api_key )\n {\n super();\n\n this._headers = {\n 'X-Api-Key': api_key\n };\n }\n\n\n /**\n * @override\n */\n requestTile( z, x, y, callback )\n {\n var actrl = new AbortController();\n\n fetch( this._makeURL( z, x, y ), { headers: this._headers,\n signal: actrl.signal } )\n .then( response => {\n return response.ok ?\n response.arrayBuffer() : Promise.reject( Error( response.statusText ) );\n } )\n .then( buffer => {\n // データ取得に成功\n callback( buffer );\n } )\n .catch( () => {\n // データ取得に失敗または取り消し\n callback( null );\n } );\n\n return actrl;\n }\n\n\n /**\n * @override\n */\n cancelRequest( id )\n {\n var actrl = id; // 要求 ID を AbortController に変換\n actrl.abort(); // 取り消したので要求を中止\n }\n\n\n /**\n * URL を作成\n * @private\n */\n _makeURL( z, x, y )\n {\n return 'https://tiles.mapray.com/dem/' + z + '/' + x + '/' + y + '.bin';\n }\n\n}\n\n\nexport default CloudDemProvider;\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport pin_vs_code from \"./shader/pin.vert\";\nimport pin_fs_code from \"./shader/pin.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary テキストマテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n * @see mapray.PinEntity\n */\nclass PinMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, options = {} )\n {\n super( glenv, pin_vs_code, options.ridMaterial ? rid_fs_code : pin_fs_code );\n\n // 不変パラメータを事前設定\n this.bindProgram();\n this.setInteger( \"u_image\", PinMaterial.TEXUNIT_IMAGE );\n this.setInteger( \"u_image_mask\", PinMaterial.TEXUNIT_IMAGE_MASK );\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n return false;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // mat4 u_obj_to_clip\n this.setObjToClip( stage, primitive );\n\n // 画面パラメータ: {2/w, 2/h}\n // vec2 u_sparam\n var sparam = PinMaterial._sparam;\n sparam[0] = 2 / stage._width;\n sparam[1] = 2 / stage._height;\n this.setVector2( \"u_sparam\", sparam );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // テクスチャのバインド\n // sampler2D u_image\n var image = props[\"image\"];\n this.bindTexture2D( PinMaterial.TEXUNIT_IMAGE, image.handle );\n\n // テクスチャマスクのバインド\n // sampler2D u_image_mask\n var image_mask = props[\"image_mask\"];\n this.bindTexture2D( PinMaterial.TEXUNIT_IMAGE_MASK, image_mask.handle );\n }\n }\n\n}\n\n\n// クラス定数の定義\n{\n PinMaterial.TEXUNIT_IMAGE = 0; // 画像のテクスチャユニット\n PinMaterial.TEXUNIT_IMAGE_MASK = 1; // 画像のテクスチャユニット\n\n // 計算用一時領域\n PinMaterial._sparam = GeoMath.createVector2f();\n PinMaterial._bg_color = GeoMath.createVector3f();\n PinMaterial._fg_color = GeoMath.createVector3f();\n}\n\n\nexport default PinMaterial;\n","import Dom from \"./util/Dom\";\nimport Resource, { ResourceType } from \"./Resource\";\n\n\n/**\n * @classdesc\n * アイコン画像のローダーです。\n * 何らかのプロパティに応じて、アイコンが読み込まれます。\n * 同一リソースが要求された場合は、読み込み中または読み込み済みのアイコンを返却します。\n * 同一リソースであるかの判定には、getKey(prop)関数により返却される値を用います。\n * @private\n */\nclass IconLoader {\n\n /**\n * @summary コンストラクタ\n */\n constructor() {\n this._cache = new Map();\n }\n\n\n /**\n * @summary プロパティに応じたアイコンを返却します。\n * すでに同一リソースを生成した場合は生成済みのインスタンスを返却し、そうでない場合はdoCreate(prop)により生成します。\n * @param {any} prop\n * @return {IconLoaderItem}\n */\n create( prop ) {\n const key = this.getKey( prop );\n let value = this._cache.get(key);\n if ( !value ) this._cache.set( key, value = this.doCreate( prop ) );\n return value;\n }\n\n\n /**\n * @summary プロパティに応じたアイコンを生成します。\n * @abstract\n * @param {any} prop プロパティ\n * @return {IconLoaderItem}\n */\n doCreate( prop ) {\n }\n\n\n /**\n * @summary プロパティに応じたキーを返却します。\n * 必要に応じてオーバーライドされることを想定した関数です。\n * ディフォルトでは、プロパティ自体がキーとなるように動作します。\n * @param {any} prop プロパティ\n */\n getKey( prop ) {\n return prop;\n }\n\n\n /**\n * @summary プロパティに応じたアイコンの読み込みを開始し、インスタンスを返却します。\n * 読み込みは開始しますが読み込み完了していない可能性があります。\n * この関数はasync関数ではありません。読み込み終了を監視するには、この関数の返却値に対してonEnd(callback)を呼び出します。\n * @param {any} prop プロパティ\n * @return {IconLoaderItem}\n */\n load( prop ) {\n const icon = this.create( prop );\n icon.load();\n return icon;\n }\n\n}\n\n\n\n/**\n * @classdesc アイコン画像ローダーのアイコンです。抽象クラスです。\n * ステータスの管理、読み込み完了後の通知等を行います。\n * @private\n */\nclass IconLoaderItem {\n\n constructor() {\n this._status = IconLoaderItem.Status.NOT_LOADED;\n this._funcs = [];\n this._icon = null;\n }\n\n\n /**\n * @summary アイコンの状態\n * @type {IconLoaderItem.Status}\n * @readonly\n */\n get status() {\n return this._status;\n }\n\n\n /**\n * @summary アイコンの読み込みが完了しているか\n * @return {boolean}\n */\n isLoaded() {\n return this._status === IconLoaderItem.Status.LOADED;\n }\n\n\n /**\n * @summary アイコンの読み込みが完了した時点で呼び出されるコールバック関数を登録します。\n * この関数を呼び出した時点で読み込みが完了している場合は、即座にコールバック関数を実行します。\n * @param {function}\n */\n onEnd( func ) {\n const alreadyDone = this._status === IconLoaderItem.Status.LOADED || this._status === IconLoaderItem.Status.ABORTED;\n if ( alreadyDone ) func( this );\n else this._funcs.push( func );\n }\n\n\n /**\n * @summary アイコン読み込み関数(doLoad())を実行し、成功時、失敗時それぞれ後続処理を行います。\n */\n async load() {\n if ( this._status === IconLoaderItem.Status.NOT_LOADED ) {\n this._status = IconLoaderItem.Status.LOADING;\n try {\n this._icon = await this.doLoad();\n this._status = IconLoaderItem.Status.LOADED;\n }\n catch( error ) {\n this._status = IconLoaderItem.Status.ABORTED;\n }\n for ( var i = 0; i < this._funcs.length; i++ ) {\n this._funcs[ i ]( this );\n }\n this._funcs.length = 0;\n }\n }\n\n\n /**\n * @summary アイコンを読み込みます。この関数はオーバーライドされることを想定されています。\n * @abstract\n * @return {Image}\n */\n async doLoad() {\n throw new Error( \"doLoad() is not implemented in: \" + this.constructor.name );\n }\n\n\n /**\n * @summary アイコンを取得します。\n * アイコンが読み込まれるまではnullを返却します。\n * @return {Image|null}\n */\n get icon() {\n return this._icon;\n }\n\n /**\n * @summary アイコンの幅\n * アイコンが読み込まれるまでは-1を返却します。\n * @return {number}\n */\n get width() {\n return this._icon ? this.icon.width : -1;\n }\n\n /**\n * @summary アイコンの高さ\n * アイコンが読み込まれるまでは-1を返却します。\n * @return {number}\n */\n get height() {\n return this._icon ? this.icon.height : -1;\n }\n\n\n /**\n * @summary アイコンをキャンバスコンテキストに描画します。\n * @param {CanvasRenderingContext2D} context\n * @param {number} x\n * @param {number} y\n * @param {number} width\n * @param {number} height\n */\n draw( context, x, y, width, height ) {\n context.drawImage( this.icon, x, y, width, height );\n }\n\n}\n\n/**\n * @private\n */\nIconLoaderItem.Status = {\n NOT_LOADED: \"not loaded\",\n LOADING: \"loading\",\n LOADED: \"loaded\",\n ABORTED: \"aborted\"\n};\n\n\n\n/**\n * @classdesc アイコン画像のURLを指定してアイコンを読み込むアイコンローダーです。\n * urlは下記のように生成します。\n * url = urlPrefix + id + urlSuffix\n * @private\n */\nclass URLTemplateIconLoader extends IconLoader {\n\n /**\n * @param {string} urlPrefix\n * @param {string} urlSuffix\n */\n constructor( urlPrefix, urlSuffix ) {\n super();\n this.urlPrefix = urlPrefix;\n this.urlSuffix = urlSuffix;\n }\n\n\n /**\n * @override\n */\n doCreate( id ) {\n return new URLIconLoaderItem( this.urlPrefix + id + this.urlSuffix );\n }\n\n}\n\n\n\n/**\n * @classdesc URLTemplateIconLoaderのアイコンです。\n *\n * @private\n */\nclass URLIconLoaderItem extends IconLoaderItem {\n\n /**\n * @param {string} url\n */\n constructor( url ) {\n super();\n this.url = url;\n }\n\n\n /**\n * @override\n */\n async doLoad() {\n return await Dom.loadImage( this.url, { crossOrigin: \"Anonymous\" } );\n }\n\n}\n\n\n\n/**\n * @classdesc テキストアイコンを生成するアイコンローダーです。\n *\n * @private\n */\nclass TextIconLoader extends IconLoader {\n\n /**\n * プロパティに応じたアイコンを生成します。\n * @param {string} prop.text プロパティ\n */\n doCreate( prop ) {\n return new TextIconLoaderItem( prop.text, prop.props );\n }\n\n\n /**\n * プロパティに応じたキーを返却します。\n * @param {string} prop.text プロパティ\n */\n getKey( prop ) {\n return prop.text;\n }\n\n}\n\n\n\n/**\n * @classdesc TextIconLoaderのアイコンです。\n *\n * @private\n */\nclass TextIconLoaderItem extends IconLoaderItem {\n\n /**\n * @param {string} text text\n * @param {mapray.Vector2} [props.size] size in pixel\n * @param {string} [props.font_family] font family\n */\n constructor( text, props = {} ) {\n super();\n this.text = text;\n this.props = props;\n }\n\n\n /**\n * @override\n */\n async doLoad() {\n var props = this.props;\n var size = props.size ? props.size[0] : 20;\n var fontFamily = props.font_family ? (\"'\" + props.font_family + \"'\") : Dom.SYSTEM_FONT_FAMILY;\n var context = Dom.createCanvasContext( size, size );\n context.textAlign = \"center\";\n context.textBaseline = \"alphabetic\";\n context.font = (size * 0.6756756757) + \"px \" + fontFamily;\n context.fillText( this.text, size * 0.5, size * 0.7432432432 );\n return context.canvas;\n }\n\n\n /**\n * @override\n */\n draw( context, x, y, width, height ) {\n context.drawImage( this.icon, x, y, width, height );\n }\n\n}\n\n\n\n/**\n * @classdesc 画像からアイコンを生成するアイコンローダーです。\n * \n * @private\n */\nclass ImageIconLoader extends IconLoader {\n\n /**\n * プロパティに応じたアイコンを生成します。\n * @param {string} prop.text プロパティ\n */\n doCreate( image_src ) {\n return new ImageIconLoaderItem( image_src );\n }\n\n}\n\n\n\n/**\n * @classdesc ImageIconLoaderのアイコンです。\n * \n * @private\n */\nclass ImageIconLoaderItem extends IconLoaderItem {\n\n /**\n * @param {string|HTMLImageElement|HTMLCanvasElement} image_src image source\n */\n constructor( image_src ) {\n super();\n this._image_src = image_src;\n }\n\n\n /**\n * @override\n */\n async doLoad() {\n const image_src = this._image_src;\n const image = (\n image_src instanceof Resource ? await image_src.load( { type: ResourceType.IMAGE } ):\n typeof( image_src ) === \"string\" ? await Dom.loadImage( image_src ):\n image_src instanceof HTMLImageElement ? await Dom.waitForLoad( image_src ):\n image_src instanceof HTMLCanvasElement ? image_src:\n null\n );\n if ( !image ) throw new Error( \"not supported: \" + image_src );\n return image;\n }\n\n}\n\n\n\nexport { URLTemplateIconLoader, TextIconLoader, ImageIconLoader };\nexport default IconLoader;\n","import Entity from \"./Entity\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport Texture from \"./Texture\";\nimport PinMaterial from \"./PinMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport { RenderTarget } from \"./RenderStage\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport IconLoader, { URLTemplateIconLoader, TextIconLoader } from \"./IconLoader\";\nimport Dom from \"./util/Dom\";\nimport EasyBindingBlock from \"./animation/EasyBindingBlock\";\nimport Type from \"./animation/Type\";\nimport AnimUtil from \"./animation/AnimUtil\";\nimport AbstractPointEntity from \"./AbstractPointEntity\";\n\n/**\n * @summary ピンエンティティ\n * @memberof mapray\n * @extends mapray.Entity\n *\n * @example\n * var pin = new mapray.PinEntity(viewer.scene);\n * pin.addTextPin( \"32\", new mapray.GeoPoint(139.768, 35.635) );\n * pin.addTextPin( \"A\", new mapray.GeoPoint(139.768, 35.636), { fg_color: [0.0, 0.0, 1.0], bg_color: [1.0, 0.0, 0.0] } );\n * pin.addTextPin( \"始\", new mapray.GeoPoint(139.768, 35.637), { size: 50 } );\n * pin.addTextPin( \"終\", new mapray.GeoPoint(139.768, 35.639), { size: 50, font_family: \"Georgia\" } );\n * pin.addPin( new mapray.GeoPoint(139.766, 35.6361) );\n * pin.addMakiIconPin( \"ferry-15\", new mapray.GeoPoint(139.764, 35.6361), { size: 150, fg_color: [0.2, 0.2, 0.2], bg_color: [1.0, 1.0, 1.0] } );\n * pin.addMakiIconPin( \"car-15\", new mapray.GeoPoint(139.762, 35.6361), { size: 60, fg_color: [1.0, 1.0, 1.0], bg_color: [0.2, 0.2, 0.2] } );\n * pin.addMakiIconPin( \"bus-15\", new mapray.GeoPoint(139.760, 35.6361), { size: 40, fg_color: [1.0, 0.3, 0.1], bg_color: [0.1, 0.3, 1.0] } );\n * pin.addMakiIconPin( \"bus-15\", new mapray.GeoPoint(139.759, 35.6361) );\n * pin.addMakiIconPin( \"car-15\", new mapray.GeoPoint(139.758, 35.6361) );\n * viewer.scene.addEntity(pin);\n *\n */\nclass PinEntity extends AbstractPointEntity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n // 親プロパティ\n this._parent_props = {\n fg_color: null,\n bg_color: null,\n size: null,\n font_family: null,\n };\n\n // Entity.PrimitiveProducer インスタンス\n this._primitive_producer = new PrimitiveProducer( this );\n\n this._animation.addDescendantUnbinder( () => { this._unbindDescendantAnimations(); } );\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return this._primitive_producer;\n }\n\n\n /**\n * @override\n */\n onChangeAltitudeMode( prev_mode )\n {\n this._primitive_producer.onChangeAltitudeMode();\n }\n\n\n /**\n * EasyBindingBlock.DescendantUnbinder 処理\n *\n * @private\n */\n _unbindDescendantAnimations()\n {\n // すべてのエントリーを解除\n for ( let entry of this._entries ) {\n entry.animation.unbindAllRecursively();\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector2 = Type.find( \"vector2\" );\n const vector3 = Type.find( \"vector3\" );\n\n // パラメータ名: fg_color\n // パラメータ型: vector3\n // アイコンの色\n block.addEntry( \"fg_color\", [vector3], null, value => {\n this.setFGColor( value );\n } );\n \n // パラメータ名: bg_color\n // パラメータ型: vector3\n // アイコン背景の色\n block.addEntry( \"bg_color\", [vector3], null, value => {\n this.setBGColor( value );\n } );\n\n // パラメータ名: size\n // パラメータ型: vector2 | number\n // 型が vector2 のとき アイコンのピクセルサイズX, Y 順であると解釈\n // 型が number のとき アイコンのピクセルサイズX, Y は同値\n const size_temp = GeoMath.createVector2();\n let size_type;\n\n let size_tsolver = curve => {\n size_type = AnimUtil.findFirstTypeSupported( curve, [vector2, number] );\n return size_type;\n };\n\n block.addEntry( \"size\", [vector2, number], size_tsolver, value => {\n if ( size_type === vector2 ) {\n this.setSize( value );\n }\n else { // size_type === number\n size_temp[0] = value;\n size_temp[1] = value;\n this.setSize( size_temp );\n }\n } );\n }\n\n\n /**\n * @summary アイコンのピクセルサイズを指定\n * @param {mapray.Vector3} size アイコンのピクセルサイズ\n */\n setSize( size )\n {\n this._setVector2Property( \"size\", size );\n }\n\n\n /**\n * @summary アイコンの色を設定\n * @param {mapray.Vector3} color アイコンの色\n */\n setFGColor( color )\n {\n this._setVector3Property( \"fg_color\", color );\n }\n\n\n /**\n * @summary アイコン背景の色を設定\n * @param {mapray.Vector3} color アイコン背景の色\n */\n setBGColor( color )\n {\n this._setVector3Property( \"bg_color\", color );\n }\n\n\n /**\n * @summary テキストアイコンのフォントを設定\n * @param {string} font_family フォントファミリー\n */\n setFontFamily( font_family )\n {\n this._setValueProperty( \"font_family\", font_family );\n }\n\n\n /**\n * Add Pin\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {float} [props.size] アイコンサイズ\n * @param {mapray.Vector3} [props.fg_color] アイコン色\n * @param {mapray.Vector3} [props.bg_color] 背景色\n * @param {string} [props.id] Entryを識別するID\n * @return {mapray.PinEntity.TextPinEntry} 追加したEntry\n */\n addPin( position, props )\n {\n return this.addTextPin( \"\", position, props );\n }\n\n /**\n * Add Maki Icon Pin\n * @param {string} id ID of Maki Icon\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {float} [props.size] アイコンサイズ\n * @param {mapray.Vector3} [props.fg_color] アイコン色\n * @param {mapray.Vector3} [props.bg_color] 背景色\n * @param {string} [props.id] Entryを識別するID\n * @return {mapray.PinEntity.MakiIconPinEntry} 追加したEntry\n */\n addMakiIconPin( id, position, props )\n {\n var entry = new MakiIconPinEntry( this, id, position, props );\n this._entries.push( entry );\n this._primitive_producer.onAddEntry();\n return entry;\n }\n\n /**\n * Add Text Pin\n * @param {string} text ピンに表示されるテキスト\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {float} [props.size] アイコンサイズ\n * @param {mapray.Vector3} [props.fg_color] アイコン色\n * @param {mapray.Vector3} [props.bg_color] 背景色\n * @param {string} [props.font_family] フォントファミリー\n * @param {string} [props.id] Entryを識別するID\n * @return {mapray.PinEntity.TextPinEntry} 追加したEntry\n */\n addTextPin( text, position, props )\n {\n var entry = new TextPinEntry( this, text, position, props );\n this._entries.push( entry );\n this._primitive_producer.onAddEntry();\n return entry;\n }\n\n\n /**\n * @summary 専用マテリアルを取得\n * @private\n */\n _getMaterial( render_target )\n {\n var scene = this.scene;\n if ( render_target === RenderTarget.SCENE ) {\n if ( !scene._PinEntity_pin_material ) {\n // scene にマテリアルをキャッシュ\n scene._PinEntity_pin_material = new PinMaterial( scene.glenv );\n }\n return scene._PinEntity_pin_material;\n }\n else if (render_target === RenderTarget.RID) {\n if ( !scene._PinEntity_pin_material_pick ) {\n // scene にマテリアルをキャッシュ\n scene._PinEntity_pin_material_pick = new PinMaterial( scene.glenv, { ridMaterial: true } );\n }\n return scene._PinEntity_pin_material_pick;\n }\n }\n\n\n /**\n * @private\n */\n _setValueProperty( name, value )\n {\n var props = this._parent_props;\n if ( props[name] != value ) {\n props[name] = value;\n this._primitive_producer.onChangeParentProperty();\n }\n }\n\n\n /**\n * @private\n */\n _setVector3Property( name, value )\n {\n var dst = this._parent_props[name];\n if ( !dst ) {\n dst = this._parent_props[name] = GeoMath.createVector3f( value );\n this._primitive_producer.onChangeParentProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] || dst[2] !== value[2] ) {\n GeoMath.copyVector3( value, dst );\n this._primitive_producer.onChangeParentProperty();\n }\n }\n\n\n /**\n * @private\n */\n _setVector2Property( name, value )\n {\n var dst = this._parent_props[name];\n if ( !dst ) {\n this._parent_props[name] = GeoMath.createVector2f( value );\n this._primitive_producer.onChangeParentProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] ) {\n GeoMath.copyVector2( value, dst );\n this._primitive_producer.onChangeParentProperty();\n }\n }\n\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n var position = new GeoPoint();\n\n for ( let entry of json.entries ) {\n position.setFromArray( entry.position );\n this.addPin( position, entry );\n }\n \n if ( json.size ) this.setSize( json.size );\n if ( json.fg_color ) this.setFGColor( json.fg_color );\n if ( json.bg_color ) this.setBGColor( json.bg_color );\n if ( json.font_family ) this.setBGColor( json.font_family );\n }\n\n \n /**\n * @summary IDでEntryを取得\n * @param {string} id ID\n * @return {mapray.PinEntity.MakiIconPinEntry|mapray.PinEntity.TextPinEntry} IDが一致するEntry(無ければundefined)\n */\n getEntry( id )\n {\n return this._entries.find((entry) => entry.id === id);\n }\n}\n\n\n// クラス定数の定義\n{\n PinEntity.SAFETY_PIXEL_MARGIN = 1;\n PinEntity.MAX_IMAGE_WIDTH = 4096;\n PinEntity.CIRCLE_SEP_LENGTH = 32;\n PinEntity.DEFAULT_SIZE = GeoMath.createVector2f( [30, 30] );\n PinEntity.DEFAULT_FONT_FAMILY = \"sans-serif\";\n PinEntity.DEFAULT_FG_COLOR = GeoMath.createVector3f( [1.0, 1.0, 1.0] );\n PinEntity.DEFAULT_BG_COLOR = GeoMath.createVector3f( [0.35, 0.61, 0.81] );\n\n PinEntity.SAFETY_PIXEL_MARGIN = 1;\n PinEntity.MAX_IMAGE_WIDTH = 4096;\n}\n\n\n\n/**\n * @summary PrimitiveProducer\n *\n * TODO: relative で標高の変化のたびにテクスチャを生成する必要はないので\n * Layout でのテクスチャの生成とメッシュの生成を分離する\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n /**\n * @param {mapray.PinEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._glenv = entity.scene.glenv;\n this._dirty = true;\n\n // プリミティブの要素\n this._transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._properties = {\n image: null, // 画像\n image_mask: null, // マスク画像\n };\n\n // プリミティブ\n var primitive = new Primitive( this._glenv, null, entity._getMaterial( RenderTarget.SCENE ), this._transform );\n primitive.properties = this._properties;\n this._primitive = primitive;\n\n var pickPrimitive = new Primitive( this._glenv, null, entity._getMaterial( RenderTarget.RID ), this._transform );\n pickPrimitive.properties = this._properties;\n this._pickPrimitive = pickPrimitive;\n\n // プリミティブ配列\n this._primitives = [];\n this._pickPrimitives = [];\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n const region = new EntityRegion();\n\n for ( let {position} of this.entity._entries ) {\n region.addPoint( position );\n }\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n this._dirty = true;\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n this._updatePrimitive( stage );\n return stage.getRenderTarget() === RenderTarget.SCENE ? this._primitives : this._pickPrimitives;\n }\n\n\n /**\n * @summary 親プロパティが変更されたことを通知\n */\n onChangeParentProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 子プロパティが変更されたことを通知\n */\n onChangeChildProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 高度モードが変更されたことを通知\n */\n onChangeAltitudeMode()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary エントリが追加されたことを通知\n */\n onAddEntry()\n {\n // 変化した可能性がある\n this.needToCreateRegions();\n this._dirty = true;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 入力:\n * this.entity._entries\n * this._dirty\n * 出力:\n * this._transform\n * this._properties.image\n * this._primitive.mesh\n * this._primitives\n * this._dirty\n *\n * @return {array.} this._primitives\n *\n * @private\n */\n _updatePrimitive()\n {\n if ( !this._dirty ) {\n // 更新する必要はない\n return;\n }\n\n if ( this.entity._entries.length == 0 ) {\n this._primitives = [];\n this._pickPrimitives = [];\n this._dirty = false;\n return;\n }\n\n // 各エントリーの GOCS 位置を生成 (平坦化配列)\n var gocs_array = this._createFlatGocsArray();\n\n // プリミティブの更新\n // primitive.transform\n this._updateTransform( gocs_array );\n\n var layout = new Layout( this, gocs_array );\n if ( !layout.isValid() ) {\n // 更新に失敗\n this._primitives = [];\n this._pickPrimitives = [];\n this._dirty = false;\n return;\n }\n\n // テクスチャ設定\n var properties = this._properties;\n if ( properties.image ) {\n properties.image.dispose();\n }\n properties.image = layout.texture;\n\n if ( properties.image_mask ) {\n properties.image_mask.dispose();\n }\n properties.image_mask = layout.texture_mask;\n\n // メッシュ生成\n var mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_offset\", size: 2 },\n { name: \"a_texcoord\", size: 2 },\n { name: \"a_texmaskcoord\", size: 2 },\n { name: \"a_fg_color\", size: 3 },\n { name: \"a_bg_color\", size: 3 },\n ],\n vertices: layout.vertices,\n indices: layout.indices\n };\n var mesh = new Mesh( this._glenv, mesh_data );\n\n // メッシュ設定\n // primitive.mesh\n var primitive = this._primitive;\n if ( primitive.mesh ) {\n primitive.mesh.dispose();\n }\n var pickPrimitive = this._pickPrimitive;\n if ( pickPrimitive.mesh ) {\n pickPrimitive.mesh.dispose();\n }\n primitive.mesh = mesh;\n pickPrimitive.mesh = mesh;\n\n // 更新に成功\n this._primitives = [primitive];\n this._pickPrimitives = [pickPrimitive];\n this._dirty = false;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 条件:\n * this.entity._entries.length > 0\n * 入力:\n * this.entity._entries.length\n * 出力:\n * this._transform\n *\n * @param {number[]} gocs_array GOCS 平坦化配列\n *\n * @private\n */\n _updateTransform( gocs_array )\n {\n var num_entries = this.entity._entries.length;\n var xsum = 0;\n var ysum = 0;\n var zsum = 0;\n\n for ( let i = 0; i < num_entries; ++i ) {\n let ibase = 3*i;\n xsum += gocs_array[ibase];\n ysum += gocs_array[ibase + 1];\n zsum += gocs_array[ibase + 2];\n }\n\n // 変換行列の更新\n var transform = this._transform;\n transform[12] = xsum / num_entries;\n transform[13] = ysum / num_entries;\n transform[14] = zsum / num_entries;\n }\n\n\n /**\n * @summary GOCS 平坦化配列を取得\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GOCS 平坦化配列\n * @private\n */\n _createFlatGocsArray()\n {\n const num_points = this.entity._entries.length;\n return GeoPoint.toGocsArray( this._getFlatGeoPoints_with_Absolute(), num_points,\n new Float64Array( 3 * num_points ) );\n }\n\n\n /**\n * @summary GeoPoint 平坦化配列を取得 (絶対高度)\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GeoPoint 平坦化配列\n * @private\n */\n _getFlatGeoPoints_with_Absolute()\n {\n const owner = this.entity;\n const entries = owner._entries;\n const num_points = entries.length;\n const flat_array = new Float64Array( 3 * num_points );\n\n // flat_array[] に経度要素と緯度要素を設定\n for ( let i = 0; i < num_points; ++i ) {\n let pos = entries[i].position;\n flat_array[3*i] = pos.longitude;\n flat_array[3*i + 1] = pos.latitude;\n }\n\n switch ( owner.altitude_mode ) {\n case AltitudeMode.RELATIVE:\n case AltitudeMode.CLAMP:\n // flat_array[] の高度要素に現在の標高を設定\n owner.scene.viewer.getExistingElevations( num_points, flat_array, 0, 3, flat_array, 2, 3 );\n\n if ( owner.altitude_mode === AltitudeMode.RELATIVE ) {\n // flat_array[] の高度要素に絶対高度を設定\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] += entries[i].position.altitude;\n }\n }\n break;\n\n default: // AltitudeMode.ABSOLUTE\n // flat_array[] の高度要素に絶対高度を設定\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] = entries[i].position.altitude;\n }\n break;\n }\n\n return flat_array;\n }\n\n}\n\n\n\n/**\n * @summary ピン要素\n * @hideconstructor\n * @memberof mapray.PinEntity\n * @public\n * @abstract\n */\nclass AbstractPinEntry {\n\n constructor( owner, position, props ) {\n this._owner = owner;\n this._position = position.clone();\n\n // animation.BindingBlock\n this._animation = new EasyBindingBlock();\n \n this._setupAnimationBindingBlock();\n\n this._props = Object.assign( {}, props ); // props の複製\n this._copyPropertyVector3f( \"fg_color\" ); // deep copy\n this._copyPropertyVector3f( \"bg_color\" ); // deep copy\n this._copyPropertyVector2f( \"size\" ); // deep copy\n }\n\n _loadIcon() {\n throw new Error(\"loadIcon() is not implemented: \" + this.constructor.name);\n }\n\n /**\n * @summary 位置\n * @type {mapray.GeoPoint}\n * @readonly\n * @package\n */\n get position()\n {\n return this._position;\n }\n\n /**\n * @summary ID\n * @type {string}\n * @readonly\n */\n get id()\n {\n return this._props.hasOwnProperty( \"id\" ) ? this._props.id : \"\";\n }\n\n /**\n * @summary アイコンサイズ (Pixels)\n * @type {mapray.Vector2}\n * @readonly\n * @package\n */\n get size()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return (\n props.size || parent.size ||\n (\n this.icon ? GeoMath.createVector2f( [ this.icon.width, this.icon.height ] ):\n PinEntity.DEFAULT_SIZE\n )\n );\n }\n\n /**\n * @summary アイコン色\n * @type {mapray.Vector3}\n * @readonly\n * @package\n */\n get fg_color()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return props.fg_color || parent.fg_color || PinEntity.DEFAULT_FG_COLOR;\n }\n\n /**\n * @summary アイコン背景色\n * @type {mapray.Vector3}\n * @readonly\n * @package\n */\n get bg_color()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return props.bg_color || parent.bg_color || PinEntity.DEFAULT_BG_COLOR;\n }\n\n /**\n * @summary アニメーションパラメータ設定\n *\n * @type {mapray.animation.BindingBlock}\n * @readonly\n */\n get animation() { return this._animation; }\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector2 = Type.find( \"vector2\" );\n const vector3 = Type.find( \"vector3\" );\n \n // パラメータ名: position\n // パラメータ型: vector3\n // ベクトルの要素が longitude, latitude, altitude 順であると解釈\n const position_temp = new GeoPoint();\n\n block.addEntry( \"position\", [vector3], null, value => {\n position_temp.setFromArray( value ); // Vector3 -> GeoPoint\n this.setPosition( position_temp );\n } );\n\n // パラメータ名: fg_color\n // パラメータ型: vector3\n // アイコンの色\n block.addEntry( \"fg_color\", [vector3], null, value => {\n this.setFGColor( value );\n } );\n \n // パラメータ名: bg_color\n // パラメータ型: vector3\n // アイコン背景の色\n block.addEntry( \"bg_color\", [vector3], null, value => {\n this.setBGColor( value );\n } );\n\n // パラメータ名: size\n // パラメータ型: vector2 | number\n // 型が vector2 のとき アイコンのピクセルサイズX, Y 順であると解釈\n // 型が number のとき アイコンのピクセルサイズX, Y は同値\n const size_temp = GeoMath.createVector2();\n let size_type;\n\n let size_tsolver = curve => {\n size_type = AnimUtil.findFirstTypeSupported( curve, [vector2, number] );\n return size_type;\n };\n\n block.addEntry( \"size\", [vector2, number], size_tsolver, value => {\n if ( size_type === vector2 ) {\n this.setSize( value );\n }\n else { // size_type === number\n size_temp[0] = value;\n size_temp[1] = value;\n this.setSize( size_temp );\n }\n } );\n }\n\n /**\n * @summary モデル原点位置を設定\n *\n * @param {mapray.GeoPoint} position モデル原点の位置\n */\n setPosition( position )\n {\n if ( this._position.longitude !== position.longitude ||\n this._position.latitude !== position.latitude ||\n this._position.altitude !== position.altitude ) {\n // 位置が変更された\n this._position.assign( position );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n /**\n * @summary アイコンのピクセルサイズを指定\n * @param {mapray.Vector3} size アイコンのピクセルサイズ\n */\n setSize( size )\n {\n this._setVector2Property( \"size\", size );\n }\n\n\n /**\n * @summary アイコンの色を設定\n * @param {mapray.Vector3} color アイコンの色\n */\n setFGColor( color )\n {\n this._setVector3Property( \"fg_color\", color );\n }\n\n\n /**\n * @summary アイコン背景の色を設定\n * @param {mapray.Vector3} color アイコン背景の色\n */\n setBGColor( color )\n {\n this._setVector3Property( \"bg_color\", color );\n }\n\n /**\n * @private\n */\n _copyPropertyVector3f( name )\n {\n var props = this._props;\n if ( props.hasOwnProperty( name ) ) {\n props[name] = GeoMath.createVector3f( props[name] );\n }\n }\n\n /**\n * @private\n */\n _copyPropertyVector2f( name )\n {\n var props = this._props;\n if ( props.hasOwnProperty( name ) ) {\n if ( typeof( props[name] ) === 'number' ) {\n props[name] = GeoMath.createVector2f( [ props[name], props[name] ] );\n }\n else {\n props[name] = GeoMath.createVector2f( props[name] );\n }\n }\n }\n \n /**\n * @private\n */\n _setVector3Property( name, value )\n {\n var dst = this._props[name];\n if ( !dst ) {\n dst = this._props[name] = GeoMath.createVector3f( value );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] || dst[2] !== value[2] ) {\n GeoMath.copyVector3( value, dst );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n /**\n * @private\n */\n _setVector2Property( name, value )\n {\n var dst = this._props[name];\n if ( !dst ) {\n this._props[name] = GeoMath.createVector2f( value );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] ) {\n GeoMath.copyVector2( value, dst );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n isLoaded() {\n return this._icon.isLoaded();\n }\n\n get icon() {\n return this._icon;\n }\n\n draw( context, x, y, width, height ) {\n this._icon.draw( context, x, y, width, height );\n }\n}\n\nPinEntity.AbstractPinEntry = AbstractPinEntry;\n\n\n/**\n * @summary MakiIcon要素\n * @hideconstructor\n * @memberof mapray.PinEntity\n * @extends mapray.PinEntity.AbstractPinEntry\n * @public\n */\nclass MakiIconPinEntry extends AbstractPinEntry {\n\n /**\n * @param {mapray.PinEntity} owner 所有者\n * @param {string} id MakiアイコンのID\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {float} [props.size] アイコンサイズ\n * @param {mapray.Vector3} [props.fg_color] アイコン色\n * @param {mapray.Vector3} [props.bg_color] 背景色\n * @param {string} [props.id] Entryを識別するID\n */\n constructor( owner, id, position, props )\n {\n super( owner, position, props );\n \n this.setId( id );\n \n this._setupMakiIconPinAnimationBindingBlock();\n }\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupMakiIconPinAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const string = Type.find( \"string\" );\n\n // パラメータ名: id\n // パラメータ型: string\n // アイコンのID\n block.addEntry( \"id\", [string], null, value => {\n this.setId( value );\n } ); \n }\n\n /**\n * @summary アイコンのIDを設定\n * @param {string} maki_id アイコンのID\n */\n setId( maki_id )\n {\n if ( this._maki_id !== maki_id ) {\n // アイコンのIDが変更された\n this._maki_id = maki_id;\n this._icon = MakiIconPinEntry.makiIconLoader.load( maki_id );\n this._icon.onEnd(item => {\n this._owner.getPrimitiveProducer()._dirty = true;\n });\n }\n }\n}\n\nPinEntity.MakiIconPinEntry = MakiIconPinEntry;\n\n\n{\n MakiIconPinEntry.makiIconLoader = new URLTemplateIconLoader( \"https://resource.mapray.com/styles/v1/icons/maki/\", \".svg\" );\n}\n\n\n\n\n/**\n * @summary MakiIcon要素\n * @hideconstructor\n * @memberof mapray.PinEntity\n * @extends mapray.PinEntity.AbstractPinEntry\n * @public\n */\nclass TextPinEntry extends AbstractPinEntry {\n\n /**\n * @param {mapray.PinEntity} owner 所有者\n * @param {string} text テキスト\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {float} [props.size] アイコンピクセルサイズ\n * @param {mapray.Vector3} [props.fg_color] アイコン色\n * @param {mapray.Vector3} [props.bg_color] 背景色\n * @param {string} [props.font_family] フォントファミリー\n * @param {string} [props.id] Entryを識別するID\n */\n constructor( owner, text, position, props )\n {\n super( owner, position, props );\n \n this.setText( text );\n \n this._setupTextPinAnimationBindingBlock();\n }\n\n\n /**\n * @summary フォントファミリー\n * @type {string}\n * @readonly\n * @package\n */\n get font_family()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return props.font_family || parent.font_family || PinEntity.DEFAULT_FONT_FAMILY;\n }\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupTextPinAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const string = Type.find( \"string\" );\n\n // パラメータ名: text\n // パラメータ型: string\n // テキスト\n block.addEntry( \"text\", [string], null, value => {\n this.setText( value );\n } ); \n }\n\n /**\n * @summary テキストを設定\n * @param {string} text テキスト\n */\n setText( text )\n {\n if ( this._text !== text ) {\n // テキストが変更された\n this._text = text;\n this._icon = TextPinEntry.textIconLoader.load( {\n text: this._text,\n props: {\n size: this.size,\n font_family: this.font_family,\n }\n } );\n this._icon.onEnd(item => {\n this._owner.getPrimitiveProducer()._dirty = true;\n });\n }\n }\n}\n\nPinEntity.TextPinEntry = TextPinEntry;\n\n\n{\n TextPinEntry.textIconLoader = new TextIconLoader();\n}\n\n\n\n\n/**\n * @summary 要素を Canvas 上にレイアウト\n *\n * @memberof mapray.PinEntity\n * @private\n */\nclass Layout {\n\n /**\n * @desc\n * 入力:\n * owner._glenv\n * owner.entity._entries\n * owner._transform\n *\n * @param {PrimitiveProducer} owner 所有者\n * @param {number[]} gocs_array GOCS 平坦化配列\n */\n constructor( owner, gocs_array )\n {\n this._owner = owner;\n this._items = this._createItemList();\n this._is_valid = true;\n\n var row_layouts = this._createRowLayouts();\n if ( row_layouts.length == 0 ) {\n // 有効なテキストが1つも無い\n this._is_valid = false;\n return;\n }\n\n // アイテムの配置の設定とキャンバスサイズの決定\n var size = this._setupLocation( row_layouts );\n\n this._texture = this._createTexture( size.width, size.height );\n this._texture_mask = this._createTextureMask();\n this._vertices = this._createVertices( size.width, size.height, gocs_array );\n this._indices = this._createIndices();\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._is_valid;\n }\n\n\n /**\n * @summary テクスチャ\n * @type {mapray.Texture}\n * @readonly\n */\n get texture()\n {\n return this._texture;\n }\n\n /**\n * @summary テクスチャマスク\n * @type {mapray.Texture}\n * @readonly\n */\n get texture_mask()\n {\n return this._texture_mask;\n }\n\n\n /**\n * @summary 頂点配列\n * @desc\n * 条件:\n * this._entries.length > 0\n * 入力:\n * this._entries\n * this._transform\n * @type {Float32Array}\n * @readonly\n */\n get vertices()\n {\n return this._vertices;\n }\n\n\n /**\n * @summary インデックス配列\n * @type {Uint32Array}\n * @readonly\n */\n get indices()\n {\n return this._indices;\n }\n\n\n /**\n * @summary レイアウトアイテムのリストを生成\n * @return {array.}\n * @private\n */\n _createItemList()\n {\n const map = new Map();\n\n const items = [];\n let counter = 0;\n for ( let entry of this._owner.entity._entries ) {\n if ( entry.isLoaded() ) {\n let item = map.get( entry.icon );\n if ( !item ) {\n map.set( entry.icon, item = new LItem( this ) );\n items.push( item );\n }\n item.add( counter++, entry );\n }\n }\n\n return items;\n }\n\n /**\n * @summary RowLayout のリストを生成\n * @return {array.}\n * @private\n */\n _createRowLayouts()\n {\n // アイテムリストの複製\n var items = [].concat( this._items );\n\n // RowLayout 内であまり高さに差が出ないように、アイテムリストを高さで整列\n items.sort( function( a, b ) { return a.height_pixel - b.height_pixel; } );\n\n // リストを生成\n var row_layouts = [];\n while ( items.length > 0 ) {\n var row_layout = new RowLayout( items );\n if ( row_layout.isValid() ) {\n row_layouts.push( row_layout );\n }\n }\n\n return row_layouts;\n }\n\n\n /**\n * @summary テクスチャを生成\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @return {mapray.Texture} テキストテクスチャ\n * @private\n */\n _createTexture( width, height )\n {\n var context = Dom.createCanvasContext( width, height );\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n item.draw( context );\n }\n\n var glenv = this._owner._glenv;\n var opts = {\n usage: Texture.Usage.ICON\n };\n return new Texture( glenv, context.canvas, opts );\n }\n\n _createTextureMask()\n {\n var context = Dom.createCanvasContext( 3, 3 );\n context.fillRect( 1, 1, 1, 1 );\n var glenv = this._owner._glenv;\n var opts = {\n usage: Texture.Usage.ICON,\n mag_filter: glenv.context.NEAREST\n };\n return new Texture( glenv, context.canvas, opts );\n }\n\n /**\n * @summary 頂点配列を生成\n *\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @param {number[]} gocs_array GOCS 平坦化配列\n * @return {array.} 頂点配列 [左下0, 右下0, 左上0, 右上0, ...]\n *\n * @private\n */\n _createVertices( width, height, gocs_array )\n {\n var vertices = [];\n\n // テキスト集合の原点 (GOCS)\n var transform = this._owner._transform;\n var xo = transform[12];\n var yo = transform[13];\n var zo = transform[14];\n\n /*\n || \n | | \n | |<--rx--->| \n ___-------___ ---- \n / \\ ^ \n / \\ ry \n | | | ----\n | | v ^ \n | c | ---- size.y\n | | ^ V \n | | | ----\n \\ / | \n '----_0___3_----' | \n | | | \n | | h \n | | | \n | | | \n | | | \n | | v \n 1---2 ------------ \n \n >| w |< \n */\n\n var xn = 1 / width;\n var yn = 1 / height;\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n for ( var ie = 0; ie < item.entries.length; ie++ ) {\n var eitem = item.entries[ie];\n var entry = eitem.entry;\n var size = entry.size;\n var rx = size[0] * 1.5 / 2;\n var ry = size[1] * 1.5 / 2;\n var h = ry * 2;\n var w = Math.max(2, rx / 10);\n\n // Relativize based on (xo, yo, zo)\n var ibase = eitem.index * 3;\n var xm = gocs_array[ibase] - xo;\n var ym = gocs_array[ibase + 1] - yo;\n var zm = gocs_array[ibase + 2] - zo;\n\n var fg_color = entry.fg_color;\n var bg_color = entry.bg_color;\n\n // Image dimensions (Image Coordinate)\n var xc = item.pos_x;\n var yc = item.pos_y;\n var xsize = item.width;\n var ysize = item.height;\n\n var vertices_push_texture = ( px, py ) => {\n vertices.push( (xc + xsize * px) * xn, 1 - (yc + ysize * py) * yn );\n };\n\n // p0\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -w / 2, h - ry ); // a_offset\n vertices_push_texture( 0.5 - (w/2/rx), 1.5/2 + 0.5 ); // a_texcoord\n vertices.push( -0.25 + 0.5, -0.25 + 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n\n // p1\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -w / 2, 0 ); // a_offset\n vertices_push_texture( 0.5 - (w/2/rx), 1.5/2 + 0.5 ); // a_texcoord\n vertices.push( -0.25 + 0.5, -0.25 + 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n\n // p2\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( w / 2, 0 ); // a_offset\n vertices_push_texture( 0.5 + (w/2/rx), 1.5/2 + 0.5 ); // a_texcoord\n vertices.push( -0.25 + 0.5, -0.25 + 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n\n // p3\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( w / 2, h - ry ); // a_offset\n vertices_push_texture( 0.5 + (w/2/rx), 1.5/2 + 0.5 ); // a_texcoord\n vertices.push( -0.25 + 0.5, -0.25 + 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n\n // c\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( 0, h ); // a_offset\n vertices_push_texture( 0.5, 0.5 ); // a_texcoord\n vertices.push( 0.5, 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n\n for ( var k = 1; k < PinEntity.CIRCLE_SEP_LENGTH; k++ ) {\n var th = (k / PinEntity.CIRCLE_SEP_LENGTH * 2 - 0.5) * Math.PI;\n var cos_th = Math.cos(th);\n var sin_th = Math.sin(th);\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( rx * cos_th, ry * sin_th + h ); // a_offset\n vertices_push_texture( 1.5 * cos_th / 2 + 0.5, -1.5 * sin_th / 2 + 0.5 ); // a_texcoord\n vertices.push( cos_th * 0.25 + 0.5 , sin_th * 0.25 + 0.5 ); // a_texmaskcoord\n vertices.push( ...fg_color );\n vertices.push( ...bg_color );\n }\n }\n }\n\n return vertices;\n }\n\n\n /**\n * @summary インデックス配列を生成\n * @return {array.} インデックス配列 []\n * @private\n */\n _createIndices()\n {\n var indices = [];\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n for ( var ie = 0; ie < item.entries.length; ie++ ) {\n var eitem = item.entries[ie];\n var base = ( 4 + 1 + PinEntity.CIRCLE_SEP_LENGTH - 1 ) * eitem.index;\n\n var p = base;\n var p0 = p;\n var p3 = p + 3;\n indices.push( p, p+1, p+2 );\n indices.push( p, p+2, p+3 );\n p += 4;\n\n var centerPos = p++;\n indices.push( centerPos, p0, p3 );\n indices.push( centerPos, p3, p );\n for ( var j = 1; j < PinEntity.CIRCLE_SEP_LENGTH - 1; j++ ) {\n indices.push( centerPos, p++, p );\n }\n indices.push( centerPos, p++, p0 );\n }\n }\n\n return indices;\n }\n\n\n /**\n * @summary アイテムの配置を設定\n * @param {array.} row_layouts\n * @return {object} キャンバスサイズ\n * @private\n */\n _setupLocation( row_layouts )\n {\n var width = 0;\n var height = 0;\n\n height += PinEntity.SAFETY_PIXEL_MARGIN;\n\n for ( var i = 0; i < row_layouts.length; ++i ) {\n var row_layout = row_layouts[i];\n row_layout.locate( height );\n width = Math.max( row_layout.width_assumed, width );\n height += row_layout.height_pixel + PinEntity.SAFETY_PIXEL_MARGIN;\n }\n\n return {\n width: width,\n height: height\n };\n }\n\n}\n\n\n\n/**\n * @summary レイアウト対象\n * @memberof mapray.PinEntity\n * @private\n */\nclass LItem {\n\n /**\n * @param {mapray.PinEntity.Layout} layout 所有者\n * @param {mapray.PinEntity.Entry} entry PinEntityのエントリ\n */\n constructor( layout )\n {\n this.entries = [];\n\n // テキストの基点\n this._pos_x = 0; // 左端\n this._pos_y = 0; // ベースライン位置\n\n this._height = this._width = null;\n\n this._is_canceled = false;\n }\n\n add( index, entry ) {\n var size = entry.size;\n if ( this._width === null || this._width < size[0] ) this._width = size[0];\n if ( this._height === null || this._height < size[1] ) this._height = size[1];\n this.entries.push( { index, entry } );\n }\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_x()\n {\n return this._pos_x;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_y()\n {\n return this._pos_y;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get width()\n {\n return this._width;\n }\n\n get height()\n {\n return this._height;\n }\n\n\n /**\n * キャンバス上でのテキストの横画素数\n * @type {number}\n * @readonly\n */\n get width_pixel()\n {\n return Math.ceil( this._width );\n }\n\n\n /**\n * キャンバス上でのテキストの縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return Math.ceil( this._height );\n }\n\n\n /**\n * 取り消し状態か?\n * @type {boolean}\n * @readonly\n */\n get is_canceled()\n {\n return this._is_canceled;\n }\n\n\n /**\n * @summary 取り消し状態に移行\n */\n cancel()\n {\n this._is_canceled = true;\n }\n\n\n /**\n * @summary 配置を決定\n * @param {number} x テキスト矩形左辺の X 座標 (キャンバス座標系)\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( x, y )\n {\n this._pos_x = x;\n this._pos_y = y;\n }\n\n draw( context ) {\n\n this.entries[0].entry.draw( context, this._pos_x, this.pos_y, this.width, this.height );\n\n var RENDER_BOUNDS = false;\n if ( RENDER_BOUNDS ) {\n context.beginPath();\n context.moveTo( this._pos_x , this._pos_y );\n context.lineTo( this._pos_x + this.width, this._pos_y );\n context.lineTo( this._pos_x + this.width, this._pos_y + this.height );\n context.lineTo( this._pos_x , this._pos_y + this.height );\n context.closePath();\n context.stroke();\n }\n }\n}\n\n\n\n/**\n * @summary 水平レイアウト\n * @memberof mapray.PinEntity\n * @private\n */\nclass RowLayout {\n\n /**\n * @desc\n * レイアウトされた、またはレイアウトに失敗したアイテムは src_items から削除される。
\n * レイアウトに失敗したアイテムは取り消し (is_canceled) になる。
\n * @param {array.} src_items アイテムリスト\n */\n constructor( src_items )\n {\n var width_assumed_total = 0;\n var height_pixel_max = 0;\n var row_items = [];\n\n width_assumed_total += PinEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n while ( src_items.length > 0 ) {\n var item = src_items.shift();\n var width_assumed = item.width_pixel + PinEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n\n if ( width_assumed_total + width_assumed <= PinEntity.MAX_IMAGE_WIDTH ) {\n // 行にアイテムを追加\n row_items.push( item );\n width_assumed_total += width_assumed;\n height_pixel_max = Math.max( item.height_pixel, height_pixel_max );\n }\n else {\n if ( row_items.length == 0 ) {\n // テキストが長すぎて表示できない\n item.cancel();\n }\n else {\n // 次の行になるため差し戻して終了\n src_items.unshift( item );\n break;\n }\n }\n }\n\n this._items = row_items;\n this._width_assumed = width_assumed_total;\n this._height_pixel = height_pixel_max;\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._items.length > 0;\n }\n\n\n /**\n * \n * @type {array.}\n * @readonly\n */\n get items()\n {\n return this._items;\n }\n\n\n /**\n * キャンバス上での行の横占有画素数\n * @type {number}\n * @readonly\n */\n get width_assumed()\n {\n return this._width_assumed;\n }\n\n\n /**\n * キャンバス上での行の縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return this._height_pixel;\n }\n\n\n /**\n * @summary レイアウトの配置を決定\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( y )\n {\n var items = this._items;\n var x = 0;\n\n x += PinEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n item.locate( x, y );\n x += item.width_pixel + PinEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n }\n }\n\n}\n\n\n\nexport default PinEntity;\n","import EntityMaterial from \"./EntityMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport image_icon_vs_code from \"./shader/image_icon.vert\";\nimport image_icon_fs_code from \"./shader/image_icon.frag\";\nimport rid_fs_code from \"./shader/rid.frag\";\nimport { RenderTarget } from \"./RenderStage\";\n\n\n/**\n * @summary イメージアイコンマテリアル\n * @memberof mapray\n * @extends mapray.EntityMaterial\n * @private\n * @see mapray.ImageIconEntity\n */\nclass ImageIconMaterial extends EntityMaterial {\n\n /**\n * @param {mapray.GLEnv} glenv\n */\n constructor( glenv, options = {} )\n {\n super( glenv, image_icon_vs_code, options.ridMaterial ? rid_fs_code : image_icon_fs_code );\n\n // 不変パラメータを事前設定\n this.bindProgram();\n this.setInteger( \"u_image\", ImageIconMaterial.TEXUNIT_IMAGE );\n // this.setInteger( \"u_image_mask\", ImageIconMaterial.TEXUNIT_IMAGE_MASK );\n }\n\n\n /**\n * @override\n */\n isTranslucent( stage, primitive )\n {\n // 半透明画像は非対応\n return false;\n }\n\n\n /**\n * @override\n */\n setParameters( stage, primitive )\n {\n super.setParameters( stage, primitive );\n\n var props = primitive.properties;\n\n // mat4 u_obj_to_clip\n this.setObjToClip( stage, primitive );\n\n // 画面パラメータ: {2/w, 2/h}\n // vec2 u_sparam\n var sparam = ImageIconMaterial._sparam;\n sparam[0] = 2 / stage._width;\n sparam[1] = 2 / stage._height;\n this.setVector2( \"u_sparam\", sparam );\n\n if (stage.getRenderTarget() === RenderTarget.SCENE) {\n // テクスチャのバインド\n // sampler2D u_image\n var image = props[\"image\"];\n this.bindTexture2D( ImageIconMaterial.TEXUNIT_IMAGE, image.handle );\n\n // テクスチャマスクのバインド\n // sampler2D u_image_mask\n // var image_mask = props[\"image_mask\"];\n // this.bindTexture2D( ImageIconMaterial.TEXUNIT_IMAGE_MASK, image_mask.handle );\n }\n }\n\n}\n\n\n// クラス定数の定義\n{\n ImageIconMaterial.TEXUNIT_IMAGE = 0; // 画像のテクスチャユニット\n // ImageIconMaterial.TEXUNIT_IMAGE_MASK = 1; // 画像マスクのテクスチャユニット\n\n // 計算用一時領域\n ImageIconMaterial._sparam = GeoMath.createVector2f();\n ImageIconMaterial._bg_color = GeoMath.createVector3f();\n ImageIconMaterial._fg_color = GeoMath.createVector3f();\n}\n\n\nexport default ImageIconMaterial;\n","import Entity from \"./Entity\";\nimport Primitive from \"./Primitive\";\nimport Mesh from \"./Mesh\";\nimport Texture from \"./Texture\";\nimport ImageIconMaterial from \"./ImageIconMaterial\";\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport { RenderTarget } from \"./RenderStage\";\nimport AltitudeMode from \"./AltitudeMode\";\nimport EntityRegion from \"./EntityRegion\";\nimport { ImageIconLoader } from \"./IconLoader\";\nimport Dom from \"./util/Dom\";\nimport EasyBindingBlock from \"./animation/EasyBindingBlock\";\nimport Type from \"./animation/Type\";\nimport AnimUtil from \"./animation/AnimUtil\";\nimport Resource, { URLResource } from \"./Resource\";\nimport AbstractPointEntity from \"./AbstractPointEntity\";\n\n\n/**\n * @summary 画像アイコンエンティティ\n * @memberof mapray\n * @extends mapray.Entity\n */\nclass ImageIconEntity extends AbstractPointEntity {\n\n /**\n * @param {mapray.Scene} scene 所属可能シーン\n * @param {object} [opts] オプション集合\n * @param {object} [opts.json] 生成情報\n * @param {object} [opts.refs] 参照辞書\n * @param {mapray.Loader.TransformCallback} [opts.transform] \n */\n constructor( scene, opts )\n {\n super( scene, opts );\n\n // 親プロパティ\n this._parent_props = {\n size: null,\n origin: null,\n };\n\n // Entity.PrimitiveProducer インスタンス\n this._primitive_producer = new PrimitiveProducer( this );\n\n this._animation.addDescendantUnbinder( () => { this._unbindDescendantAnimations(); } );\n this._setupAnimationBindingBlock();\n\n // 生成情報から設定\n if ( opts && opts.json ) {\n this._setupByJson( opts.json );\n }\n }\n\n\n /**\n * @override\n */\n getPrimitiveProducer()\n {\n return this._primitive_producer;\n }\n\n\n /**\n * EasyBindingBlock.DescendantUnbinder 処理\n *\n * @private\n */\n _unbindDescendantAnimations()\n {\n // すべてのエントリーを解除\n for ( let entry of this._entries ) {\n entry.animation.unbindAllRecursively();\n }\n }\n\n\n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const vector2 = Type.find( \"vector2\" );\n\n // パラメータ名: size\n // パラメータ型: vector2 | number\n // 型が vector2 のとき アイコンのピクセルサイズX, Y 順であると解釈\n // 型が number のとき アイコンのピクセルサイズX, Y の値\n const size_temp = GeoMath.createVector2();\n let size_type;\n\n let size_tsolver = curve => {\n size_type = AnimUtil.findFirstTypeSupported( curve, [vector2, number] );\n return size_type;\n };\n\n block.addEntry( \"size\", [vector2, number], size_tsolver, value => {\n if ( size_type === vector2 ) {\n this.setSize( value );\n }\n else { // size_type === number\n size_temp[0] = value;\n size_temp[1] = value;\n this.setSize( size_temp );\n }\n } );\n }\n\n\n /**\n * @summary アイコンのサイズを指定\n * @param {mapray.Vector2} size アイコンのピクセルサイズ\n */\n setSize( size ) {\n this._setVector2Property( \"size\", size );\n }\n\n\n /**\n * @summary アイコンの原点位置を指定\n * @param {mapray.Vector2} origin アイコンの原点位置\n */\n setOrigin( origin ) {\n this._setVector2Property( \"origin\", origin );\n }\n\n\n /**\n * @summary Add Image Icon\n * @param {URL|HTMLImageElement|HTMLCanvasElement} image_src 画像\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {mapray.Vector2} [props.size] アイコンサイズ\n * @param {string} [props.id] Entryを識別するID\n * @param {mapray.Loader.Transform} [props.transform] URL変換関数\n * @return {mapray.ImageIconEntity.ImageEntry} 追加したEntry\n */\n addImageIcon( image_src, position, props ) \n {\n var entry = new ImageEntry( this, image_src, position, props );\n this._entries.push( entry );\n this._primitive_producer.onAddEntry();\n return entry;\n }\n\n\n /**\n * @summary 専用マテリアルを取得\n * @private\n */\n _getMaterial( render_target )\n {\n var scene = this.scene;\n if ( render_target === RenderTarget.SCENE ) {\n if ( !scene._ImageEntity_image_material ) {\n // scene にマテリアルをキャッシュ\n scene._ImageEntity_image_material = new ImageIconMaterial( scene.glenv );\n }\n return scene._ImageEntity_image_material;\n }\n else if (render_target === RenderTarget.RID) {\n if ( !scene._ImageEntity_image_material_pick ) {\n // scene にマテリアルをキャッシュ\n scene._ImageEntity_image_material_pick = new ImageIconMaterial( scene.glenv, { ridMaterial: true } );\n }\n return scene._ImageEntity_image_material_pick;\n }\n }\n\n\n /**\n * @private\n */\n _setValueProperty( name, value )\n {\n var props = this._parent_props;\n if ( props[name] != value ) {\n props[name] = value;\n this._primitive_producer.onChangeParentProperty();\n }\n }\n\n /**\n * @private\n */\n _setVector2Property( name, value )\n {\n var dst = this._parent_props[name];\n if ( !dst ) {\n this._parent_props[name] = GeoMath.createVector2f( value );\n this._primitive_producer.onChangeParentProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] ) {\n GeoMath.copyVector2( value, dst );\n this._primitive_producer.onChangeParentProperty();\n }\n }\n\n /**\n * @private\n */\n _setupByJson( json )\n {\n var position = new GeoPoint();\n\n for ( let entry of json.entries ) {\n position.setFromArray( entry.position );\n this.addImageIcon( position, entry );\n }\n\n if ( json.size ) this.setSize( json.size );\n if ( json.origin ) this.setOrigin( json.origin );\n }\n\n \n /**\n * @summary IDでEntryを取得\n * @param {string} id ID\n * @return {mapray.ImageIconEntity.ImageEntry} IDが一致するEntry(無ければundefined)\n */\n getEntry( id )\n {\n return this._entries.find((entry) => entry.id === id);\n }\n}\n\n\n// クラス定数の定義\n{\n ImageIconEntity.DEFAULT_COLOR = GeoMath.createVector3f( [1, 1, 1] );\n ImageIconEntity.SAFETY_PIXEL_MARGIN = 1;\n ImageIconEntity.MAX_IMAGE_WIDTH = 4096;\n ImageIconEntity.CIRCLE_SEP_LENGTH = 32;\n ImageIconEntity.DEFAULT_ICON_SIZE = GeoMath.createVector2f( [30, 30] );\n ImageIconEntity.DEFAULT_ORIGIN = GeoMath.createVector2f( [ 0.5, 0.5 ] );\n\n ImageIconEntity.SAFETY_PIXEL_MARGIN = 1;\n ImageIconEntity.MAX_IMAGE_WIDTH = 4096;\n}\n\n\n\n/**\n * @summary PrimitiveProducer\n *\n * TODO: relative で標高の変化のたびにテクスチャを生成する必要はないので\n * Layout でのテクスチャの生成とメッシュの生成を分離する\n *\n * @private\n */\nclass PrimitiveProducer extends Entity.PrimitiveProducer {\n\n /**\n * @param {mapray.ImageIconEntity} entity\n */\n constructor( entity )\n {\n super( entity );\n\n this._glenv = entity.scene.glenv;\n this._dirty = true;\n\n // プリミティブの要素\n this._transform = GeoMath.setIdentity( GeoMath.createMatrix() );\n this._properties = {\n image: null, // アイコン画像\n };\n\n // プリミティブ\n var primitive = new Primitive( this._glenv, null, entity._getMaterial( RenderTarget.SCENE ), this._transform );\n primitive.properties = this._properties;\n this._primitive = primitive;\n\n var pickPrimitive = new Primitive( this._glenv, null, entity._getMaterial( RenderTarget.RID ), this._transform );\n pickPrimitive.properties = this._properties;\n this._pickPrimitive = pickPrimitive;\n\n // プリミティブ配列\n this._primitives = [];\n\n this._pickPrimitives = [];\n }\n\n\n /**\n * @override\n */\n createRegions()\n {\n const region = new EntityRegion();\n\n for ( let {position} of this.entity._entries ) {\n region.addPoint( position );\n }\n\n return [region];\n }\n\n\n /**\n * @override\n */\n onChangeElevation( regions )\n {\n this._dirty = true;\n }\n\n\n /**\n * @override\n */\n getPrimitives( stage )\n {\n this._updatePrimitive();\n return stage.getRenderTarget() === RenderTarget.SCENE ? this._primitives : this._pickPrimitives;\n }\n\n\n /**\n * @summary 親プロパティが変更されたことを通知\n */\n onChangeParentProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 子プロパティが変更されたことを通知\n */\n onChangeChildProperty()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary 高度モードが変更されたことを通知\n */\n onChangeAltitudeMode()\n {\n this._dirty = true;\n }\n\n\n /**\n * @summary エントリが追加されたことを通知\n */\n onAddEntry()\n {\n // 変化した可能性がある\n this.needToCreateRegions();\n this._dirty = true;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 入力:\n * this.entity._entries\n * this._dirty\n * 出力:\n * this._transform\n * this._properties.image\n * this._primitive.mesh\n * this._primitives\n * this._dirty\n *\n * @return {array.} this._primitives\n *\n * @private\n */\n _updatePrimitive()\n {\n if ( !this._dirty ) {\n // 更新する必要はない\n return;\n }\n\n if ( this.entity._entries.length == 0 ) {\n this._primitives = [];\n this._pickPrimitives = [];\n this._dirty = false;\n return;\n }\n\n // 各エントリーの GOCS 位置を生成 (平坦化配列)\n var gocs_array = this._createFlatGocsArray();\n\n // プリミティブの更新\n // primitive.transform\n this._updateTransform( gocs_array );\n\n var layout = new Layout( this, gocs_array );\n if ( !layout.isValid() ) {\n // 更新に失敗\n this._primitives = [];\n this._pickPrimitives = [];\n this._dirty = false;\n return;\n }\n\n // テクスチャ設定\n var properties = this._properties;\n if ( properties.image ) {\n properties.image.dispose();\n }\n properties.image = layout.texture;\n\n // メッシュ生成\n var mesh_data = {\n vtype: [\n { name: \"a_position\", size: 3 },\n { name: \"a_offset\", size: 2 },\n { name: \"a_texcoord\", size: 2 },\n ],\n vertices: layout.vertices,\n indices: layout.indices\n };\n var mesh = new Mesh( this._glenv, mesh_data );\n\n // メッシュ設定\n // primitive.mesh\n var primitive = this._primitive;\n if ( primitive.mesh ) {\n primitive.mesh.dispose();\n }\n primitive.mesh = mesh;\n\n var pickPrimitive = this._pickPrimitive;\n if ( pickPrimitive.mesh ) {\n pickPrimitive.mesh.dispose();\n }\n pickPrimitive.mesh = mesh;\n\n // 更新に成功\n this._primitives = [primitive];\n this._pickPrimitives = [pickPrimitive];\n this._dirty = false;\n }\n\n\n /**\n * @summary プリミティブの更新\n *\n * @desc\n * 条件:\n * this.entity._entries.length > 0\n * 入力:\n * this.entity._entries.length\n * 出力:\n * this._transform\n *\n * @param {number[]} gocs_array GOCS 平坦化配列\n *\n * @private\n */\n _updateTransform( gocs_array )\n {\n var num_entries = this.entity._entries.length;\n var xsum = 0;\n var ysum = 0;\n var zsum = 0;\n\n for ( let i = 0; i < num_entries; ++i ) {\n let ibase = 3*i;\n xsum += gocs_array[ibase];\n ysum += gocs_array[ibase + 1];\n zsum += gocs_array[ibase + 2];\n }\n\n // 変換行列の更新\n var transform = this._transform;\n transform[12] = xsum / num_entries;\n transform[13] = ysum / num_entries;\n transform[14] = zsum / num_entries;\n }\n\n\n /**\n * @summary GOCS 平坦化配列を取得\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GOCS 平坦化配列\n * @private\n */\n _createFlatGocsArray()\n {\n const num_points = this.entity._entries.length;\n return GeoPoint.toGocsArray( this._getFlatGeoPoints_with_Absolute(), num_points,\n new Float64Array( 3 * num_points ) );\n }\n\n\n /**\n * @summary GeoPoint 平坦化配列を取得 (絶対高度)\n *\n * 入力: this.entity._entries\n *\n * @return {number[]} GeoPoint 平坦化配列\n * @private\n */\n _getFlatGeoPoints_with_Absolute()\n {\n const owner = this.entity;\n const entries = owner._entries;\n const num_points = entries.length;\n const flat_array = new Float64Array( 3 * num_points );\n\n // flat_array[] に経度要素と緯度要素を設定\n for ( let i = 0; i < num_points; ++i ) {\n let pos = entries[i].position;\n flat_array[3*i] = pos.longitude;\n flat_array[3*i + 1] = pos.latitude;\n }\n\n switch ( owner.altitude_mode ) {\n case AltitudeMode.RELATIVE:\n case AltitudeMode.CLAMP:\n // flat_array[] の高度要素に現在の標高を設定\n owner.scene.viewer.getExistingElevations( num_points, flat_array, 0, 3, flat_array, 2, 3 );\n\n if ( owner.altitude_mode === AltitudeMode.RELATIVE ) {\n // flat_array[] の高度要素に絶対高度を設定\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] += entries[i].position.altitude;\n }\n }\n break;\n\n default: // AltitudeMode.ABSOLUTE\n // flat_array[] の高度要素に絶対高度を設定\n for ( let i = 0; i < num_points; ++i ) {\n flat_array[3*i + 2] = entries[i].position.altitude;\n }\n break;\n }\n\n return flat_array;\n }\n\n}\n\n\n\n/**\n * @summary 要素\n * @hideconstructor\n * @memberof mapray.ImageIconEntity\n * @public\n */\nclass ImageEntry {\n\n /**\n * @param {mapray.ImageIconEntity} owner 所有者\n * @param {string} image_src アイコン画像\n * @param {mapray.GeoPoint} position 位置\n * @param {object} [props] プロパティ\n * @param {mapray.Vector2} [props.size] アイコンサイズ\n * @param {string} [props.id] Entryを識別するID\n * @param {mapray.Loader.Transform} [props.transform] URL変換関数\n */\n constructor( owner, image_src, position, props )\n {\n this._owner = owner;\n this._position = position.clone();\n\n // animation.BindingBlock\n this._animation = new EasyBindingBlock();\n \n this._setupAnimationBindingBlock();\n\n this._props = Object.assign( {}, props ); // props の複製\n this._copyPropertyVector2f( \"size\" ); // deep copy\n this._copyPropertyVector2f( \"origin\" ); // deep copy\n\n this.setImage( image_src );\n }\n\n /**\n * @summary 位置\n * @type {mapray.GeoPoint}\n * @readonly\n * @package\n */\n get position()\n {\n return this._position;\n }\n\n /**\n * @summary ID\n * @type {string}\n * @readonly\n */\n get id()\n {\n return this._props.hasOwnProperty( \"id\" ) ? this._props.id : \"\";\n }\n\n /**\n * @summary アイコンサイズ (Pixels)\n * @type {mapray.Vector2}\n * @readonly\n * @package\n */\n get size()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return (\n props.size || parent.size ||\n GeoMath.createVector2f( [ this._icon.width, this._icon.height ] )\n );\n }\n\n /**\n * @summary アイコンオリジン位置 (左上を(0, 0)、右下を(1, 1)としする数字を指定する。)\n * @type {mapray.Vector2}\n * @readonly\n * @package\n */\n get origin()\n {\n const props = this._props;\n const parent = this._owner._parent_props;\n return props.origin || parent.origin || ImageIconEntity.DEFAULT_ORIGIN;\n }\n\n /**\n * @summary アニメーションパラメータ設定\n *\n * @type {mapray.animation.BindingBlock}\n * @readonly\n */\n get animation() { return this._animation; }\n \n /**\n * アニメーションの BindingBlock を初期化\n *\n * @private\n */\n _setupAnimationBindingBlock()\n {\n const block = this.animation; // 実体は EasyBindingBlock\n\n const number = Type.find( \"number\" );\n const string = Type.find( \"string\" );\n const vector2 = Type.find( \"vector2\" );\n const vector3 = Type.find( \"vector3\" );\n \n // パラメータ名: image_src\n // パラメータ型: string\n // 画像のパス\n block.addEntry( \"image_src\", [string], null, value => {\n this.setImage( value );\n } );\n\n // パラメータ名: position\n // パラメータ型: vector3\n // ベクトルの要素が longitude, latitude, altitude 順であると解釈\n const position_temp = new GeoPoint();\n\n block.addEntry( \"position\", [vector3], null, value => {\n position_temp.setFromArray( value ); // Vector3 -> GeoPoint\n this.setPosition( position_temp );\n } );\n\n // パラメータ名: size\n // パラメータ型: vector2 | number\n // 型が vector2 のとき アイコンのピクセルサイズX, Y 順であると解釈\n // 型が number のとき アイコンのピクセルサイズX, Y は同値\n const size_temp = GeoMath.createVector2();\n let size_type;\n\n let size_tsolver = curve => {\n size_type = AnimUtil.findFirstTypeSupported( curve, [vector2, number] );\n return size_type;\n };\n\n block.addEntry( \"size\", [vector2, number], size_tsolver, value => {\n if ( size_type === vector2 ) {\n this.setSize( value );\n }\n else { // size_type === number\n size_temp[0] = value;\n size_temp[1] = value;\n this.setSize( size_temp );\n }\n } );\n }\n\n \n /**\n * @summary 画像のパスを設定\n * @param {string} image_src 画像のパス\n */\n setImage( image_src )\n {\n if ( this._image_src !== image_src ) {\n // 画像のパスが変更された\n this._image_src = image_src;\n const resource = (\n image_src instanceof Resource ? image_src:\n new URLResource( image_src, { transform: this._props.transform })\n );\n this._icon = ImageEntry.iconLoader.load( resource );\n this._icon.onEnd(item => {\n this._owner.getPrimitiveProducer()._dirty = true;\n });\n }\n }\n\n /**\n * @summary テキスト原点位置を設定\n *\n * @param {mapray.GeoPoint} position テキスト原点の位置\n */\n setPosition( position )\n {\n if ( this._position.longitude !== position.longitude ||\n this._position.latitude !== position.latitude ||\n this._position.altitude !== position.altitude ) {\n // 位置が変更された\n this._position.assign( position );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n /**\n * @summary アイコンのサイズを指定\n * @param {mapray.Vector2} size アイコンのピクセルサイズ\n */\n setSize( size ) {\n this._setVector2Property( \"size\", size );\n }\n\n /**\n * @private\n */\n _copyPropertyVector3f( name )\n {\n var props = this._props;\n if ( props.hasOwnProperty( name ) ) {\n props[name] = GeoMath.createVector3f( props[name] );\n }\n }\n\n /**\n * @private\n */\n _copyPropertyVector2f( name )\n {\n var props = this._props;\n if ( props.hasOwnProperty( name ) ) {\n if ( typeof( props[name] ) === 'number' ) {\n props[name] = GeoMath.createVector2f( [ props[name], props[name] ] );\n }\n else {\n props[name] = GeoMath.createVector2f( props[name] );\n }\n }\n }\n\n /**\n * @private\n */\n _setVector2Property( name, value )\n {\n var dst = this._props[name];\n if ( !dst ) {\n this._props[name] = GeoMath.createVector2f( value );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n else if ( dst[0] !== value[0] || dst[1] !== value[1] ) {\n GeoMath.copyVector2( value, dst );\n this._owner.getPrimitiveProducer().onChangeChildProperty();\n }\n }\n\n isLoaded() {\n return this._icon.isLoaded();\n }\n\n get icon() {\n return this._icon;\n }\n\n draw( context, x, y, width, height ) {\n this._icon.draw( context, x, y, width, height );\n }\n}\n\nImageIconEntity.ImageEntry = ImageEntry;\n\n\n{\n ImageEntry.iconLoader = new ImageIconLoader();\n}\n\n\n\n/**\n * @summary Pin画像を Canvas 上にレイアウト\n * @memberof mapray.ImageIconEntity\n * @private\n */\nclass Layout {\n\n /**\n * @desc\n * 入力:\n * owner._glenv\n * owner.entity._entries\n * owner._transform\n *\n * @param {PrimitiveProducer} owner 所有者\n * @param {number[]} gocs_array GOCS 平坦化配列\n */\n constructor( owner, gocs_array )\n {\n this._owner = owner;\n this._items = this._createItemList();\n this._is_valid = true;\n\n var row_layouts = this._createRowLayouts();\n if ( row_layouts.length == 0 ) {\n // 有効なテキストが1つも無い\n this._is_valid = false;\n return;\n }\n\n // アイテムの配置の設定とキャンバスサイズの決定\n var size = this._setupLocation( row_layouts );\n\n this._texture = this._createTexture( size.width, size.height );\n this._vertices = this._createVertices( size.width, size.height, gocs_array );\n this._indices = this._createIndices();\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._is_valid;\n }\n\n\n /**\n * @summary テクスチャ\n * @type {mapray.Texture}\n * @readonly\n */\n get texture()\n {\n return this._texture;\n }\n\n /**\n * @summary 頂点配列\n * @desc\n * 条件:\n * this._entries.length > 0\n * 入力:\n * this._entries\n * this._transform\n * @type {Float32Array}\n * @readonly\n */\n get vertices()\n {\n return this._vertices;\n }\n\n\n /**\n * @summary インデックス配列\n * @type {Uint32Array}\n * @readonly\n */\n get indices()\n {\n return this._indices;\n }\n\n\n /**\n * @summary レイアウトアイテムのリストを生成\n * @return {array.}\n * @private\n */\n _createItemList()\n {\n const map = new Map();\n\n const items = [];\n let counter = 0;\n for ( let entry of this._owner.entity._entries ) {\n if ( entry.isLoaded() ) {\n let item = map.get( entry.icon );\n if ( !item ) {\n map.set( entry.icon, item = new LItem( this ) );\n items.push( item );\n }\n item.add( counter++, entry );\n }\n }\n\n return items;\n }\n\n /**\n * @summary RowLayout のリストを生成\n * @return {array.}\n * @private\n */\n _createRowLayouts()\n {\n // アイテムリストの複製\n var items = [].concat( this._items );\n\n // RowLayout 内であまり高さに差が出ないように、アイテムリストを高さで整列\n items.sort( function( a, b ) { return a.height_pixel - b.height_pixel; } );\n\n // リストを生成\n var row_layouts = [];\n while ( items.length > 0 ) {\n var row_layout = new RowLayout( items );\n if ( row_layout.isValid() ) {\n row_layouts.push( row_layout );\n }\n }\n\n return row_layouts;\n }\n\n\n /**\n * @summary テクスチャを生成\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @return {mapray.Texture} テキストテクスチャ\n * @private\n */\n _createTexture( width, height )\n {\n var context = Dom.createCanvasContext( width, height );\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n item.draw( context );\n }\n\n var glenv = this._owner._glenv;\n var opts = {\n usage: Texture.Usage.ICON\n };\n return new Texture( glenv, context.canvas, opts );\n }\n\n\n /**\n * @summary 頂点配列を生成\n *\n * @param {number} width 横幅\n * @param {number} height 高さ\n * @param {number[]} gocs_array GOCS 平坦化配列\n * @return {array.} 頂点配列 [左下0, 右下0, 左上0, 右上0, ...]\n *\n * @private\n */\n _createVertices( width, height, gocs_array )\n {\n var vertices = [];\n\n // テキスト集合の原点 (GOCS)\n var transform = this._owner._transform;\n var xo = transform[12];\n var yo = transform[13];\n var zo = transform[14];\n\n /*\n\n |<----size[0]px---->|\n\n 0-------------------3 ------------------\n | | ^ ^ \n | | | origin[1] | \n | | | | \n | | v | size[1]px\n | o | --- | \n | | ^ | \n | | | 1-origin[1] | \n | | v v \n 1-------------------2 ------------------\n \n | |<----->| 1 - origin[0]\n |<--------->| origin[0]\n */\n\n var xn = 1 / width;\n var yn = 1 / height;\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n for ( var ie = 0; ie < item.entries.length; ie++ ) {\n var eitem = item.entries[ie];\n var entry = eitem.entry;\n var size = entry.size;\n var origin = entry.origin;\n\n // Relativize based on (xo, yo, zo)\n var ibase = eitem.index * 3;\n var xm = gocs_array[ibase] - xo;\n var ym = gocs_array[ibase + 1] - yo;\n var zm = gocs_array[ibase + 2] - zo;\n\n // Image dimensions (Image Coordinate)\n var xc = item.pos_x;\n var yc = item.pos_y;\n var xsize = item.width;\n var ysize = item.height;\n\n // p0\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -origin[0]*size[0], (origin[1])*size[1] ); // a_offset\n vertices.push( xc * xn, 1.0 - yc * yn ); // a_texcoord\n\n // p1\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( -origin[0]*size[0], -(1-origin[1])*size[1] ); // a_offset\n vertices.push( xc * xn, 1 - (yc + ysize) * yn ); // a_texcoord\n\n // p2\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( (1-origin[0])*size[0], -(1-origin[1])*size[1] ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - (yc + ysize) * yn ); // a_texcoord\n\n // p3\n vertices.push( xm, ym, zm ); // a_position\n vertices.push( (1-origin[0])*size[0], origin[1]*size[1] ); // a_offset\n vertices.push( (xc + xsize) * xn, 1 - yc * yn ); // a_texcoord\n }\n }\n\n return vertices;\n }\n\n\n /**\n * @summary インデックス配列を生成\n * @return {array.} インデックス配列 []\n * @private\n */\n _createIndices()\n {\n var indices = [];\n\n var items = this._items;\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n if ( item.is_canceled ) continue;\n\n for ( var ie = 0; ie < item.entries.length; ie++ ) {\n var eitem = item.entries[ie];\n var base = 4 * eitem.index;\n\n var p = base;\n indices.push( p, p+1, p+2 );\n indices.push( p, p+2, p+3 );\n }\n }\n\n return indices;\n }\n\n\n /**\n * @summary アイテムの配置を設定\n * @param {array.} row_layouts\n * @return {object} キャンバスサイズ\n * @private\n */\n _setupLocation( row_layouts )\n {\n var width = 0;\n var height = 0;\n\n height += ImageIconEntity.SAFETY_PIXEL_MARGIN;\n\n for ( var i = 0; i < row_layouts.length; ++i ) {\n var row_layout = row_layouts[i];\n row_layout.locate( height );\n width = Math.max( row_layout.width_assumed, width );\n height += row_layout.height_pixel + ImageIconEntity.SAFETY_PIXEL_MARGIN;\n }\n\n return {\n width: width,\n height: height\n };\n }\n}\n\n\n\n/**\n * @summary レイアウト対象\n * @memberof mapray.ImageIconEntity\n * @private\n */\nclass LItem {\n\n /**\n * @param {mapray.ImageIconEntity.Layout} layout 所有者\n * @param {mapray.ImageIconEntity.Entry} entry ImageIconEntityのエントリ\n */\n constructor( layout )\n {\n this.entries = [];\n\n // テキストの基点\n this._pos_x = 0; // 左端\n this._pos_y = 0; // ベースライン位置\n\n this._height = this._width = null;\n\n this._is_canceled = false;\n }\n\n add( index, entry ) {\n var size = entry.size;\n if ( this._width === null || this._width < size[0] ) this._width = size[0];\n if ( this._height === null || this._height < size[1] ) this._height = size[1];\n this.entries.push( { index, entry } );\n }\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_x()\n {\n return this._pos_x;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get pos_y()\n {\n return this._pos_y;\n }\n\n\n /**\n * @type {number}\n * @readonly\n */\n get width()\n {\n return this._width;\n }\n\n get height()\n {\n return this._height;\n }\n\n\n /**\n * キャンバス上でのテキストの横画素数\n * @type {number}\n * @readonly\n */\n get width_pixel()\n {\n return Math.ceil( this._width );\n }\n\n\n /**\n * キャンバス上でのテキストの縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return Math.ceil( this._height );\n }\n\n\n /**\n * 取り消し状態か?\n * @type {boolean}\n * @readonly\n */\n get is_canceled()\n {\n return this._is_canceled;\n }\n\n\n /**\n * @summary 取り消し状態に移行\n */\n cancel()\n {\n this._is_canceled = true;\n }\n\n\n /**\n * @summary 配置を決定\n * @param {number} x テキスト矩形左辺の X 座標 (キャンバス座標系)\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( x, y )\n {\n this._pos_x = x;\n this._pos_y = y;\n }\n\n draw( context ) {\n\n this.entries[0].entry.draw( context, this._pos_x, this.pos_y, this.width, this.height ); // @Todo: fix this\n\n var RENDER_BOUNDS = false;\n if ( RENDER_BOUNDS ) {\n context.beginPath();\n context.moveTo( this._pos_x , this._pos_y );\n context.lineTo( this._pos_x + this.width, this._pos_y );\n context.lineTo( this._pos_x + this.width, this._pos_y + this.height );\n context.lineTo( this._pos_x , this._pos_y + this.height );\n context.closePath();\n context.stroke();\n }\n }\n}\n\n\n\n/**\n * @summary 水平レイアウト\n * @memberof mapray.ImageIconEntity\n * @private\n */\nclass RowLayout {\n\n /**\n * @desc\n * レイアウトされた、またはレイアウトに失敗したアイテムは src_items から削除される。
\n * レイアウトに失敗したアイテムは取り消し (is_canceled) になる。
\n * @param {array.} src_items アイテムリスト\n */\n constructor( src_items )\n {\n var width_assumed_total = 0;\n var height_pixel_max = 0;\n var row_items = [];\n\n width_assumed_total += ImageIconEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n while ( src_items.length > 0 ) {\n var item = src_items.shift();\n var width_assumed = item.width_pixel + ImageIconEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n\n if ( width_assumed_total + width_assumed <= ImageIconEntity.MAX_IMAGE_WIDTH ) {\n // 行にアイテムを追加\n row_items.push( item );\n width_assumed_total += width_assumed;\n height_pixel_max = Math.max( item.height_pixel, height_pixel_max );\n }\n else {\n if ( row_items.length == 0 ) {\n // テキストが長すぎて表示できない\n item.cancel();\n }\n else {\n // 次の行になるため差し戻して終了\n src_items.unshift( item );\n break;\n }\n }\n }\n\n this._items = row_items;\n this._width_assumed = width_assumed_total;\n this._height_pixel = height_pixel_max;\n }\n\n\n /**\n * @summary 有効なオブジェクトか?\n * @desc\n * 無効のとき、他のメソッドは呼び出せない。
\n * @return {boolean} 有効のとき true, 無効のとき false\n */\n isValid()\n {\n return this._items.length > 0;\n }\n\n\n /**\n * \n * @type {array.}\n * @readonly\n */\n get items()\n {\n return this._items;\n }\n\n\n /**\n * キャンバス上での行の横占有画素数\n * @type {number}\n * @readonly\n */\n get width_assumed()\n {\n return this._width_assumed;\n }\n\n\n /**\n * キャンバス上での行の縦画素数\n * @type {number}\n * @readonly\n */\n get height_pixel()\n {\n return this._height_pixel;\n }\n\n\n /**\n * @summary レイアウトの配置を決定\n * @param {number} y テキスト矩形上辺の Y 座標 (キャンバス座標系)\n */\n locate( y )\n {\n var items = this._items;\n var x = 0;\n\n x += ImageIconEntity.SAFETY_PIXEL_MARGIN; // 左マージン\n\n for ( var i = 0; i < items.length; ++i ) {\n var item = items[i];\n item.locate( x, y );\n x += item.width_pixel + ImageIconEntity.SAFETY_PIXEL_MARGIN; // テキスト幅 + 右マージン\n }\n }\n\n}\n\n\n\n\nexport default ImageIconEntity;\n","var DESCRIPTORS = require('../internals/descriptors');\nvar objectKeys = require('../internals/object-keys');\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar propertyIsEnumerable = require('../internals/object-property-is-enumerable').f;\n\n// `Object.{ entries, values }` methods implementation\nvar createMethod = function (TO_ENTRIES) {\n return function (it) {\n var O = toIndexedObject(it);\n var keys = objectKeys(O);\n var length = keys.length;\n var i = 0;\n var result = [];\n var key;\n while (length > i) {\n key = keys[i++];\n if (!DESCRIPTORS || propertyIsEnumerable.call(O, key)) {\n result.push(TO_ENTRIES ? [key, O[key]] : O[key]);\n }\n }\n return result;\n };\n};\n\nmodule.exports = {\n // `Object.entries` method\n // https://tc39.github.io/ecma262/#sec-object.entries\n entries: createMethod(true),\n // `Object.values` method\n // https://tc39.github.io/ecma262/#sec-object.values\n values: createMethod(false)\n};\n","var $ = require('../internals/export');\nvar $values = require('../internals/object-to-array').values;\n\n// `Object.values` method\n// https://tc39.github.io/ecma262/#sec-object.values\n$({ target: 'Object', stat: true }, {\n values: function values(O) {\n return $values(O);\n }\n});\n","import Loader from \"./Loader\"\nimport GeoMath from \"./GeoMath\";\nimport GeoPoint from \"./GeoPoint\";\nimport CredentialMode from \"./CredentialMode\";\nimport MarkerLineEntity from \"./MarkerLineEntity\";\nimport PolygonEntity from \"./PolygonEntity\";\nimport PinEntity from \"./PinEntity\";\nimport Resource, { URLResource, ResourceType } from \"./Resource\";\nimport AltitudeMode from \"./AltitudeMode\";\n\n/**\n * GeoJSON形式(rfc7946)のデータをシーンに読み込みます。\n * @memberof mapray\n */\nclass GeoJSONLoader extends Loader {\n\n /**\n * @desc\n * url で指定したシーンデータの読み込みを開始し、scene にエンティティを構築する。
\n * 読み込みが終了したとき options.callback を呼び出す。
\n * @param {mapray.Scene} scene 読み込み先のシーン\n * @param {string} resource シーンファイルの URL\n * @param {object} [options] オプション集合\n * @param {mapray.Loader.TransformCallback} [options.transform] リソース要求変換関数\n * @param {mapray.GeoJSONLoader.FinishCallback} [options.callback] 終了コールバック関数\n * @param {mapray.Loader.EntityCallback} [options.onEntity] エンティティコールバック関数\n */\n constructor( scene, resource, options={} )\n {\n if (resource instanceof Resource) {\n // OK\n }\n else if ( typeof resource === \"string\" ) {\n resource = new URLResource(resource, {\n type: \"json\",\n transform: options.transform\n });\n }\n else {\n throw new Error( \"Unsupported Resource: \" + resource);\n }\n\n super( scene, resource, {\n onEntity: options.onEntity,\n onLoad: options.onLoad\n } );\n\n // PinEntity\n this._getPointFGColor = options.getPointFGColor || defaultGetPointFGColorCallback;\n this._getPointBGColor = options.getPointBGColor || defaultGetPointBGColorCallback;\n this._getPointSize = options.getPointSize || defaultGetPointSizeCallback;\n this._getPointIconId = options.getPointIconId || defaultGetPointIconIdCallback;\n\n // MarkerLineEntity\n this._getLineColor = options.getLineColor || defaultGetLineColorCallback;\n this._getLineWidth = options.getLineWidth || defaultGetLineWidthCallback;\n\n // PolygonEntity\n this._getFillColor = options.getFillColor || defaultGetFillColorCallback;\n this._getExtrudedHeight = options.getExtrudedHeight || defaultGetExtrudedHeightCallback;\n\n // Common\n this._getAltitudeMode = options.getAltitudeMode || defaultGetAltitudeModeCallback;\n this._getAltitude = options.getAltitude || defaultGetAltitudeCallback;\n\n this._glenv = scene.glenv;\n this._references = {};\n this._cancelled = false;\n this._finished = false;\n }\n\n\n /**\n * @summary 読み込み処理の実態。継承クラスによって実装される。\n * @private\n */\n _load()\n {\n return (\n this._resource.load( { type: ResourceType.JSON } )\n .then( geoJson => {\n // JSON データの取得に成功\n this._check_cancel();\n this._load_geojson_object( geoJson );\n } )\n );\n }\n\n\n /**\n * Load GeoJSON Object\n * @private\n */\n _load_geojson_object( geojson )\n {\n var success;\n if ( geojson.type === TYPES.FEATURE_COLLECTION ) {\n var features = geojson.features;\n success = false;\n for ( var i = 0, len = features.length; i < len; i++ ) {\n var feature = features[i];\n var s = this._load_geojson_object( feature.featureId ? feature.feature : feature ); // @ToDo: Unknown\n // var s = this._load_geojson_object( feature );\n if (s && !success) success = s;\n }\n }\n else if ( geojson.type === TYPES.FEATURE ) {\n var geometry = geojson.geometry;\n success = this._load_geometry_object( geometry, geojson );\n }\n else if ( SUPPORTED_GEOMETRY_TYPES.indexOf( geojson.type ) !== -1 ) {\n success = this._load_geometry_object( geojson, null );\n }\n else {\n throw new Error( \"Unnsupported Type: \" + geojson.type );\n }\n \n if ( this._cancelled ) return false;\n\n return success;\n }\n\n\n /**\n * Load Geometry Object\n * @private\n */\n _load_geometry_object( geometry, geojson={} ) {\n var coords = geometry.coordinates;\n\n if (!coords && !geometry) {\n return false;\n }\n \n switch ( geometry.type ) {\n case GEOMETRY_TYPES.POINT:\n case GEOMETRY_TYPES.MULTI_POINT:\n return this._loadPoint( geometry, geojson );\n\n case GEOMETRY_TYPES.LINE_STRING:\n case GEOMETRY_TYPES.MULTI_LINE_STRING:\n return this._loadLines( geometry, geojson );\n\n case GEOMETRY_TYPES.POLYGON:\n case GEOMETRY_TYPES.MULTI_POLYGON:\n return this._loadPolygons( geometry, geojson );\n\n case GEOMETRY_TYPES.GEOMETRY_COLLECTION:\n return true;\n\n default:\n throw new Error( \"Invalid GeoJSON type: \" + geometry.type );\n }\n }\n\n\n /**\n * fetch() の init 引数に与えるオブジェクトを生成\n * @private\n */\n _make_fetch_params( tr )\n {\n var init = {\n signal: this._abort_ctrl.signal,\n credentials: (tr.credentials || CredentialMode.OMIT).credentials\n };\n\n if ( tr.headers ) {\n init.headers = (tr.headers || GeoJSONLoader._defaultHeaders);\n }\n\n return init;\n }\n\n\n /**\n * @private\n */\n _loadLines( geometry, geojson ) \n {\n const color4 = this._getLineColor( geojson );\n const width = this._getLineWidth( geojson );\n const altitude = this._getAltitude( geojson );\n const altitude_mode = this._getAltitudeMode( geojson );\n \n if ( !geometry || color4.length !== 4 ) {\n return false;\n }\n var type = geometry.type;\n var coords = geometry.coordinates;\n var rgb = color4.slice(0, 3);\n var alpha = color4[3];\n\n // If multiline, split entity\n if ( type === GEOMETRY_TYPES.MULTI_LINE_STRING ) {\n coords.forEach( points => {\n if ( !this._generateLine( points, width, rgb, alpha, altitude_mode, altitude, geojson ) ) {\n return false;\n }\n });\n return true;\n }\n else { // type === GEOMETRY_TYPES.LINE_STRING\n return this._generateLine( coords, width, rgb, alpha, altitude_mode, altitude, geojson )\n }\n }\n\n\n /**\n * @private\n */\n _generateLine( points, width, color, opaticy, altitude_mode, altitude, geojson )\n {\n if ( !points ) {\n return false;\n }\n\n var entity = new MarkerLineEntity( this._scene );\n entity.altitude_mode = altitude_mode;\n var fp = this._flatten( points, altitude );\n entity.addPoints( fp );\n entity.setLineWidth( width );\n entity.setColor( color );\n entity.setOpacity( opaticy );\n this._onEntity( this, entity, geojson );\n\n return true;\n }\n\n\n /**\n * @private\n */\n _loadPoint( geometry, geojson )\n {\n const fgColor = this._getPointFGColor( geojson );\n const bgColor = this._getPointBGColor( geojson );\n const iconId = this._getPointIconId( geojson );\n const size = this._getPointSize( geojson );\n const altitude_mode = this._getAltitudeMode( geojson );\n const altitude = this._getAltitude( geojson );\n \n if ( !geometry ) {\n return false;\n }\n var type = geometry.type;\n\n\n var props = {\n \"fg_color\": fgColor.slice( 0, 3 ),\n \"bg_color\": bgColor.slice( 0, 3 ),\n size: size,\n };\n\n // If multiline, split entity\n if ( type === GEOMETRY_TYPES.POINT ) {\n var entity = new PinEntity( this._scene );\n entity.altitude_mode = altitude_mode;\n var alt = this._getActualValue( altitude, geometry.coordinates[2], GeoJSONLoader.defaultAltitude );\n var coords = new GeoPoint( geometry.coordinates[0], geometry.coordinates[1], alt );\n if ( iconId !== null ) {\n entity.addMakiIconPin( iconId, coords, props );\n }\n else {\n entity.addPin( coords, props );\n }\n this._onEntity( this, entity, geojson );\n }\n else { // type === GEOMETRY_TYPES.MULTI_POINT\n var entity = new PinEntity( this._scene );\n entity.altitude_mode = altitude_mode;\n for ( var i = 0; i < geometry.coordinates.length; i++ ) {\n var targetCoordinates = geometry.coordinates[i];\n var alt = this._getActualValue( altitude, geometry.coordinates[2], GeoJSONLoader.defaultAltitude );\n var coords = new GeoPoint( targetCoordinates[0], targetCoordinates[1], alt );\n if ( iconId !== null ) {\n entity.addMakiIconPin( iconId, coords, props );\n // entity.addPin( coords, props );\n }\n else {\n entity.addPin( coords, props );\n }\n }\n this._onEntity( this, entity, geojson );\n }\n\n return true;\n }\n\n\n /**\n * @private\n */\n _loadPolygons( geometry, geojson )\n {\n const color4 = this._getFillColor( geojson );\n const altitude_mode = this._getAltitudeMode( geojson );\n const altitude = this._getAltitude( geojson );\n const extruded_height = this._getExtrudedHeight( geojson );\n\n if ( !geometry || color4.length !== 4 ) {\n return false;\n }\n var type = geometry.type;\n var coords = geometry.coordinates;\n var rgb = color4.slice(0, 3);\n var alpha = color4[3];\n\n // If multiline, split entity\n if ( type === GEOMETRY_TYPES.MULTI_POLYGON ) {\n coords.forEach( points => {\n if ( !this._generatePolygon( points, rgb, alpha, altitude_mode, altitude, extruded_height, geojson ) ) {\n return false;\n }\n });\n return true;\n }\n else { // type === GEOMETRY_TYPES.POLYGON\n return this._generatePolygon( coords, rgb, alpha, altitude_mode, altitude, extruded_height, geojson )\n }\n }\n\n\n /**\n * @private\n */\n _generatePolygon( pointsList, color, opaticy, altitude_mode, altitude, extruded_height, geojson ) \n {\n if ( !pointsList ) {\n return false;\n }\n\n const entity = new PolygonEntity( this._scene );\n entity.altitude_mode = altitude_mode;\n entity.extruded_height = extruded_height;\n entity.setColor( color );\n entity.setOpacity( opaticy );\n for ( let i=0; i< pointsList.length; i++ ) {\n const fp = this._flatten( pointsList[ i ], altitude, pointsList[ i ].length-1 );\n if ( !fp ) return false;\n if ( i === 0 ) entity.addOuterBoundary( fp );\n else entity.addInnerBoundary( fp );\n }\n this._onEntity( this, entity, geojson );\n \n return true;\n }\n\n\n /**\n * @private\n */\n _getActualValue( valueFromCallback, valueInGeoJSON, defaultValue ) {\n return (\n valueFromCallback != null ? valueFromCallback: // value from callback is the most prioritized\n valueInGeoJSON != null ? valueInGeoJSON: // value in GeoJSON will be used if defined\n defaultValue // default value\n );\n }\n\n\n /**\n * @private\n */\n _flatten( ary, altitude, len=ary.length )\n {\n return ary.reduce( (p, c, i) => (\n i >= len ? p :\n p.concat( c.slice(0, 2), this._getActualValue( altitude, c[2], GeoJSONLoader.defaultAltitude ) )\n ), [] );\n };\n}\n\n\n\n{\n GeoJSONLoader._defaultHeaders = {};\n GeoJSONLoader.defaultLineColor = [0, 0, 0, 1];\n GeoJSONLoader.defaultFillColor = [0, 0, 0, 1];\n GeoJSONLoader.defaultLineWidth = 1;\n GeoJSONLoader.defaultPointFGColor = [1.0, 1.0, 1.0];\n GeoJSONLoader.defaultPointBGColor = [0.35, 0.61, 0.81];\n GeoJSONLoader.defaultPointSize = 30;\n GeoJSONLoader.defaultPointIconId = null;\n GeoJSONLoader.defaultAltitude = 0.0;\n GeoJSONLoader.defaultExtrudedHeight = 0.0;\n}\n\n\n\nfunction defaultGetLineColorCallback( geojson )\n{\n return GeoJSONLoader.defaultLineColor;\n}\n\nfunction defaultGetLineWidthCallback( geojson ) \n{\n return GeoJSONLoader.defaultLineWidth;\n}\n\nfunction defaultGetFillColorCallback( geojson )\n{\n return GeoJSONLoader.defaultFillColor;\n}\n\n\nfunction defaultGetPointFGColorCallback( geojson )\n{\n return GeoJSONLoader.defaultPointFGColor;\n}\n\nfunction defaultGetPointBGColorCallback( geojson )\n{\n return GeoJSONLoader.defaultPointBGColor;\n}\n\nfunction defaultGetPointSizeCallback( geojson )\n{\n return GeoJSONLoader.defaultPointSize;\n}\n\nfunction defaultGetPointIconIdCallback( geojson )\n{\n return GeoJSONLoader.defaultPointIconId;\n}\n\n\nfunction defaultGetAltitudeModeCallback( geojson )\n{\n return AltitudeMode.ABSOLUTE;\n}\nfunction defaultGetAltitudeCallback( geojson )\n{\n return null;\n}\n\nfunction defaultGetExtrudedHeightCallback( geojson )\n{\n return GeoJSONLoader.defaultExtrudedHeight;\n}\n\n\nvar TYPES = {\n FEATURE: \"Feature\",\n FEATURE_COLLECTION: \"FeatureCollection\",\n};\n\nvar GEOMETRY_TYPES = {\n POINT: \"Point\",\n MULTI_POINT: \"MultiPoint\",\n LINE_STRING: \"LineString\",\n MULTI_LINE_STRING: \"MultiLineString\",\n POLYGON: \"Polygon\",\n MULTI_POLYGON: \"MultiPolygon\",\n GEOMETRY_COLLECTION: \"GeometryCollection\",\n FEATURE: \"Feature\"\n};\n\nvar SUPPORTED_GEOMETRY_TYPES = Object.values( GEOMETRY_TYPES );\n\nexport default GeoJSONLoader;\n","/**\n * @summary デバッグ統計\n * @classdesc\n * エンジン開発用の統計オブジェクトである。
\n *
NOTE: オブジェクトの振舞いはエンジンの実装に依存するため、一般アプリの開発では使用できない。
\n * @memberof mapray\n */\nclass DebugStats {\n\n /**\n */\n constructor()\n {\n /**\n * @summary リクエスト待ちの DEM 数\n * @member mapray.DebugStats#num_wait_reqs_dem\n * @type {number}\n */\n\n /**\n * @summary リクエスト待ちの画像数\n * @member mapray.DebugStats#num_wait_reqs_img\n * @type {number}\n */\n\n /**\n * @summary 描画地表断片数\n * @member mapray.DebugStats#num_drawing_flakes\n * @type {number}\n */\n\n /**\n * @summary 描画地表断頂点数\n * @member mapray.DebugStats#num_drawing_flake_vertices\n * @type {number}\n */\n\n /**\n * @summary 地表断片処理 A の数\n * @member mapray.DebugStats#num_procA_flakes\n * @type {number}\n */\n\n /**\n * @summary 地表断片処理 B の数\n * @member mapray.DebugStats#num_procB_flakes\n * @type {number}\n */\n\n this.clearStats();\n }\n\n\n /**\n * 統計値をクリア\n * @package\n */\n clearStats()\n {\n this.num_wait_reqs_dem = 0;\n this.num_wait_reqs_img = 0;\n this.num_drawing_flakes = 0;\n this.num_drawing_flake_vertices = 0;\n this.num_procA_flakes = 0;\n this.num_procB_flakes = 0;\n }\n\n\n /**\n * @summary 更新が完了したときに呼び出される\n * @abstract\n */\n onUpdate()\n {\n }\n\n}\n\n\nexport default DebugStats;\n","var global = require('../internals/global');\nvar trim = require('../internals/string-trim').trim;\nvar whitespaces = require('../internals/whitespaces');\n\nvar $parseInt = global.parseInt;\nvar hex = /^[+-]?0[Xx]/;\nvar FORCED = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;\n\n// `parseInt` method\n// https://tc39.github.io/ecma262/#sec-parseint-string-radix\nmodule.exports = FORCED ? function parseInt(string, radix) {\n var S = trim(String(string));\n return $parseInt(S, (radix >>> 0) || (hex.test(S) ? 16 : 10));\n} : $parseInt;\n","var $ = require('../internals/export');\nvar parseIntImplementation = require('../internals/number-parse-int');\n\n// `parseInt` method\n// https://tc39.github.io/ecma262/#sec-parseint-string-radix\n$({ global: true, forced: parseInt != parseIntImplementation }, {\n parseInt: parseIntImplementation\n});\n","'use strict';\nvar $ = require('../internals/export');\nvar getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor').f;\nvar toLength = require('../internals/to-length');\nvar notARegExp = require('../internals/not-a-regexp');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\nvar correctIsRegExpLogic = require('../internals/correct-is-regexp-logic');\nvar IS_PURE = require('../internals/is-pure');\n\nvar nativeEndsWith = ''.endsWith;\nvar min = Math.min;\n\nvar CORRECT_IS_REGEXP_LOGIC = correctIsRegExpLogic('endsWith');\n// https://github.com/zloirock/core-js/pull/702\nvar MDN_POLYFILL_BUG = !IS_PURE && !CORRECT_IS_REGEXP_LOGIC && !!function () {\n var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith');\n return descriptor && !descriptor.writable;\n}();\n\n// `String.prototype.endsWith` method\n// https://tc39.github.io/ecma262/#sec-string.prototype.endswith\n$({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {\n endsWith: function endsWith(searchString /* , endPosition = @length */) {\n var that = String(requireObjectCoercible(this));\n notARegExp(searchString);\n var endPosition = arguments.length > 1 ? arguments[1] : undefined;\n var len = toLength(that.length);\n var end = endPosition === undefined ? len : min(toLength(endPosition), len);\n var search = String(searchString);\n return nativeEndsWith\n ? nativeEndsWith.call(that, search, end)\n : that.slice(end - search.length, end) === search;\n }\n});\n","import GeoPoint from \"./GeoPoint\";\n\n/**\n * @summary データセットを表現する抽象クラス\n */\nexport class AbstractDataset {\n\n /**\n * @param {MaprayApi} api\n */\n constructor( api ) {\n this._aip = api;\n }\n\n /**\n * @summary データセットのidを取得\n * @return {string}\n */\n getId() {\n return this._id;\n }\n\n /**\n * @summary オーナーのidを取得\n * @return {string}\n */\n getOwnerId() {\n return this._owner_id;\n }\n\n /**\n * @summary 名前を取得\n * @return {string}\n */\n getName() {\n return this._name;\n }\n\n /**\n * @summary 説明を取得\n * @return {string}\n */\n getDescription() {\n return this._description;\n }\n\n /**\n * @summary 作成日時を取得\n * @return {Date}\n */\n getCreatedAt() {\n return this._created_at;\n }\n\n /**\n * @summary 更新日時を取得\n * @return {Date}\n */\n getUpdatedAt() {\n return this._updated_at;\n }\n\n /**\n * @private\n * @param {json} サーバから返却されたjson\n */\n _restoreFromJson( json ) {\n this._id = json.id;\n this._owner_id = json.owner_id;\n this._name = json.name;\n this._description = json.description;\n this._created_at = new Date(json.created_at);\n this._updated_at = new Date(json.updated_at);\n }\n}\n\n\n\n/**\n * @summary データセットを表現するクラス\n */\nexport class Dataset extends AbstractDataset {\n\n /**\n * @param {MaprayApi} api\n */\n constructor( api ) {\n super( api );\n }\n\n /**\n * @private\n * @param {json} サーバから返却されたjson\n */\n _restoreFromJson( json ) {\n super._restoreFromJson( json );\n }\n\n /**\n * @private\n * @param {MaprayApi} api\n * @param {json} サーバから返却されたjson\n * @return {Dataset}\n */\n static createFromJson( api, json ) {\n const dataset = new Dataset( api );\n dataset._restoreFromJson( json );\n return dataset;\n }\n}\n\n\n\n/**\n * @summary 3Dデータセットを表現するクラス\n */\nexport class Dataset3D extends AbstractDataset {\n\n /**\n * @param {MaprayApi} api\n */\n constructor( api ) {\n super( api );\n }\n\n /**\n * @summary 原点位置\n * @return {mapray.GeoPoint}\n */\n getOrigin() {\n return this._origin;\n }\n\n /**\n * @summary モデルが公開されているURL\n * @return {string}\n */\n getUrl() {\n return this._url;\n }\n\n /**\n * @summary フォーマット\n * @private\n * @return {string}\n */\n getFormat() {\n return this._format;\n }\n\n /**\n * @summary シーンID\n * @private\n * @return {string}\n */\n getSceneId() {\n return this._scene_id;\n }\n\n /**\n * @summary Path\n * @private\n * @return {string}\n */\n getPath() {\n return this._path;\n }\n\n /**\n * @summary SRID\n * @private\n * @return {string}\n */\n getSRID() {\n return this._srid;\n }\n\n /**\n * @private\n * @param {json} サーバから返却されたjson\n */\n _restoreFromJson( json ) {\n /* missing options\n \"x\": 137.715,\n \"y\": 34.71111,\n \"z\": 0,\n \"roll\": 0,\n \"tilt\": 0,\n \"heading\": 0,\n \"sx\": 1,\n \"sy\": 1,\n \"sz\": 1,\n \"offset_x\": 0,\n \"offset_y\": 0,\n \"offset_z\": 0,\n \"offset_roll\": 0,\n \"offset_tilt\": 0,\n \"offset_heading\": 0,\n \"offset_sx\": 1,\n \"offset_sy\": 1,\n \"offset_sz\": 1,\n \"altitude_mode\": \"absolute\",\n */\n super._restoreFromJson( json );\n this._url = json.url;\n this._scene_id = json.scene_id;\n this._path = json.path;\n this._format = json.format;\n this._srid = json.srid;\n this._origin = new GeoPoint(json.x, json.y, json.z);\n }\n\n /**\n * @private\n * @param {MaprayApi} api\n * @param {json} サーバから返却されたjson\n * @return {Dataset3D}\n */\n static createFromJson( api, json ) {\n const dataset = new Dataset3D( api );\n dataset._restoreFromJson( json );\n return dataset;\n }\n}\n\n\n\n/**\n * @summary 点群データセットを表現するクラス\n */\nexport class PointCloudDataset extends AbstractDataset {\n\n /**\n * @param {MaprayApi} api\n */\n constructor( api ) {\n super( api );\n }\n\n /**\n * @summary 点群ファイルが公開されているURLを取得\n */\n getUrl() {\n return this._url;\n }\n\n /**\n * @summary 点群のバウンディングボックスを取得\n */\n getBoundingBox() {\n return this._bounding_box;\n }\n\n /**\n * @summary 1レベルに1ボックスしか存在しないボックスの中で最も高いレベルのボックス。\n * (点群に含まれる全ての点を包含するボックスの中で最も高いレベルのボックス)\n * @return string\n */\n getContentRoot() {\n return this._content_root;\n }\n\n /**\n * @summary フォーマット(現在はrawのみ対応)\n * @return string\n */\n getFormat() {\n return this._format;\n }\n\n /**\n * @private\n * @param {json} サーバから返却されたjson\n */\n _restoreFromJson( json ) {\n super._restoreFromJson( json );\n // this._srid = json.srid;\n this._url = json.url;\n this._bounding_box = json.bbox;\n this._content_root = Array.isArray(json.content_root) ? json.content_root.join(\"/\") : json.content_root;\n this._format = json.format;\n }\n\n /**\n * @private\n * @param {MaprayApi} api\n * @param {json} サーバから返却されたjson\n * @return {PointCloudDataset}\n */\n static createFromJson( api, json ) {\n const dataset = new PointCloudDataset( api );\n dataset._restoreFromJson( json );\n return dataset;\n }\n}\n","import HTTP from \"./HTTP\";\nimport Dom from \"./util/Dom\";\nimport Resource, { ResourceType } from \"./Resource\";\n\n\n\n/**\n * @summary Mapray Cloudに登録されたデータにおいて、URLアクセスを要するリソースを表現する。\n *
\n * - index.htmlのように基準となるファイルを指定し、そのファイルからの相対パスでサブリソースへアクセスする。\n *
- \n * コンストラクタで基準となるファイルを指定し、load()はこのファイルを読み込む。\n * loadSubResource( sub_url )は、sub_urlが相対パスの場合は基準となるファイルからの相対パスとして解釈される。\n *
- ルートパスを指定し配下のリソースへアクセスする。\n *
- \n * コンストラクタで基準となるURLを指定する。この時、URLは必ず/で終了する必要があり、load()は動作が定義されない。\n * loadSubResource( sub_url )は、sub_urlが相対パスの場合は基準となるURLからの相対パスとして解釈される。\n *
\n * @private\n */\nclass ApiUrlResource extends Resource {\n\n /**\n * @param {MaprayApi} api\n * @param {string} url\n */\n constructor( api, url ) {\n const index = url.lastIndexOf( \"/\" );\n if ( index === -1 ) throw new Error( \"invalid url\" );\n //super( api, url.substr( 0, index + 1 ) );\n super();\n this._api = api;\n this._url = url;\n this._base_url = url.substr( 0, index + 1 )\n }\n\n /**\n * @param {object} options\n * @override\n */\n async load( options={} ) {\n const response = await this._api.fetch( HTTP.METHOD.GET, this._url );\n return (\n options.type === ResourceType.JSON ? await response.json():\n response\n );\n }\n\n /**\n * @override\n */\n loadSubResourceSupported() {\n return true;\n }\n\n /**\n * @summary リソースにアクセスする。sub_urlは相対・絶対の両方に対応。\n * @override\n * @param {string} sub_url\n * @return {Resource}\n */\n async loadSubResource( sub_url, options={} ) {\n const url = Dom.resolveUrl( this._base_url, sub_url );\n const response = await this._api.fetch( HTTP.METHOD.GET, url );\n if ( !response.ok ) throw new Error( response.statusText );\n return (\n options.type === ResourceType.BINARY ? await response.arrayBuffer():\n options.type === ResourceType.IMAGE ? await Dom.loadImage( await response.blob() ):\n response\n );\n }\n}\n\n\n\n/**\n * Mapray Cloudに登録されたDatasetを表現するリソース。\n */\nexport class DatasetResource extends Resource {\n constructor( api, datasetId ) {\n super();\n this._api = api;\n this._datasetId = datasetId;\n }\n\n /**\n * @return {Promise(object)} データ(geojson)\n */\n async load() {\n return await this._api.getFeatures( this._datasetId );\n }\n}\n\n\n\n/**\n * Mapray Cloudに登録された3DDatasetのモデルを表現するリソース。\n */\nexport class Dataset3DSceneResource extends Resource {\n\n /**\n * @param {MaprayApi} api\n * @param {string|string[]} datasetIds データセットのid。複数指定する場合は配列を指定する。\n */\n constructor( api, datasetIds ) {\n super();\n this._api = api;\n this._datasetIds = Array.isArray( datasetIds ) ? datasetIds : [ datasetIds ];\n }\n\n /**\n * @return {Promise(object)} シーンファイル(json)\n */\n async load() {\n return await this._api.get3DDatasetScene( this._datasetIds );\n }\n\n /**\n * @protected\n */\n resolveResourceSupported() {\n return true;\n }\n\n /**\n * @summary シーンファイルに含まれるモデル及びモデルに関連づけられたリソースへアクセス際に利用されるResource。\n * @param {string} sub_url モデルURL\n * @return {Resource} \n */\n resolveResource( sub_url ) {\n return new ApiUrlResource( this._api, sub_url );\n }\n}\n\n\n\n/**\n * Mapray Cloudに登録されたPoint Cloud Datasetを表現するリソース。\n */\nexport class PointCloudDatasetResource extends Resource {\n\n /**\n * @param {MaprayApi} api\n * @param {string} datasetId データセットのid\n */\n constructor( api, datasetId ) {\n super();\n this._api = api;\n this._datasetId = datasetId;\n }\n\n /**\n * @return {Promise