プロを目指す人のためのTypeScript入門、読書メモ

忘れそうなことメモ

  • TypeScript の数値はIEEE 754 倍精度浮動小数点数

    • 数値(仮数部)の精度が53 ビット
  • Falsyな値は、0, NaN, 0n, "", null, undefinedのみ。

  • 短絡評価

    • x || y は、xがfalsyのときのみyが評価される
    • x ?? y は、xnullまたはundefinedのときのみyが評価される。(空文字列を有効としたい場合)
    • x && yは、xがtruelyのときのみyが評価される。

ES2021

しおり

3.1から

ArcGIS Javascript APIでのLat, LngとXYZとの相互変換

距離を考慮した計算が必要なときは、XY座標系に直してから計算しないと多分だめなので、 現在表示している地図上のXY座標系と、Lat, Lng(緯度経度)とを相互変換する方法を調べてみました。

緯度経度からXY座標へ

import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils";

webMercatorUtils.lngLatToXY(lng, lat)

自分の環境だとXY座標の単位はメートルでした。

XY座標から緯度経度へ

import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils";

webMercatorUtils.xyToLngLat(x, y)

表示画面内の変換

function xyToLatLng(view: SceneView, x: number, y: number) {
  const pt = view.toMap({ x, y });
  const { longitude, latitude } = pt;
  return { longitude, latitude };
}

function latLngToXY(view: SceneView, latitude: number, longitude: number) {
  const { x, y } = view.toScreen(new Point({ latitude, longitude }));
  return {x, y};
}

ブラウザ内に表示している範囲においてのみ変換ができましたが、 画面範囲になると負の値になったり、nullが帰ってきたりでうまく機能しないことがあります。

References

developers.arcgis.com developers.arcgis.com

とげとげジオメトリの生成

コードはこんな感じ。正二十面体の各面に対して、まず面の法線と重心を求めて、重心から法線方向に一定値delta進めたところに一つ頂点をを生やした感じ。

  function generateNeedlesGeometry(size: number) {
    const delta = size * 2.0;

    const geometry = new THREE.IcosahedronGeometry(size, 0);
    const positions = geometry.attributes.position;
    const { array, itemSize } = positions;
  
    const bufArray: number[] = [];
  
    const v1 = new THREE.Vector3();
    const v2 = new THREE.Vector3();
    const v3 = new THREE.Vector3();
  
    for (let i = 0, j = 0; i < array.length; i += itemSize, j++) {
      const x = array[i];
      const y = array[i+1];
      const z = array[i+2];
  
      if (j == 0) {
        v1.set(x, y, z);
      } else if (j == 1) {
        v2.set(x, y, z);
      } else if (j == 2) {
        v3.set(x, y, z);
        
        const v12 = new THREE.Vector3().subVectors(v2, v1);
        const v23 = new THREE.Vector3().subVectors(v3, v2);
        const vn = new THREE.Vector3().crossVectors(v12, v23).normalize();
        vn.multiplyScalar(delta);
  
        const vg = new THREE.Vector3();
        vg.add(v1);
        vg.add(v2);
        vg.add(v3);
        vg.multiplyScalar(1/3);
  
        vg.add(vn);
        
        bufArray.push(v1.x, v1.y, v1.z);
        bufArray.push(v2.x, v2.y, v2.z);
        bufArray.push(vg.x, vg.y, vg.z);
  
        bufArray.push(v2.x, v2.y, v2.z);
        bufArray.push(v3.x, v3.y, v3.z);
        bufArray.push(vg.x, vg.y, vg.z);
  
        bufArray.push(v3.x, v3.y, v3.z);
        bufArray.push(v1.x, v1.y, v1.z);
        bufArray.push(vg.x, vg.y, vg.z);
  
        j = 0;
      }
    }

    const buf = new THREE.BufferGeometry();
    buf.setAttribute( 'position', new THREE.Float32BufferAttribute( bufArray, 3 ) );
    return buf;
  }

Viteでthreejsを使うときのメモ

Viteはthreejsで使うときも、基本、そのまま使えますが、 多少のご作法は理解しておく必要がありましたので、メモを残しておきます。

JSのコード中で読み込むアセットの取り扱い

jsでimportなどせずにパスやURLを指定して読み込むテクスチャやフォントなどです。 これらのファイルは、publicフォルダを作ってそこに格納します。 publicに格納しておくことで、build時にdistフォルダに自動的にコピーされます。 (publicに格納していないとbuild後のjsからそのままではアクセスできなくなります。)

jsからのアクセスするときには、publicを除いて記載すればよいです。 たとえば、public/textures/1.pngのファイルには、以下のように/textures/1.pngへアクセスすればよいです。

const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('/textures/matcaps/1.png')"/textures/1.png"

ForgeViewerでの同一モデルの複数配置

Extensionなどのコードを読んでやりたいことの実現方法を調べた結果をまとめています。 どこまでが公式に保証されている動作かは把握できていないので、今後のバージョンで動く保証はできないです。

同一モデルの複数配置

同一モデルを位置を変えて複数配置したい場合は以下のようにすればできる。モデルの読み込みを一度だけにすることでメモリ消費を抑えることができそう(要確認)。

function loadModels(viewer: Autodesk.Viewing.GuiViewer3D, urn: string, offsets: Vector3[]) {
  return new Promise<Autodesk.Viewing.Model>((resolve, reject) => {
    Autodesk.Viewing.Document.load(
      urn,
      function onDocumentLoadSuccess(viewerDocument: Autodesk.Viewing.Document) {
        // viewerDocument is an instance of Autodesk.Viewing.Document
        console.log('Successed fetching Forge manifest');

        const rootNode = viewerDocument.getRoot();
        const defaultModel = rootNode.getDefaultGeometry();

        for (const offset of offsets) {
          viewer.loadDocumentNode(viewerDocument, defaultModel, {
            // preserveView: true,
            keepCurrentModels: true,
            //placementTransform: (new THREE.Matrix4()).setPosition(xform),
            globalOffset: offset,
          }).then(model => {
            resolve(model);
          });
        }
      },
      function onDocumentLoadFailure() {
        console.error('Failed fetching Forge manifest');
        reject();
      }
    );
  });
}

loadDocumenntNodeのオプションにkeepCurrentModelsを設定しておくことで、複数モデルを読み込むことができました。 試せていませんが、placementTransformを指定することで、回転やスケールなども設定できそうです。

Forge Viewer上でモデルの色や位置を変更したい場合

Extensionなどのコードを読んでやりたいことの実現方法を調べた結果をまとめています。 どこまでが公式に保証されている動作かは把握できていないので、今後のバージョンで動く保証はできないです。

モデルの色を変更したい場合

以下でできるが、コードをみてわかる通り、dbId毎に設定する必要があるので、色を変更したい部分のすべてのdbIdをピックアップする手段が別途必要になります。

function setColor(model: Autodesk.Viewing.Model) {
    const r = 1;
    const g = 0;
    const b = 0;
    model.setThemingColor(dbId, new THREE.Vector4(r, g, b, 1));
}

alphaは設定するとどうなるんだろう?(要確認)

モデルの位置を変更したい場合

以下でできる。

function setOffset(model: Autodesk.Viewing.Model, offset: THREE.Vector3) {
    (model as any).setGlobalOffset(offset);
}

動的に変更した場合の画面表示への反映方法

位置(色も?)をデータ上で変更しても、そのままでは画面は再描画されるまで更新されません。 そのため、アニメーション表示するためには、強制的に画面の再描画が必要になります。 具体的には以下で再描画できます。

function repaint(viewer: Autodesk.Viewing.GuiViewer3D) {
    viewer.impl.invalidate(true, true, true);
}
````