@operato/scene-visualizer 10.0.0-beta.2 → 10.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,18 @@ import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
6
6
  import { Component } from '@hatiolab/things-scene';
7
7
  export default class GLTFInfoEditor extends OxPropertyEditor {
8
8
  static styles: import("lit").CSSResult[];
9
- src?: string;
9
+ src: string | undefined;
10
10
  width: number;
11
11
  height: number;
12
12
  depth: number;
13
+ constructor();
13
14
  editorTemplate(value: any, spec: PropertySpec): TemplateResult;
15
+ private _applyAction;
16
+ /**
17
+ * 현재 컴포넌트의 W/H/D 중 가장 큰 값을 기준으로,
18
+ * 원래 모델의 비율에 맞게 나머지 치수를 조절한다.
19
+ */
20
+ private _applyProportional;
14
21
  updated(changes: PropertyValues<this>): void;
15
22
  fetchSourceInfo(component: Component, src: string): Promise<unknown>;
16
23
  }
@@ -35,16 +35,23 @@ let GLTFInfoEditor = class GLTFInfoEditor extends OxPropertyEditor {
35
35
  font-weight: bold;
36
36
  }
37
37
 
38
- div[info] md-elevated-button {
38
+ .buttons {
39
+ display: flex;
40
+ gap: 4px;
39
41
  margin-left: auto;
42
+ }
43
+
44
+ .buttons md-elevated-button {
40
45
  --md-icon-size: 24px;
41
46
  }
42
47
  `
43
48
  ];
44
- src;
45
- width = 0;
46
- height = 0;
47
- depth = 0;
49
+ constructor() {
50
+ super();
51
+ this.width = 0;
52
+ this.height = 0;
53
+ this.depth = 0;
54
+ }
48
55
  editorTemplate(value, spec) {
49
56
  const valid = this.width && this.height && this.depth;
50
57
  var property = spec.property || {};
@@ -58,32 +65,81 @@ let GLTFInfoEditor = class GLTFInfoEditor extends OxPropertyEditor {
58
65
  <div label>[W x H x D] :</div>
59
66
  ${valid
60
67
  ? html ` <div value>${this.width} x ${this.height} x ${this.depth}</div>
61
- <md-elevated-button
62
- @click=${(e) => {
63
- this.dispatchEvent(new CustomEvent('i-need-selected', {
64
- bubbles: true,
65
- composed: true,
66
- detail: {
67
- callback: (selected) => {
68
- typeof action === 'function' &&
69
- action(selected[0], {
70
- width: this.width,
71
- height: this.height,
72
- depth: this.depth
73
- });
74
- }
75
- }
76
- }));
68
+ <div class="buttons">
69
+ <md-elevated-button
70
+ title="원래 크기로 설정"
71
+ @click=${() => {
72
+ this._applyAction(action, {
73
+ width: this.width,
74
+ height: this.height,
75
+ depth: this.depth
76
+ });
77
77
  }}
78
- >
79
- <md-icon>autorenew</md-icon>
80
- </md-elevated-button>`
78
+ >
79
+ <md-icon>autorenew</md-icon>
80
+ </md-elevated-button>
81
+ <md-elevated-button
82
+ title="현재 크기에 비율 맞춤"
83
+ @click=${() => this._applyProportional(action)}
84
+ >
85
+ <md-icon>aspect_ratio</md-icon>
86
+ </md-elevated-button>
87
+ </div>`
81
88
  : html ` <div></div> `}
82
89
  </div>
83
90
  </div>
84
91
  </fieldset>
85
92
  `;
86
93
  }
94
+ _applyAction(action, dimension) {
95
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
96
+ bubbles: true,
97
+ composed: true,
98
+ detail: {
99
+ callback: (selected) => {
100
+ typeof action === 'function' && action(selected[0], dimension);
101
+ }
102
+ }
103
+ }));
104
+ }
105
+ /**
106
+ * 현재 컴포넌트의 W/H/D 중 가장 큰 값을 기준으로,
107
+ * 원래 모델의 비율에 맞게 나머지 치수를 조절한다.
108
+ */
109
+ _applyProportional(action) {
110
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
111
+ bubbles: true,
112
+ composed: true,
113
+ detail: {
114
+ callback: (selected) => {
115
+ const component = selected[0];
116
+ if (!component || typeof action !== 'function')
117
+ return;
118
+ const { width: cw = 1, height: ch = 1, depth: cd = 1 } = component.state;
119
+ const { width: ow, height: oh, depth: od } = this; // 원래 모델 치수
120
+ if (!ow || !oh || !od)
121
+ return;
122
+ // 현재 치수 중 가장 큰 값과, 그에 대응하는 원래 모델 치수로 scale 계산
123
+ const maxCurrent = Math.max(cw, ch, cd);
124
+ let scale;
125
+ if (maxCurrent === cw) {
126
+ scale = cw / ow;
127
+ }
128
+ else if (maxCurrent === ch) {
129
+ scale = ch / oh;
130
+ }
131
+ else {
132
+ scale = cd / od;
133
+ }
134
+ action(component, {
135
+ width: Math.round(ow * scale),
136
+ height: Math.round(oh * scale),
137
+ depth: Math.round(od * scale)
138
+ });
139
+ }
140
+ }
141
+ }));
142
+ }
87
143
  updated(changes) {
88
144
  if (changes.has('src')) {
89
145
  this.dispatchEvent(new CustomEvent('i-need-selected', {
@@ -1 +1 @@
1
- {"version":3,"file":"property-editor-gltf-info.js","sourceRoot":"","sources":["../../src/editors/property-editor-gltf-info.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues, TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport { Component } from '@hatiolab/things-scene'\n\nimport * as THREE from 'three'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\n\nconst EXT_SPECGLOSS = 'KHR_materials_pbrSpecularGlossiness'\n\nclass SpecGlossCompat {\n name = EXT_SPECGLOSS\n extendMaterialParams() {\n return Promise.resolve()\n }\n}\n\nfunction registerSpecGlossCompat(loader: GLTFLoader) {\n loader.register(() => new SpecGlossCompat())\n}\n\n@customElement('property-editor-gltf-info')\nexport default class GLTFInfoEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n div[info] {\n display: flex;\n flex-direction: row;\n font-size: 12px;\n }\n\n div[info] * {\n align-self: center;\n }\n\n div[info] title {\n font-weight: bold;\n }\n\n div[info] md-elevated-button {\n margin-left: auto;\n --md-icon-size: 24px;\n }\n `\n ]\n\n @property({ type: String }) src?: string\n\n @state() width: number = 0\n @state() height: number = 0\n @state() depth: number = 0\n\n editorTemplate(value: any, spec: PropertySpec): TemplateResult {\n const valid = this.width && this.height && this.depth\n\n var property = spec.property || {}\n var { action } = property\n\n return html`\n <fieldset fullwidth>\n <legend><ox-i18n msgid=\"label.gltf-info\">GLTF info.</ox-i18n></legend>\n\n <div>\n <div info>\n <div label>[W x H x D] :</div>\n ${valid\n ? html` <div value>${this.width} x ${this.height} x ${this.depth}</div>\n <md-elevated-button\n @click=${(e: MouseEvent) => {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n typeof action === 'function' &&\n action(selected[0], {\n width: this.width,\n height: this.height,\n depth: this.depth\n })\n }\n }\n })\n )\n }}\n >\n <md-icon>autorenew</md-icon>\n </md-elevated-button>`\n : html` <div></div> `}\n </div>\n </div>\n </fieldset>\n `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('src')) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: async (selected: any[]) => {\n await this.fetchSourceInfo(selected[0], this.src!)\n }\n }\n })\n )\n }\n }\n\n async fetchSourceInfo(component: Component, src: string) {\n if (!src || !src.trim()) {\n return\n }\n\n try {\n const path = component.app.url(src)\n\n let gltfLoader = new GLTFLoader()\n registerSpecGlossCompat(gltfLoader)\n\n gltfLoader.setCrossOrigin('use-credentials')\n\n return new Promise((resolve: any) => {\n gltfLoader.load(path, gltf => {\n var box = new THREE.Box3().setFromObject(gltf.scene)\n var { x, y, z } = box.getSize(new THREE.Vector3())\n\n this.width = Math.floor(x)\n this.depth = Math.floor(y)\n this.height = Math.floor(z)\n\n resolve()\n })\n })\n } catch (e) {\n console.error(e)\n return\n }\n }\n}\n"]}
1
+ {"version":3,"file":"property-editor-gltf-info.js","sourceRoot":"","sources":["../../src/editors/property-editor-gltf-info.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues, TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport { Component } from '@hatiolab/things-scene'\n\nimport * as THREE from 'three'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\n\nconst EXT_SPECGLOSS = 'KHR_materials_pbrSpecularGlossiness'\n\nclass SpecGlossCompat {\n name = EXT_SPECGLOSS\n extendMaterialParams() {\n return Promise.resolve()\n }\n}\n\nfunction registerSpecGlossCompat(loader: GLTFLoader) {\n loader.register(() => new SpecGlossCompat())\n}\n\n@customElement('property-editor-gltf-info')\nexport default class GLTFInfoEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n div[info] {\n display: flex;\n flex-direction: row;\n font-size: 12px;\n }\n\n div[info] * {\n align-self: center;\n }\n\n div[info] title {\n font-weight: bold;\n }\n\n .buttons {\n display: flex;\n gap: 4px;\n margin-left: auto;\n }\n\n .buttons md-elevated-button {\n --md-icon-size: 24px;\n }\n `\n ]\n\n @property({ type: String }) declare src: string | undefined\n\n @state() declare width: number\n @state() declare height: number\n @state() declare depth: number\n\n constructor() {\n super()\n this.width = 0\n this.height = 0\n this.depth = 0\n }\n\n editorTemplate(value: any, spec: PropertySpec): TemplateResult {\n const valid = this.width && this.height && this.depth\n\n var property = spec.property || {}\n var { action } = property\n\n return html`\n <fieldset fullwidth>\n <legend><ox-i18n msgid=\"label.gltf-info\">GLTF info.</ox-i18n></legend>\n\n <div>\n <div info>\n <div label>[W x H x D] :</div>\n ${valid\n ? html` <div value>${this.width} x ${this.height} x ${this.depth}</div>\n <div class=\"buttons\">\n <md-elevated-button\n title=\"원래 크기로 설정\"\n @click=${() => {\n this._applyAction(action, {\n width: this.width,\n height: this.height,\n depth: this.depth\n })\n }}\n >\n <md-icon>autorenew</md-icon>\n </md-elevated-button>\n <md-elevated-button\n title=\"현재 크기에 비율 맞춤\"\n @click=${() => this._applyProportional(action)}\n >\n <md-icon>aspect_ratio</md-icon>\n </md-elevated-button>\n </div>`\n : html` <div></div> `}\n </div>\n </div>\n </fieldset>\n `\n }\n\n private _applyAction(action: any, dimension: { width: number; height: number; depth: number }) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n typeof action === 'function' && action(selected[0], dimension)\n }\n }\n })\n )\n }\n\n /**\n * 현재 컴포넌트의 W/H/D 중 가장 큰 값을 기준으로,\n * 원래 모델의 비율에 맞게 나머지 치수를 조절한다.\n */\n private _applyProportional(action: any) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n const component = selected[0]\n if (!component || typeof action !== 'function') return\n\n const { width: cw = 1, height: ch = 1, depth: cd = 1 } = component.state\n const { width: ow, height: oh, depth: od } = this // 원래 모델 치수\n\n if (!ow || !oh || !od) return\n\n // 현재 치수 중 가장 큰 값과, 그에 대응하는 원래 모델 치수로 scale 계산\n const maxCurrent = Math.max(cw, ch, cd)\n let scale: number\n\n if (maxCurrent === cw) {\n scale = cw / ow\n } else if (maxCurrent === ch) {\n scale = ch / oh\n } else {\n scale = cd / od\n }\n\n action(component, {\n width: Math.round(ow * scale),\n height: Math.round(oh * scale),\n depth: Math.round(od * scale)\n })\n }\n }\n })\n )\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('src')) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: async (selected: any[]) => {\n await this.fetchSourceInfo(selected[0], this.src!)\n }\n }\n })\n )\n }\n }\n\n async fetchSourceInfo(component: Component, src: string) {\n if (!src || !src.trim()) {\n return\n }\n\n try {\n const path = component.app.url(src)\n\n let gltfLoader = new GLTFLoader()\n registerSpecGlossCompat(gltfLoader)\n\n gltfLoader.setCrossOrigin('use-credentials')\n\n return new Promise((resolve: any) => {\n gltfLoader.load(path, gltf => {\n var box = new THREE.Box3().setFromObject(gltf.scene)\n var { x, y, z } = box.getSize(new THREE.Vector3())\n\n this.width = Math.floor(x)\n this.depth = Math.floor(y)\n this.height = Math.floor(z)\n\n resolve()\n })\n })\n } catch (e) {\n console.error(e)\n return\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/scene-visualizer",
3
3
  "description": "visualizer component for operato-scene",
4
4
  "author": "heartyoh",
5
- "version": "10.0.0-beta.2",
5
+ "version": "10.0.0-beta.3",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
@@ -63,5 +63,5 @@
63
63
  "prettier --write"
64
64
  ]
65
65
  },
66
- "gitHead": "350ece104754d007967cf8e3f54d0d157465e94a"
66
+ "gitHead": "11790eeafaf6b7949e2f44e2ae670c0364c3a47d"
67
67
  }