Matterport Labs 2021 (English)
Matterport Labsのページは、Matterport自身の開発者が、__マーターポート・プラットフォーム__を使用して何を開発できるかについてのアイデアを示すための場所です。この記事の執筆時点で、3つのプロトタイプを使用できます:
- Pano Painter: 水平の表面にのみテクスチャを適用し、どんな表面にでも色を付けるペイントツール。
- Fly Through Generator: ユーザーがシーン内で選択した2点間のフライスルーを自動的に生成するパスファインダーツール。
- Spatial Planner: 部屋ごとにシーン内の家具のレイアウトを計画するためのルームプランナーツール。
プロトタイプの作成に使用された一部の機能はSDK(およびAPI)の現在のバージョンでは実装してないため、これらのプロトタイプは__Matterport SDKではなく__、__マーターポート・プラットフォーム__を使用して開発できると書いてます。
と言うのは?
マーターポート・プラットフォーム
__マーターポート・プラットフォーム__って何ですか?
__マーターポート・プラットフォーム__は、Showcase SDK、Model API、チュートリアルのコードサンプル、SDKのサンプルで構成されています。また、公式には配布されていませんが、ブラウザーの開発ツールから簡単にアクセスでき、SDKの未来のバージョンで公式に導入される可能性のある機能を示めします。
使用されるスタックには、次のテクノロジーが含まれます:
- Yarn
- TypeScript
- React
- Three.js(バージョン0.124.0以下)
アプリケーションに応じて他のライブラリと同様に。
でも、どうやってLabsアプリのコードを手に入れられますか?
Labsアプリのコードとアセット
Labsアプリのコードは、ブラウザーの開発ツールから入手できます。
Chromeバージョン91.0.4472.114(公式ビルド)(x86_64)を使用していますが、Chromeでこれを行う方法は次のとおりです:
- コードを表示するアプリケーションを起動します(この例では、Pano Painterアプリケーションを使用します)。
- 開発ツールを開きます。
-
ソース
タブを選択します:
- そして
Page
タブを選択し、top
アイテムをダブルクリックして中身を見ます:
そこにはたくさんのものがあるので、もっとも面白い項目を開いてみましょう:
・ __labs.matterport.com__には、ルートページのアセットとバンドルされたコードが含まれていますが、今は触らずに置いておきます。
・ __app-iframe (index.html)__>__static.matterport.com__には、ペインターツールのアセットとバンドルされたコードが含まれています。これは探しているソースコードではないため、アセットのみ抽出します。
・ __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
には、ペインターアプリケーションによってインポートされたライブラリに関する情報が含まれています
・ __app-iframe (index.html)__>__sdk-iframe (showcase.html)__>__static.matterport.com__には、SDK Bundleのアセットとバンドルコードが含まれています。LabsアプリケーションはSDKバンドルの新しいバージョンを使用しているため、これを抽出します。現在のバージョンのSDKバンドルのファイルをこれらに置き換える必要があります。
コードとアセットが見つかったので、それを実行するために新しいプロジェクトを構築する必要があります...
基本プロジェクト
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キーを設定することを忘れないでください:
// ...
<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キーを設定する:
// ...
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.tsx
はMain
のReactコンポーネントをレンダリングします:
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コンポーネントのファイルを作成します:
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コンポーネントのファイルを作成します:
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ファイルを作成します:
# sdk-iframe {
width: 100vw;
height: 100vh;
}
body {
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
overflow: hidden;
}
TypeScript設定
以下をcompilerOptions
に追加します:
"experimentalDecorators": true,
"jsx": "react",
"baseUrl": ".",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"skipLibCheck": true,
compilerOptions
のlib
設定を変更します:
"lib": [
"dom",
"es2015"
],
include
設定を変更します:
"include": [
"src",
"src/**/*.ts",
"@types/**/*.d.ts",
]
Webpack設定
エントリ名をindex.ts
からindex.tsx
に変更します:
entry: {
app: './src/index.tsx',
},
ReactとTypeScriptのファイル拡張子を追加します:
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx']
},
モジュールローダーのルールを変更します:
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
ライブラリのコード:
※ マウスカーソルを制御するコードは、Showcase SDK examplesの@mp/common
ライブラリーコードにはないため、この機能を使用できるようにするには、common/src/index.ts
ファイルとcommon/src/MouseCursor.ts
ファイルを抽出する必要があります。
@mp/saveライブラリーの再構築
@mp/save
ライブラリーコードを再構築するために、Chromeからファイルを抽出し、それらの階層を再作成しましょう:
+ save/
+ mp_labs_save/
+ save/
+ dexie/
| dexie.js
| App.ts
| AppMessages.ts
| Page.ts
| PageMessages.ts
次に、ライブラリーのルートフォルダで、@mp/core
ライブラリーからpackage.json
とtsconfig.json
をコピーします:
+ save/
+ mp_labs_save/
+ save/
+ dexie/
| dexie.js
| App.ts
| AppMessages.ts
| Page.ts
| PageMessages.ts
| package.json
| tsconfig.json
package.json
ファイル内のTypeScriptモジュールルートファイルの名前とパスを変更します。
{
"name": "@mp/save",
"version": "1.0.0",
"main": "mp_labs_save/save/index.ts",
"scripts": {
}
}
最後に、TypeScriptモジュールのルートファイルを作成する必要があります。@mp/core
ライブラリからcore/src/index.js
ファイルを複製しましょう:
import * as App from './AppModule';
import * as Page from './PageModule';
import { Dict } from './types';
export { App, Page, Dict }
ライブラリのタイプ階層を正しく構造化するには、2つの追加ファイルが必要です。1つはApp
のタイプを定義するためのものです:
export { OutdatedData, ISaveRequestHandler, AppSaver } from './App';
export { AppMsgType, SchemaDescriptor, SchemaResponse, SaveResponse, MigrateResponse, PayloadMap } from './AppMessages';
もう1つは、Page
のタイプ用に:
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
メッセージを回避するには、下記の行を:
...
"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
ファイルのアプリエントリポイントへのパスを更新しましょう:
...
"main": "./src/index.tsx",
...
webpack.config.js
Webpack設定ファイルのアプリエントリポイントパスと出力パスも更新しましょう:
// ...
entry: './src/index.tsx',
output: {
filename: 'js/[name].bundle.js'
},
// ...
tsconfig.json
次に、TypeScript設定ファイルを更新しましょう:
...
"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
にローカルライブラリーの情報を挿入します:
"@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キーを設定することを忘れないでください:
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...
そして、src
ライブラリーフォルダーのsrc/components/Main.tsx
ファイルにもSDKキーを設定します:
// ...
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__ファイルは、処理せずにそのままコピーできます:
以下の様になります:
+ 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'.
したがって、存在しないモジュールファイルを再構築する必要があります:
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のモジュールが含まれています:
// 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ルールを抽出する必要があります:
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
したがって、ここで、\n
を実際のnewline
文字に置き換える必要があります:
# 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パスプレースホルダーを置き換える必要があります:
...
@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;
}
...
ファイルの先頭にそれぞれのパスが定義されている場合:
...
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ルールは次のようになります:
@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;
}
...
- また、文字列のコンテンツをエンコードするために使用される無関係な
\
文字を、\"
を"
に、\\
を\
に置き換えることにより、削除する必要があります:
...
.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からダウンロードする必要もあります:
そして、それらを対応する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アプリケーションで使用されるモデルのAAWs9eZ9ip6
__SID__を使用することはできませんが、同じモデルにアクセスできる別のj4RZx7ZGM6T
を使用することはできます:
// ...
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
にローカルライブラリーの情報を挿入します:
"@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キーを設定することを忘れないでください:
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...
そして、src
ライブラリフォルダのsrc/components/Main.tsx
ファイル:
// ...
this.applicationKey = urlParams.get('applicationKey') || 'PUT_YOUR_SDK_KEY_HERE';
// ...
※ このサンプルを作成するときに、SDKキーを使用してCORSエラーとアクセスの問題が発生したため、Labsアプリの08s53auxt9txz1w6hx2iww1qb
を使用しました。
起動
サンプルを起動します:
yarn start
ギットハブで__マーターポート・ラボ__のPano Painterのサンプル...
Fly Through Generator
Matterport LabsアプリのFly Through Generatorはユーザーがシーン内で選択した2点間のフライスルーを自動的に生成するパスファインダーツールです。
コード
まず、ブラウザからコードを抽出しましょう。ソースファイルを右クリックし、メニューで名前を付けて保存
を選択して、対応するmatterport-fly-through-generator/src/[…]
パスにファイルを保存します。__TypeScript__ファイルは、処理せずにそのままコピーできます:
以下の様になります:
+ 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]'.
したがって、存在しないファイルを再構築する必要があります:
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
フォルダーをコピーしましょう:
// ...
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
にローカルライブラリーの情報を挿入します:
"@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キーを設定することを忘れないでください:
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...
そして、src
ライブラリフォルダの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__ファイルは、処理せずにそのままコピーできます:
+ 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
ファイルにインポートされます:
// ...
import animation from '../../assets/labs_loader.json';
// ...
したがって、存在しないファイルを再構築する必要があります。最終的なmain.bundle.js
ファイル内にそのデータが含まれてるはずです:
assets/labs_loader.json
ファイルの内容を文字列から解析するJSON.parse('
ステートメントを探し、次の1つの行を取得します:
// ...
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
...
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
にローカルライブラリーの情報を挿入します:
"@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キーを設定することを忘れないでください:
// ...
export const sdkKey = 'PUT_YOUR_SDK_KEY_HERE';
// ...
そして、src
ライブラリフォルダの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のサンプル...