LoginSignup
2
1

More than 1 year has passed since last update.

マーターポート・ラボ 2021年

Last updated at Posted at 2021-08-17

Matterport Labs 2021 (English)

Matterport Labsのページは、Matterport自身の開発者が、マーターポート・プラットフォームを使用して何を開発できるかについてのアイデアを示すための場所です。この記事の執筆時点で、3つのプロトタイプを使用できます:

  1. Pano Painter: 水平の表面にのみテクスチャを適用し、どんな表面にでも色を付けるペイントツール。
  2. Fly Through Generator: ユーザーがシーン内で選択した2点間のフライスルーを自動的に生成するパスファインダーツール。
  3. Spatial Planner: 部屋ごとにシーン内の家具のレイアウトを計画するためのルームプランナーツール。

プロトタイプの作成に使用された一部の機能はSDK(およびAPI)の現在のバージョンでは実装してないため、これらのプロトタイプはMatterport SDKではなくマーターポート・プラットフォームを使用して開発できると書いてます。

と言うのは?

マーターポート・プラットフォーム

マーターポート・プラットフォームって何ですか?

マーターポート・プラットフォームは、Showcase SDKModel APIチュートリアルのコードサンプル、SDKのサンプルで構成されています。また、公式には配布されていませんが、ブラウザーの開発ツールから簡単にアクセスでき、SDKの未来のバージョンで公式に導入される可能性のある機能を示めします。

使用されるスタックには、次のテクノロジーが含まれます:

  • Yarn
  • TypeScript
  • React
  • Three.js(バージョン0.124.0以下)

アプリケーションに応じて他のライブラリと同様に。

でも、どうやってLabsアプリのコードを手に入れられますか?

Labsアプリのコードとアセット

Labsアプリのコードは、ブラウザーの開発ツールから入手できます。

Chromeバージョン91.0.4472.114(公式ビルド)(x86_64)を使用していますが、Chromeでこれを行う方法は次のとおりです:

1) コードを表示するアプリケーションを起動します(この例では、Pano Painterアプリケーションを使用します)。
2) 開発ツールを開きます。
3) ソースタブを選択します:
image.png
4) そしてPageタブを選択し、topアイテムをダブルクリックして中身を見ます:
image.png
そこにはたくさんのものがあるので、もっとも面白い項目を開いてみましょう:

labs.matterport.comには、ルートページのアセットとバンドルされたコードが含まれていますが、今は触らずに置いておきます。
image.png
app-iframe (index.html)static.matterport.comには、ペインターツールのアセットとバンドルされたコードが含まれています。これは探しているソースコードではないため、アセットのみ抽出します。
image.png
app-iframe (index.html)webpackには、探しているコードなどが含まれています。./fonts/には、アプリケーションで使用されるフォントが含まれています。./src/には、ペインターツールのコードが含まれています。 ../には、このアプリケーション@mp/commonおよび@mp/saveで使用される2つのMatterportライブラリのコードが含まれています(後者は公式SDKでは使用できません)。Hls/およびdatadisk/jenkins/workspace/sdk_examples-tags_painter-1.4.1/node_modulesには、ペインターアプリケーションによってインポートされたライブラリに関する情報が含まれています
image.png
app-iframe (index.html)sdk-iframe (showcase.html)static.matterport.comには、SDK Bundleのアセットとバンドルコードが含まれています。LabsアプリケーションはSDKバンドルの新しいバージョンを使用しているため、これを抽出します。現在のバージョンのSDKバンドルのファイルをこれらに置き換える必要があります。
image.png
コードとアセットが見つかったので、それを実行するために新しいプロジェクトを構築する必要があります...

基本プロジェクト

Labsアプリケーションの基本プロジェクトを構築するには、SDKバンドルチュートリアルのローカル環境のセットアップから始めましょう:

git clone https://github.com/matterport/showcase-sdk-tutorial.git
mv showcase-sdk-tutorial matterport-base
cd matterport-base 
curl https://static.matterport.com/showcase-sdk/bundle/3.1.42.14-24-gb057e5145/showcase-bundle.zip -o bundle.zip
unzip bundle.zip -d ./bundle
rm -rf bundle.zip
cd ..

次に、Showcase SDK examplesからマーターポートライブラリを取得しましょう:

git clone https://github.com/matterport/showcase-sdk-examples.git
cd showcase-sdk-examples
yarn install
yarn install-bundle
cp -r packages/common ../matterport-base/common
cp -r packages/core ../matterport-base/core
cd ..
rm -rf showcase-sdk-examples

index.htmlファイルにSDKキーを設定することを忘れないでください:

index.html
// ...
  <iframe id="showcase"
          src="/bundle/showcase.html?m=22Ub5eknCVx&play=1&qs=1&log=0&applicationKey=PUT_YOUR_SDK_KEY_HERE"// ...

そして、src/フォルダのsrc/index.tsファイルでもSDKキーを設定する:

src/index.ts
// ...
const key = 'PUT_YOUR_SDK_KEY_HERE';
// ...

ライブラリーをインストールします:

yarn install

それでサーバーを起動します:

yarn start

ブラウザーでhttp://localhost:8000/を開いてください。

デバッグコンソールを開くと、次のように表示されます:
Hello Bundle SDK!

最小限のReactアプリ

LabsアプリケーションはReactとThree.jsを使用しているので、基本プロジェクトを改善しましょう。

まず、src/index.tsの名前をsrc/index.tsxに変更します:

mv src/index.ts src/index.tsx

次に、Reactアプリケーションを作成しましょう:

  • index.tsxMainのReactコンポーネントをレンダリングします:
src/index.tsx
import './main.css';

import React from 'react';
import * as ReactDom from 'react-dom';
import { Main } from './components/Main';

ReactDom.render((
  <Main
    onMount={onMount}
  />
), document.getElementById('content'));

async function onMount(modelSid: string) {

}
  • メインのReactコンポーネントのファイルを作成します:
src/components/Main.tsx
import React, { Component, Fragment } from 'react';
import { Frame } from './Frame';
import { Dict } from '@mp/core';
const defaultSid = 'j4RZx7ZGM6T';
type Props = {
  onMount?(modelSid: string): void;
};
type State = {
  sdk: any | undefined;
  error: string | undefined;
  inProgress: boolean;
  warning: string;
};
export class Main extends Component<Props, State> {
  private modelSid: string;
  private apiHost: string;
  private applicationKey: string;
  private iframeRef = React.createRef<HTMLIFrameElement>();
  constructor(props: Props) {
    super(props);
    const urlParams = new URLSearchParams(window.location.search);
    this.modelSid = urlParams.get('m') || defaultSid;
    this.apiHost  = urlParams.get('apiHost') || '';
    this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
    this.state = {
      sdk: undefined,
      error: undefined,
      inProgress: false,
      warning: '',
    };
  }
  componentDidMount() {
    if (this.props.onMount) {
      this.props.onMount(this.modelSid);
    }
    connectSdk(
      this.iframeRef.current,
      this.applicationKey,
      (sdk) => this.onSdkConnect(sdk),
      (error) => this.setState({ error })
    );
  }
  componentWillUnmount() {
  }
  async onSdkConnect(sdk: State['sdk']) {
    type Sweep = {
      alignmentType: string;
    };
    const sweepCollection: Dict<{ aligned: boolean; }> = {};
    sdk.Sweep.data.subscribe({
      onAdded(index: string, item: Sweep) {
        sweepCollection[index] = { aligned: item.alignmentType === 'aligned' };
        setAlignedWarning();
      }
    });
    const currentPose: { sweep: string } = {
      sweep: '',
    }
    sdk.Camera.pose.subscribe({
      onChanged(pose: { sweep: string; }) {
        if (currentPose.sweep !== pose.sweep) {
          currentPose.sweep = pose.sweep;
          if (sweepCollection[currentPose.sweep]) {
            setAlignedWarning();
          }
        }
      }
    });
    const setAlignedWarning = () => {
      const currentSweep = sweepCollection[currentPose.sweep];
      if (currentSweep) {
        this.setState({
          warning: currentSweep.aligned ? '' : 'Navigate to an aligned sweep to use the paint tools',
        });
      }
    }
  }
  render() {
    return (
      <Fragment>
        <Frame
          getRef={this.iframeRef}
          modelSid={this.modelSid}
          apiHost={this.apiHost}
          applicationKey={this.applicationKey}
        />
      </Fragment>
    );
  }
}
function connectSdk(iframe: HTMLIFrameElement, applicationKey: string, onConnect: (sdk: State['sdk']) => void, onError: (error: string) => void): void {
  iframe.addEventListener('load', async function () {
    try {
      const sdk = await (iframe.contentWindow as any).MP_SDK.connect(iframe, applicationKey, '3.5');
      onConnect(sdk);
    } catch (e) {
      onError(e);
    }
  });
}
  • iframeをレンダリングするReactコンポーネントのファイルを作成します:
src/components/Frame.tsx
import React, { Component } from 'react';
interface Props {
  src: string;
}
export class Frame extends Component<Props, {}> {
  render() {
    return (
      <div className='frame'>
        <iframe id='sdk-iframe' className='frame' src={this.props.src + '&title=0&qs=1&hr=0&brand=0&help=0'}></iframe>
      </div>
    );
  }
}
  • 基本的なCSSファイルを作成します:
src/main.css
#sdk-iframe {
  width: 100vw;
  height: 100vh;
}

body {
  -ms-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  overflow: hidden;
}
TypeScript設定

以下をcompilerOptionsに追加します:

tsconfig.json
    "experimentalDecorators": true,
    "jsx": "react",
    "baseUrl": ".",
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,

compilerOptionslib設定を変更します:

tsconfig.json
    "lib": [
      "dom",
      "es2015"
    ],

include設定を変更します:

tsconfig.json
  "include": [
    "src",
    "src/**/*.ts",
    "@types/**/*.d.ts",
  ]
Webpack設定

エントリ名をindex.tsからindex.tsxに変更します:

webpack.config.js
entry: {
    app: './src/index.tsx',
  },

ReactとTypeScriptのファイル拡張子を追加します:

webpack.config.js

  resolve: {
    extensions: ['.js', '.jsx', '.json', '.ts', '.tsx']
  },

モジュールローダーのルールを変更します:

webpack.config.js
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        loader: 'ts-loader'
      },
      { enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ]
  },

マーターポートライブラリー

ほとんどのマーターポートのライブラリーは、Showcase SDK examplesから入手できます:

  • @mp/bundle-sdkライブラリーのコードはpackages/bundle/フォルダーにあります。
  • @mp/commonライブラリーのコードはpackages/common/フォルダーにあります。
  • @mp/coreライブラリーのコードはpackages/core/フォルダーにあります。

ただし、バージョンはMatterport Labsのアプリケーションで使用されているバージョンとはおそらく異なり、@mp/saveライブラリーのコードはShowcase SDK examplesにないため、ブラウザーから抽出する必要があります。

ブラウザからアプリケーションのコードを抽出したのと同じ方法で、ライブラリコードを抽出できます。ソースファイルを右クリックして、名前を付けて保存を選択するだけです。

  • @mp/commonライブラリのコード:
    image.png
    ※ マウスカーソルを制御するコードは、Showcase SDK examples@mp/commonライブラリーコードにはないため、この機能を使用できるようにするには、common/src/index.tsファイルとcommon/src/MouseCursor.tsファイルを抽出する必要があります。

  • @mp/saveライブラリのコード:
    image.png

@mp/saveライブラリーの再構築

@mp/saveライブラリーコードを再構築するために、Chromeからファイルを抽出し、それらの階層を再作成しましょう:

+ save/
    + mp_labs_save/
        + save/
            + dexie/
                | dexie.js
            | App.ts
            | AppMessages.ts
            | Page.ts
            | PageMessages.ts

次に、ライブラリーのルートフォルダで、@mp/coreライブラリーからpackage.jsontsconfig.jsonをコピーします:

+ save/
    + mp_labs_save/
        + save/
            + dexie/
                | dexie.js
            | App.ts
            | AppMessages.ts
            | Page.ts
            | PageMessages.ts
    | package.json
    | tsconfig.json

package.jsonファイル内のTypeScriptモジュールルートファイルの名前とパスを変更します。

package.json
{
  "name": "@mp/save",
  "version": "1.0.0",
  "main": "mp_labs_save/save/index.ts",
  "scripts": {
  }
}

最後に、TypeScriptモジュールのルートファイルを作成する必要があります。@mp/coreライブラリからcore/src/index.jsファイルを複製しましょう:

index.ts
import * as App from './AppModule';
import * as Page from './PageModule';
import { Dict } from './types';

export { App, Page, Dict }

ライブラリのタイプ階層を正しく構造化するには、2つの追加ファイルが必要です。1つはAppのタイプを定義するためのものです:

AppModule.ts
export { OutdatedData, ISaveRequestHandler, AppSaver } from './App';
export { AppMsgType, SchemaDescriptor, SchemaResponse, SaveResponse, MigrateResponse, PayloadMap } from './AppMessages';

もう1つは、Pageのタイプ用に:

PageModule.ts
export { DatabaseConfT, openDb, ISaveObserver, Metadata, AppSaveRequester, setupMockMessageButtons } from './Page';
export { PageMsgType, SchemaRequest, SaveRequest, LoadRequest, MigrateRequest, PayloadMap } from './PageMessages';

index.tsファイルも、通常は同じフォルダーで定義され、type.jsと呼ばれる型を使用するため、@mp/coreライブラリからcore/src/type.jsファイルをコピーするだけです。

そして、以下の様になります:

+ save/
    + mp_labs_save/
        + save/
            + dexie/
                | dexie.js
            | App.ts
            | AppMessages.ts
            | AppModule.ts
            | index.ts
            | Page.ts
            | PageMessages.ts
            | PageModule.ts
            | types.ts
    | package.json
    | tsconfig.json
「No license field」ワーニング

warning package.json: No license fieldメッセージを回避するには、下記の行を:

package.json
...
  "license": "UNLICENSED",
...

各ライブラリーフォルダーのpackage.jsonファイルの中にコピーします:

  • bundle/package.json
  • common/package.json
  • core/package.json
  • save/package.json

Nodeライブラリー

yarnを使用して必要なライブラリーを追加します:

yarn add react@16.12.0
yarn add @types/react@16.9.19
yarn add react-dom@16.12.0
yarn add @types/react-dom@16.9.5
yarn add three@0.124.0
yarn add @types/three
yarn add source-map-loader@0.2.4
yarn add css-loader
yarn add style-loader@1.1.3
yarn add webpack-dev-server
yarn add hls.js
yarn add classnames
yarn add dexie

設定

また、さまざまな設定ファイルを変更する必要があります:

package.json

まず、package.jsonファイルのアプリエントリポイントへのパスを更新しましょう:

package.json
...
  "main": "./src/index.tsx",
...
webpack.config.js

Webpack設定ファイルのアプリエントリポイントパスと出力パスも更新しましょう:

webpack.config.js
// ...
  entry: './src/index.tsx',
  output: {
    filename: 'js/[name].bundle.js'
  },
// ...
tsconfig.json

次に、TypeScript設定ファイルを更新しましょう:

tsconfig.json
...
    "experimentalDecorators": true,
    "jsx": "react",
    "baseUrl": ".",
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "lib": [
      "dom",
      "es2015"
    ],
  },
  "include": [
    "src",
    "src/**/*.ts",
    "@types/**/*.d.ts",
  ]
Yarn

また、マーターポートライブラリーのコードをyarnレジストリにリンクして、それらを使用できるようにする必要があります:

cd core
yarn link
cd ..
yarn link @mp/core
cd common
yarn link
yarn link @mp/core
cd ..
yarn link @mp/common
cd bundle
yarn link
cd ..
yarn link @mp/bundle-sdk
cd save
yarn link
cd ..
yarn link @mp/save

package.jsonにローカルライブラリーの情報を挿入します:

package.json
    "@mp/core": "^1.0.0",
    "@mp/common": "^1.0.0",
    "@mp/bundle-sdk": "^1.0.0",
    "@mp/save": "^1.0.0",

commonライブラリーフォルダーのcommon/src/index.tsファイルにSDKキーを設定することを忘れないでください:

common/src/index.ts
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...

そして、srcライブラリーフォルダーのsrc/components/Main.tsxファイルにもSDKキーを設定します:

src/components/Main.tsx
// ...
this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
// ...

サンプルテスト

サンプルをテストするために開発サーバーを起動しましょう:

yarn start

ブラウザーでhttp://localhost:8000/を開いてください。

ラボアプリの抽出

最後に、基本プロジェクトから3つのLabsアプリケーションのプロジェクトを作成しましょう:

cd .. 
cp -r matterport-base matterport-pano-painter
cp -r matterport-base matterport-fly-through-generator
cp -r matterport-base matterport-spatial-planner

これで、ブラウザ開発ツールから抽出できるコードで、ローカルで起動できるアプリケーションを作成しましょう...

Pano Painter

Matterport LabsアプリのPano Painterは水平の表面にのみテクスチャを適用し、どんな表面にでも色を付けるペイントツールです。

コード

まず、ブラウザからコードを抽出しましょう。ソースファイルを右クリックし、メニューで名前を付けて保存を選択して、対応するmatterport-pano-painter/src/[…]パスにファイルを保存します。TypeScriptファイルは、処理せずにそのままコピーできます:
image.png
以下の様になります:

+ matterport-pano-painter/
    + src/
        + components/
            + Controls/
                + styles/
                    | button.css
                    | color-picker.css
                    | history.css
                    | paint-controls.css
                    | range.css
                    | texture-controls.css
                | Button.tsx
                | ColorPicker.tsx
                | HistoryControls.tsx
                | PaintControls.tsx
                | Range.tsx
                | TextureControls.tsx
            + KeyWatcher/
                | KeyWatcher.ts
            + Save/
                | PaintSaver.ts
                | TextureSaver.ts
                | index.ts
            + SceneNodes/
                + SceneComponents/
                    | BrushCursor.ts
                    | BrushInput.ts
                    | Dropper.ts
                    | PaintTarget.ts
                    | TextureSelector.ts
                    | TextureTarget.ts
                    | shader.ts
                | CameraOverrideNode.ts
                | DropperNode.ts
                | PaintNode.ts
                | TextureNode.ts
            | Frame.tsx
            | HistoriedPaintScene.ts
            | History.ts
            | Main.tsx
            | preventDefault.ts
        | fonts.css
        | index.tsx
        | main.css

ただし、コンパイルしようとすると、src/componentsにファイルがないことを示す2種類のエラーが発生します:

  • TS2307: Cannot find module '[…]/PointerButtonEvent' or its corresponding type declarations.
  • TS2339: Property '[button|device|down|position]' does not exist on type 'never'.

したがって、存在しないモジュールファイルを再構築する必要があります:

src/components/PointerButtonEvents.ts
import {
  IVector2,
  PointerButton,
  PointerButtonEvent,
  PointerButtonMask,
  PointerDevice,
  PointerMoveEvent,
} from '@mp/common';

export class PointerButtonEventData implements PointerButtonEvent {
  readonly id: number;
  readonly position: IVector2;
  readonly button: PointerButton;
  readonly down: boolean;
  readonly device: PointerDevice;
}
export class PointerMoveEventData implements PointerMoveEvent {
  readonly id: number;
  readonly position: IVector2;
  readonly buttons: PointerButtonMask;
  readonly device: PointerDevice;
}

CSS

ただし、CSSファイルには、以下のファイルのように、単純なCSSではなくJavaScriptコードを処理するWebpackのモジュールが含まれています:

src/main.css
// Imports
var ___CSS_LOADER_API_IMPORT___ = require("../../../node_modules/css-loader/dist/runtime/api.js");
var ___CSS_LOADER_AT_RULE_IMPORT_0___ = require("-!../../../node_modules/css-loader/dist/cjs.js!./font.css");
exports = ___CSS_LOADER_API_IMPORT___(false);
exports.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
// Module
exports.push([module.id, "#sdk-iframe {\n  width: 100vw;\n  height: 100vh;\n}\n\nbody {\n  -ms-user-select: none;\n  -webkit-user-select: none;\n  user-select: none;\n  overflow: hidden;\n}\n\n.banner {\n  position: fixed;\n  bottom: calc(100% - 2em);\n  width: 100%;\n  height: 2em;\n  line-height: 2em;\n  background-color: #f5f4f3;\n  padding-left: 8px;\n  transition: bottom .5s .5s;\n}\n\n.banner.collapsed {\n  bottom: 100%;\n}\n\n.banner.error {\n  background-color: #ff3158;\n}\n\n/* debug */\nbody.has-saves {\n  overflow: scroll;\n  overflow-x: hidden;\n}\n\n.save {\n  margin: 0 32px;\n  width: 64px;\n  height: 25px;\n}\n\n.saves img {\n  width: 128px;\n  height: 128px;\n}\n", ""]);
// Exports
module.exports = exports;

次のようなステートメントでexportsにプッシュされた配列の2番目の引数文字列に含まれているCSSルールを抽出する必要があります:

*.css
exports.push([module.id, "[…]", ""]);

抽出する文字列の内容は次のようになります:

src/main.css
#sdk-iframe {\n  width: 100vw;\n  height: 100vh;\n}\n\nbody {\n  -ms-user-select: none;\n  -webkit-user-select: none;\n  user-select: none;\n  overflow: hidden;\n}\n\n.banner {\n  position: fixed;\n  bottom: calc(100% - 2em);\n  width: 100%;\n  height: 2em;\n  line-height: 2em;\n  background-color: #f5f4f3;\n  padding-left: 8px;\n  transition: bottom .5s .5s;\n}\n\n.banner.collapsed {\n  bottom: 100%;\n}\n\n.banner.error {\n  background-color: #ff3158;\n}\n\n/* debug */\nbody.has-saves {\n  overflow: scroll;\n  overflow-x: hidden;\n}\n\n.save {\n  margin: 0 32px;\n  width: 64px;\n  height: 25px;\n}\n\n.saves img {\n  width: 128px;\n  height: 128px;\n}\n

したがって、ここで、\nを実際のnewline文字に置き換える必要があります:

src/main.css
#sdk-iframe {
  width: 100vw;
  height: 100vh;
}

body {
  -ms-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  overflow: hidden;
}

.banner {
  position: fixed;
  bottom: calc(100% - 2em);
  width: 100%;
  height: 2em;
  line-height: 2em;
  background-color: #f5f4f3;
  padding-left: 8px;
  transition: bottom .5s .5s;
}

.banner.collapsed {
  bottom: 100%;
}

.banner.error {
  background-color: #ff3158;
}

/* debug */
body.has-saves {
  overflow: scroll;
  overflow-x: hidden;
}

.save {
  margin: 0 32px;
  width: 64px;
  height: 25px;
}

.saves img {
  width: 128px;
  height: 128px;
}

次に、残りのCSSファイルを処理する必要があります...ペインターアプリには、2つのディレクトリにCSSファイルがあります。

  • src/
  • src/component/Controls/styles/

すべてのCSSファイルをリストしましょう:

+ matterport-pano-painter/
    + src/
        + components/
            + Controls/
                + styles/
                    | button.css
                    | color-picker.css
                    | history.css
                    | paint-controls.css
                    | range.css
                    | texture-controls.css
        | fonts.css
        | main.css

src/fonts.cssファイルにはさらに処理が必要です。

  • ファイルの先頭にある @font-faceディレクティブで使用するフォントファイルへのURLパスを参照するため、以下のURLパスプレースホルダーを置き換える必要があります:
src/fonts.css
...
@font-face {
  font-family: 'labs-paint';
  src:
    url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + ") format('truetype'),
    url(" + ___CSS_LOADER_URL_REPLACEMENT_1___ + ") format('woff'),
    url(" + ___CSS_LOADER_URL_REPLACEMENT_2___ + ") format('svg');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
...

ファイルの先頭にそれぞれのパスが定義されている場合:

src/fonts.css
...
var ___CSS_LOADER_URL_IMPORT_0___ = require("../fonts/labs-paint.ttf?caj1pz");
var ___CSS_LOADER_URL_IMPORT_1___ = require("../fonts/labs-paint.woff?caj1pz");
var ___CSS_LOADER_URL_IMPORT_2___ = require("../fonts/labs-paint.svg?caj1pz");
...

結果のCSSルールは次のようになります:

src/fonts.css
@font-face {
  font-family: 'labs-paint';
  src:
    url("../fonts/labs-paint.ttf") format('truetype'),
    url("../fonts/labs-paint.woff") format('woff'),
    url("../fonts/labs-paint.svg") format('svg');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
...
  • また、文字列のコンテンツをエンコードするために使用される無関係な\文字を、\""に、\\\に置き換えることにより、削除する必要があります:
src/fonts.css
...
.icon-dropper-fill:before {
  content: "\e901";
}
.icon-dropper-outline:before {
  content: "\e908";
}
.icon-dropper:before {
  content: "\e902";
}
.icon-bucket-fill:before {
  content: "\e906";
}
.icon-bucket:before {
  content: "\e907";
}
.icon-brush:before {
  content: "\e900";
}
.icon-history:before {
  content: "\e904";
}
.icon-checkmark:before {
  content: "\e905";
}
.icon-chevron-down:before {
  content: "\e909";
}
...

しかし、残念ながら、アイコンはまだ正しく表示されません...

アセット

Labsアプリケーションには、フォントまたは画像用のアセットファイルが必要です。ブラウザからも抽出します...

フォント

まず、フォントをダウンロードしましょう。ソースファイルを右クリックし、メニューで名前を付けて保存を選択して、対応するmatterport-pano-painter/fonts/[…]パスにファイルを保存します:

+ matterport-pano-painter/
    + fonts/
        | labs-paint.svg
        | labs-paint.ttf
        | labs-paint.woff
イメージ

次に、画像をダウンロードしましょう。ソースファイルをクリックし、表示された画像を右クリックして、メニューで名前を付けて保存を選択し、対応するmatterport-pano-painter/assets/[…]パスにファイルを保存します:

+ matterport-pano-painter/
    + assets/
        | brick1.jpg
        | carpet1.jpg
        | hardwood1.jpg
        | plywood1.jpg
バンドルのイメージ

最後に、ペインターアプリケーションバンドルの追加画像をiframeからダウンロードする必要もあります:
image.png
そして、それらを対応するmatterport-pano-painter/bundle/images/[…]パスに保存します:

+ matterport-pano-painter/
    + bundle/
        + images/
            | Roboto-Bold.png
            | dot_default_2.png
            | dot_selected_2_2.png
            | line_default_2.png
            | line_selected_2_2.png
            | line_default_2.png
            | line_selected_2_2.png
モデルSID

また、Labsアプリケーションで使用されるモデルのAAWs9eZ9ip6SIDを使用することはできませんが、同じモデルにアクセスできる別のj4RZx7ZGM6Tを使用することはできます:

src/components/Main.tsx
// ...
const defaultSid = 'j4RZx7ZGM6T';
// ...

サンプル

作成したローカルLabsアプリケーションを試してみましょう!

インストール

Node.jsモジュールをインストールします:

yarn install

マーターポートライブラリーをリンクします:

cd core
yarn link
cd ..
yarn link @mp/core
cd common
yarn link
yarn link @mp/core
cd ..
yarn link @mp/common
cd bundle
yarn link
cd ..
yarn link @mp/bundle-sdk
cd save
yarn link
cd ..
yarn link @mp/save

package.jsonにローカルライブラリーの情報を挿入します:

package.json
    "@mp/core": "^1.0.0",
    "@mp/common": "^1.0.0",
    "@mp/bundle-sdk": "^1.0.0",
    "@mp/save": "^1.0.0",

commonライブラリフォルダのcommon/src/index.tsファイルにSDKキーを設定することを忘れないでください:

common/src/index.ts
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...

そして、srcライブラリフォルダのsrc/components/Main.tsxファイル:

src/components/Main.tsx
// ...
this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
// ...

※ このサンプルを作成するときに、SDKキーを使用してCORSエラーとアクセスの問題が発生したため、Labsアプリの08s53auxt9txz1w6hx2iww1qbを使用しました。

起動

サンプルを起動します:

yarn start

image.png
ギットハブでマーターポート・ラボPano Painterのサンプル...

Fly Through Generator

Matterport LabsアプリのFly Through Generatorはユーザーがシーン内で選択した2点間のフライスルーを自動的に生成するパスファインダーツールです。

コード

まず、ブラウザからコードを抽出しましょう。ソースファイルを右クリックし、メニューで名前を付けて保存を選択して、対応するmatterport-fly-through-generator/src/[…]パスにファイルを保存します。TypeScriptファイルは、処理せずにそのままコピーできます:
image.png
以下の様になります:

+ matterport-fly-through-generator/
    + src/
        + react-components/
            | Frame.tsx
            | Main.tsx
            | PlayControl.tsx
            | SpeedControl.tsx
        + scene-components/
            | AxisContril.ts
            | DirtyTracker.ts
            | Grid.ts
            | Grip.ts
            | HighlightPoint.ts
            | NearestSweep.ts
            | PathBuilder.ts
            | PathBuilder2.ts
            | PathVisualizer.ts
            | SceneNodeObserver.ts
            | Sphere.ts
            | SplineCamera.ts
            | SweepVisualizer.ts
            | TunnelVisualizer.ts
        | AppContext.ts
        | AppFSM.ts
        | Application.ts
        | AppSerializer.ts
        | EnvContext.ts
        | index.tsx
        | SdkService.ts

Nodeライブラリー

yarnで次のライブラリーを追加します:

yarn add @material-ui/core
yarn add @material-ui/icons
yarn add @tweenjs/tween.js
yarn add rxjs
yarn add xstate@4.10.0

マーターポートライブラリー

Pano Painterアプリと同様に、ほとんどのMatterportライブラリーはShowcase SDK examplesから入手できます:

  • @mp/bundle-sdkライブラリのコードはpackages/bundle/フォルダーのなかにあります
  • @mp/commonライブラリのコードはpackages/common/フォルダーのなかにあります
  • @mp/coreライブラリのコードはpackages/core/フォルダーのなかにあります

ただし、バージョンはMatterport Labsのアプリケーションで使用されているバージョンとはおそらく異なり、@mp/saveライブラリーのコードはShowcase SDK examplesにないため、ブラウザーから抽出するまたはPano Painterアプリからコピーする必要があります。

src/interface.tsの再構築

ただし、コンパイルしようとすると、src/にファイルがないことを示す3種類のエラーが発生します:

  • TS2307: Cannot find module '[…]/interfaces' or its corresponding type declarations.
  • TS2339: Property '[composer|debug|nodeFactory]' does not exist on type 'ComponentContext'.
  • TS2305: Module '"../interfaces"' has no exported member '[IDisposable|PathPoint]'.

したがって、存在しないファイルを再構築する必要があります:

src/interfaces.ts
import { BehaviorSubject } from 'rxjs';
import { ISceneNode, SceneComponent } from '@mp/common';
import { ObservableValue } from '@mp/core';
import { Box3, Material, Mesh, MeshStandardMaterial, Vector3 } from 'three';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial';
import { Quaternion } from "three"
import { FSMEvent, FSMSchema } from './AppFSM';
import { Interpreter } from 'xstate';


export type PathPoint = {
    position: Vector3;
    floorPosition: Vector3;
    id: string;
}
export type PathState = {
    path: ObservableValue<any[]>,
    pathLength: ObservableValue<Number>,
    validPath: ObservableValue<any>,
}
export type EditState = {
    transformMode: ObservableValue<String>,
    focus: ObservableValue<Vector3>,
    focusTarget: ObservableValue<any>,
    hoverTarget: ObservableValue<any>,
    boxTransform: {
        position: ObservableValue<Vector3>,
        rotation: ObservableValue<Quaternion>,
        scale: ObservableValue<Vector3>,
    },
    startPos: ObservableValue<Vector3>,
    endPos: ObservableValue<Vector3>,
    pathState: {
        path: ObservableValue<any[]>,
        pathLength: ObservableValue<Number>,
        validPath: ObservableValue<boolean>,
    },
    visualizerState: {
        duration: ObservableValue<number>,
    }
}

export type EditMode = 'translate'| 'rotate'|'scale';

export type BoxTransformState = {
    position: ObservableValue<Vector3>;
    rotation: ObservableValue<Quaternion>;
    scale: ObservableValue<Vector3>; 
}

// From src/scene-components/PathBuilder.ts
export type Sweep = {
    floor: number;
    neighbors: string[];
    position: Vector3;
    rotation: Vector3;
    uuid: string;
    id: string;
    alignmentType: String;
}

// From the Spatial Planner sample's src/types.ts
export type IEnvContext = {
    apiHost: string;
    applicationKey: string;
    sid: string;
}

// From the Showcase SDK sample'S packages/inspector/src/Scene.ts
export interface IContext {
    editState: EditState;
    fsm: Interpreter<any, FSMSchema, FSMEvent, any>;
}

export interface IScene {
    readonly objects: BehaviorSubject<ISceneNode[]>;
    readonly widget: SceneComponent | null;
    readonly cameraInput: SceneComponent | null;
    readonly sensor: any;

    /**
     * This function deserializes the provided string into scene nodes.
     * Additionally, it starts the scene nodes right away.
     * @param serialized serialized scene objects
     */
    deserialize(serialized: string): Promise<void>;

    /**
     * Serialize the entire scene to a string.
     */
    serialize(): Promise<string>;
    addObject(node: ISceneNode): void;
    removeObject(node: ISceneNode): void;

    /**
     * Restores the inspector to the it's default state by removing all non-inspector scene nodes.
     */
    reset(): void;
}

export interface IDisposable {
    dispose(): void;
}

export interface IRoom {
    name: string;
    center: Vector3;
    radius: number;
    allMeshes: Mesh[];
    bbox: Box3;
    damMeshes: Mesh[];

    getMaterial(): MeshStandardMaterial;
    getEdgesMaterial(): LineMaterial;
    setVisible(visible: boolean): void;
    setObjectsVisible(visible: boolean): void;
    setObjectsInteractable(interactable: boolean): void;
    setDamVisible(visible: boolean): void;
    setGreyBoxVisible(visible: boolean): void;
    setMaterial(chunkType: ChunkType, material: Material, edges: boolean): IDisposable;
    setMaterial2(chunkType: ChunkType, material: Material, edges: boolean): void;
    setInteractable(interactable: boolean): void;
    meshForChunk(chunkType: ChunkType): Mesh[];
}

export enum ChunkType {
    Wall = 'wall',
    Ceiling = 'ceiling',
    Floor = 'floor',
    Door = 'door',
    Other = 'other',
    FloorCollider = 'FloorCollider',
}

export enum Materials {
    Edges = 'Edges',
    Invisible = 'Invisible',
    SolidMaterial = 'SolidMaterial',
}

export enum MeshDrawStyle {
    Basic = 'Basic',
    GreyBox = 'GreyBox',
    Transitioning = 'Transitioning',
}

export interface IRoomBuilder extends IRoom {
    addMesh(type: ChunkType, mesh: Mesh): void;
    addDamMesh(mesh: Mesh): void;
    addObject(node: ISceneNode, bbox: Box3, disposables: IDisposable[]): void;
    removeObject(node: ISceneNode): void;
    removeAllObjects(): void;
}

export type SelectingStateVisualProps = {
    roomHoverColor: number;
    roomUnhoverColor: number;
    roomHoverOpacity: number;
    roomUnhoverOpacity: number;
    edgesHoverColor: number;
    edgesUnhoverColor: number;
    edgesHoverOpacity: number;
    edgesUnhoverOpacity: number;
    edgesHoverWidth: number;
    edgesUnhoverWidth: number;
};

export type AppState = {
    phase: any;
    rooms: Map<string, IRoomBuilder>;
    roomSelection: ObservableValue<IRoom | null>;
    objectSelection: ObservableValue<ISceneNode | null>;
    meshDrawStyle: ObservableValue<MeshDrawStyle>;
    roomHover: ObservableValue<IRoom[]>;
    clippingHeight: ObservableValue<number>;
    sketchPainter: ObservableValue<SceneComponent | null>;
    selectingStateVisualProps: SelectingStateVisualProps;
}

設定

Webpack設定ファイルも編集しないといけません:

webpack.config.js

コピープラグインを使用して、バンドルのfontsフォルダーをコピーしましょう:

webpack.config.js
// ...
    new CopyPlugin([
      {
        from: 'node_modules/@mp/bundle-sdk',
        to: 'bundle'
      },
      { from: 'assets', to: 'assets'},
      { from: 'node_modules/@mp/bundle-sdk/fonts', to: 'fonts'},
    ]),
// ...

サンプル

作成したローカルLabsアプリケーションを試してみましょう!

インストール

Node.jsモジュールをインストールします:

yarn install

マーターポートライブラリーをリンクします:

cd core
yarn link
cd ..
yarn link @mp/core
cd common
yarn link
yarn link @mp/core
cd ..
yarn link @mp/common
cd bundle
yarn link
cd ..
yarn link @mp/bundle-sdk
cd save
yarn link
cd ..
yarn link @mp/save

package.jsonにローカルライブラリーの情報を挿入します:

package.json
    "@mp/core": "^1.0.0",
    "@mp/common": "^1.0.0",
    "@mp/bundle-sdk": "^1.0.0",
    "@mp/save": "^1.0.0",

commonライブラリフォルダのcommon/src/index.tsファイルにSDKキーを設定することを忘れないでください:

common/src/index.ts
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...

そして、srcライブラリフォルダのsrc/components/Main.tsxファイル:

src/components/Main.tsx
// ...
this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
// ...

※ このサンプルを作成するときに、SDKキーを使用してCORSエラーとアクセスの問題が発生したため、Labsアプリの08s53auxt9txz1w6hx2iww1qbを使用しました。

起動

サンプルを起動します:

yarn start

残念ながら、サンプルは正しく機能せず、ヒントはブラウザのJavaScriptコンソールにあります:

AppSerializer.ts:44 Uncaught (in promise) components must be an array

ギットハブでマーターポート・ラボFly-Through Generator sample...

Spatial Planner

Matterport LabsアプリのSpatial Plannerは部屋ごとにシーン内の家具のレイアウトを計画するためのルームプランナーツールです。

コード

まず、ブラウザからコードを抽出しましょう。ソースファイルを右クリックし、メニューで名前を付けて保存を選択して、対応するmatterport-spatial-planner/src/[…]パスにファイルを保存します。TypeScriptファイルは、処理せずにそのままコピーできます:

image.png
以下の様になります:

+ matterport-spatial-planner/
    + src/
        + commands/
            | AddObjectCommand.ts
            | AddObjectToRoomCenterCommand.ts
            | AddSketchCommand.ts
            | ClearSelectionCommand.ts
            | DownloadImageCommand.ts
            | MoveCameraCommand.ts
            | RemoveObjectSelectionCommand.ts
            | SetGizmoModeCommand.ts
            | SetMeshDrawStyleCommand.ts
            | UpdateobjectCommand.ts
        + react-components/
            | AddObjectButton.tsx
            | AddObjectModal.tsx
            | AppRoot.tsx
            | BundleIframe.tsx
            | CommandProvider.tsx
            | DimensionEdit.tsx
            | DrawStyleprovider.tsx
            | EditMode.tsx
            | EditSizeModal.tsx
            | EnvContext.tsx
            | FSMContextProvider.tsx
            | FurnitureItem.tsx
            | GizmoModeprovider.tsx
            | ImageButton.styles.tsx
            | ImageButon.tsx
            | Main.tsx
            | NumberEdit.tsx
            | ObjectSelectionProvider.tsx
            | Objectselector.tsx
            | RoomSelectionProvider.tsx
            | SelectRoom.tsx
            | TopLeftControls.tsx
        + scene-components/
            | AnimatedValue.ts
            | BloomEffect.ts
            | ClippingPlane.ts
            | Draggablesurface.ts
            | EdgesMesh.ts
            | EventDispatcher.ts
            | FabricRenderer.ts
            | ItemView.ts
            | Plane.ts
            | PlaneDragHelper.ts
            | RoomComponent.ts
            | Selectable.ts
            | SelectableTint.ts
            | Skydome.ts
            | Slots.ts
            | SmoothTranslation.ts
            | SnapAxis.ts
            | SnapToFloor.ts
            | TransformGizmo.ts
            | TransformGizmo2.ts
        | AnimatedNumberPool.ts
        | Application2.ts
        | ApplicationFSM.ts
        | AppSerializer.ts
        | AssetMap.ts
        | CommandContext.ts
        | DrawStyleContext.ts
        | EditingClickSpy.ts
        | FSMContext.ts
        | GizmoModeContext.ts
        | graphql.ts
        | HideElement.ts
        | HoverSpy.ts
        | index.tsx
        | interfaces.ts
        | MaterialAnimator.ts
        | MaterialStacks.ts
        | MeshDrawStyleUpdater.ts
        | ObjectSelectionContext.ts
        | react-app-env.d.ts
        | Room.ts
        | RoomLoader.ts
        | RoomSelectionContext.ts
        | Roomupdater.ts
        | SelectionClickSpy.ts
        | SelectionMediator.ts
        | SharedMaterials.ts
        | types.tsx
        | util.ts

Nodeライブラリー

yarnで次のライブラリーを追加します:

yarn add @material-ui/core
yarn add @material-ui/styles
yarn add @tweenjs/tween.js
yarn add rxjs
yarn add xstate@4.10.0
yarn add fabric
yarn add @types/fabric
yarn add lottie-react
yarn add tweakpane@2.4.3
yarn add jspdf

アセット

Spatial Plannerでは、​​他のソースファイルのようにブラウザから直接抽出できないJSONデータファイルをインポートする必要があります。

assets/labs_loader.jsonを再構築

assets/labs_loader.jsonファイルはsrc/react-components/AppRoot.tsxファイルにインポートされます:

src/react-components/AppRoot.tsx
// ...
import animation from '../../assets/labs_loader.json';
// ...

したがって、存在しないファイルを再構築する必要があります。最終的なmain.bundle.jsファイル内にそのデータが含まれてるはずです:
image.png
assets/labs_loader.jsonファイルの内容を文字列から解析するJSON.parse('ステートメントを探し、次の1つの行を取得します:

js/main.bundle.js
// ...
    t.exports = JSON.parse('{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.21","a":"Nick Woods","k":"","d":"loader for Matterport Labs","tc":""},"fr":29.9700012207031,"ip":0,"op":91.000003706506,"w":400,"h":400,"nm":"Loader","ddd":1,"assets":[{"id":"comp_0","layers":[{"ddd":1,"ind":1,"ty":4,"nm":"Cube 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":75,"s":[0]},{"t":106.000004317469,"s":[60]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[779,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":60,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":90,"s":[200,200]},{"t":120.0000048877,"s":[0,0]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784313725,0.956862745098,0.952941176471,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60.0000024438501,"op":960.000039101602,"st":60.0000024438501,"bm":0},{"ddd":1,"ind":2,"ty":4,"nm":"Cube 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":30,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":60,"s":[200,200]},{"t":90.0000036657751,"s":[0,0]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784313725,0.956862745098,0.952941176471,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30.0000012219251,"op":930.000037879677,"st":30.0000012219251,"bm":0},{"ddd":1,"ind":3,"ty":4,"nm":"Cube","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"t":45.0000018328876,"s":[-60]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[220,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":30,"s":[200,200]},{"t":60.0000024438501,"s":[0,0]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784313725,0.956862745098,0.952941176471,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0}]}],"fonts":{"list":[{"fName":"IBMPlexSans-Medium","fFamily":"IBM Plex Sans","fStyle":"Medium","ascent":73.9990234375}]},"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Outline","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[350,350],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.61568627451,0.61568627451,0.61568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":60.0000024438501,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":30,"s":[100]},{"t":90.0000036657751,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"loading","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[199.73,112.564,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"s":36,"f":"IBMPlexSans-Medium","t":"LOADING","j":2,"tr":200,"lh":43.2,"ls":0,"fc":[0.616,0.616,0.616]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":900.000036657751,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Source animation","refId":"comp_0","sr":0.5,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,220,0],"ix":2},"a":{"a":0,"k":[500,500,0],"ix":1},"s":{"a":0,"k":[30,30,100],"ix":6}},"ao":0,"w":1000,"h":1000,"ip":30.0000012219251,"op":91.000003706506,"st":30.0000012219251,"bm":0}],"markers":[],"chars":[{"ch":"L","size":36,"style":"Medium","w":51.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[48.3,0],[48.3,-10],[19.9,-10],[19.9,-69.8],[8.6,-69.8],[8.6,0]],"c":true},"ix":2},"nm":"L","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"L","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"O","size":36,"style":"Medium","w":71.2,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.7,1.566],[-2.634,3.034],[-1.434,4.5],[0,5.867],[1.433,4.5],[2.633,3.034],[3.7,1.567],[4.533,0],[3.7,-1.566],[2.633,-3.033],[1.433,-4.5],[0,-5.866],[-1.434,-4.5],[-2.634,-3.033],[-3.7,-1.566],[-4.534,0]],"o":[[3.7,-1.566],[2.633,-3.033],[1.433,-4.5],[0,-5.866],[-1.434,-4.5],[-2.634,-3.033],[-3.7,-1.566],[-4.534,0],[-3.7,1.567],[-2.634,3.034],[-1.434,4.5],[0,5.867],[1.433,4.5],[2.633,3.034],[3.7,1.566],[4.533,0]],"v":[[47.95,-1.15],[57.45,-8.05],[63.55,-19.35],[65.7,-34.9],[63.55,-50.45],[57.45,-61.75],[47.95,-68.65],[35.6,-71],[23.25,-68.65],[13.75,-61.75],[7.65,-50.45],[5.5,-34.9],[7.65,-19.35],[13.75,-8.05],[23.25,-1.15],[35.6,1.2]],"c":true},"ix":2},"nm":"O","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[2.2,0.938],[1.566,1.773],[0.866,2.544],[0,3.213],[0,0],[-0.867,2.544],[-1.567,1.773],[-2.2,0.938],[-2.734,0],[-2.234,-0.938],[-1.567,-1.773],[-0.867,-2.544],[0,-3.211],[0,0],[0.866,-2.544],[1.566,-1.773],[2.233,-0.938],[2.666,0]],"o":[[-2.2,-0.938],[-1.567,-1.773],[-0.867,-2.544],[0,0],[0,-3.211],[0.866,-2.544],[1.566,-1.773],[2.2,-0.938],[2.666,0],[2.233,0.938],[1.566,1.773],[0.866,2.544],[0,0],[0,3.213],[-0.867,2.544],[-1.567,1.773],[-2.234,0.938],[-2.734,0]],"v":[[28.2,-10.205],[22.55,-14.27],[18.9,-20.745],[17.6,-29.378],[17.6,-40.422],[18.9,-49.053],[22.55,-55.528],[28.2,-59.594],[35.6,-61],[42.95,-59.594],[48.65,-55.528],[52.3,-49.053],[53.6,-40.422],[53.6,-29.378],[52.3,-20.745],[48.65,-14.27],[42.95,-10.205],[35.6,-8.8]],"c":true},"ix":2},"nm":"O","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"O","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"A","size":36,"style":"Medium","w":67.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[64,0],[40.2,-69.8],[26,-69.8],[2.2,0],[13.7,0],[19.8,-18.9],[45.9,-18.9],[52.2,0]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43.1,-28.6],[22.5,-28.6],[32.6,-59.4],[33.1,-59.4]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"D","size":36,"style":"Medium","w":68.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-3.634,1.467],[-2.567,2.9],[-1.4,4.367],[0,5.8],[1.4,4.367],[2.566,2.9],[3.633,1.467],[4.466,0],[0,0]],"o":[[0,0],[4.466,0],[3.633,-1.466],[2.566,-2.9],[1.4,-4.366],[0,-5.8],[-1.4,-4.366],[-2.567,-2.9],[-3.634,-1.466],[0,0],[0,0]],"v":[[8.6,0],[33.3,0],[45.45,-2.2],[54.75,-8.75],[60.7,-19.65],[62.8,-34.9],[60.7,-50.15],[54.75,-61.05],[45.45,-67.6],[33.3,-69.8],[8.6,-69.8]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-3.2,-3.266],[0,-6.333],[0,0],[3.2,-3.266],[5.2,0]],"o":[[0,0],[0,0],[5.2,0],[3.2,3.267],[0,0],[0,6.334],[-3.2,3.267],[0,0]],"v":[[19.9,-10],[19.9,-59.8],[33.3,-59.8],[45.9,-54.9],[50.7,-40.5],[50.7,-29.3],[45.9,-14.9],[33.3,-10]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"D","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"I","size":36,"style":"Medium","w":41.6,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[36,0],[36,-9.2],[26.4,-9.2],[26.4,-60.6],[36,-60.6],[36,-69.8],[5.6,-69.8],[5.6,-60.6],[15.1,-60.6],[15.1,-9.2],[5.6,-9.2],[5.6,0]],"c":true},"ix":2},"nm":"I","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"I","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"N","size":36,"style":"Medium","w":71.4,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.2,0],[62.8,0],[62.8,-69.8],[52,-69.8],[52,-16.4],[51.7,-16.4],[44,-31.2],[21.2,-69.8],[8.6,-69.8],[8.6,0],[19.4,0],[19.4,-53.4],[19.7,-53.4],[27.4,-38.6]],"c":true},"ix":2},"nm":"N","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"N","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"},{"ch":"G","size":36,"style":"Medium","w":70.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.9,-1.642],[1.5,-1.106],[2,-0.569],[2.2,0],[3.366,3.547],[0,6.492],[0,0],[-3.367,3.547],[-5.534,0],[-2.8,-2.042],[-1.334,-3.28],[0,0],[4.333,2.759],[6.266,0],[3.733,-1.566],[2.666,-3.066],[1.466,-4.5],[0,-5.8],[-1.434,-4.5],[-2.567,-3.033],[-3.534,-1.566],[-4.134,0],[-3.234,2.216],[-0.734,3.373],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,2.212],[-0.9,1.642],[-1.5,1.106],[-2,0.57],[-5.534,0],[-3.367,-3.547],[0,0],[0,-6.491],[3.366,-3.547],[4.266,0],[2.8,2.042],[0,0],[-2.4,-4.92],[-4.334,-2.759],[-4.6,0],[-3.734,1.567],[-2.667,3.067],[-1.467,4.5],[0,5.867],[1.433,4.5],[2.566,3.034],[3.533,1.566],[5.333,0],[3.233,-2.216],[0,0],[0,0]],"v":[[53.5,0],[63.4,0],[63.4,-37.2],[38.1,-37.2],[38.1,-27.5],[52.5,-27.5],[52.5,-22.07],[51.15,-16.289],[47.55,-12.167],[42.3,-9.655],[36,-8.8],[22.65,-14.12],[17.6,-29.178],[17.6,-40.622],[22.65,-55.68],[36,-61],[46.6,-57.936],[52.8,-49.953],[62,-55.339],[51.9,-66.859],[36,-71],[23.5,-68.65],[13.9,-61.7],[7.7,-50.35],[5.5,-34.9],[7.65,-19.35],[13.65,-8.05],[22.8,-1.15],[34.3,1.2],[47.15,-2.123],[53.1,-10.506],[53.5,-10.506]],"c":true},"ix":2},"nm":"G","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"G","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"IBM Plex Sans"}]}')
// ...

そして、文字列の内容を新しいassets/labs_loader.jsonとしてプロジェクトに保存するだけです!

設定

Webpack設定ファイルも編集しないといけません:

tsconfig.json
tsconfig.json
...
    new CopyPlugin([
      {
        from: 'node_modules/@mp/bundle-sdk',
        to: 'bundle'
      },
      { from: 'assets/*', to: './'},
      { from: 'bundle/images/*', to: './images/'}
    ]),
...

サンプル

作成したローカルLabsアプリケーションを試してみましょう!

インストール

Node.jsモジュールをインストールします:

yarn install

マーターポートライブラリーをリンクします:

cd core
yarn link
cd ..
yarn link @mp/core
cd common
yarn link
yarn link @mp/core
cd ..
yarn link @mp/common
cd bundle
yarn link
cd ..
yarn link @mp/bundle-sdk
cd save
yarn link
cd ..
yarn link @mp/save

package.jsonにローカルライブラリーの情報を挿入します:

package.json
    "@mp/core": "^1.0.0",
    "@mp/common": "^1.0.0",
    "@mp/bundle-sdk": "^1.0.0",
    "@mp/save": "^1.0.0",

commonライブラリフォルダのcommon/src/index.tsファイルにSDKキーを設定することを忘れないでください:

common/src/index.ts
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...

そして、srcライブラリフォルダのsrc/components/Main.tsxファイル:

src/components/Main.tsx
// ...
this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
// ...

※ このサンプルを作成するときに、SDKキーを使用してCORSエラーとアクセスの問題が発生したため、Labsアプリの08s53auxt9txz1w6hx2iww1qbを使用しました。

起動

サンプルを起動します:

yarn start

残念ながら、サンプルは正しく機能せず、ヒントはブラウザのJavaScriptコンソールにあります:

AppSerializer.ts:44 Uncaught (in promise) components must be an array

ギットハブでマーターポート・ラボSpatial Plannerのサンプル...

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1