123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464 |
- /**
- * Inspector.js
- *
- * @author realor
- */
- import { Panel } from './Panel.js'
- import { Tree } from './Tree.js'
- import { Dialog } from './Dialog.js'
- import { ConfirmDialog } from './ConfirmDialog.js'
- import { Application } from './Application.js'
- import { Solid } from '../core/Solid.js'
- import { SolidGeometry } from '../core/SolidGeometry.js'
- import { Formula } from '../formula/Formula.js'
- import { ObjectUtils } from '../utils/ObjectUtils.js'
- import { FormulaDialog } from './FormulaDialog.js'
- import { PropertiesDialog } from './PropertiesDialog.js'
- import { PropertyDialog } from './PropertyDialog.js'
- import { BuilderDialog } from './BuilderDialog.js'
- import { ControllerDialog } from './ControllerDialog.js'
- import { Controls } from './Controls.js'
- import { I18N } from '../i18n/I18N.js'
- import * as THREE from '../lib/three.module.js'
- class Inspector extends Panel {
- static mainMaterialProperties = [
- 'id',
- 'uuid',
- 'type',
- 'name',
- 'color',
- 'specular',
- 'emissive',
- 'shininess',
- 'opacity',
- 'transparent',
- 'side',
- 'emissiveIntensity',
- 'fog',
- 'depthTest',
- 'depthWrite',
- 'polygonOffset',
- 'polygonOffsetFactor',
- 'polygonOffsetUnits',
- 'sizeAttenuation',
- 'map'
- ]
- constructor(application) {
- super(application)
- this.position = 'right'
- this.object = null
- this.objects = null
- this.state = {}
- this.objectSectionName = 'Object'
- this.materialSectionName = 'Material'
- this.builderSectionName = 'Builder'
- this.formulasSectionName = 'Formulas'
- this.geometrySectionName = 'Geometry'
- this.propertiesSectionName = 'Properties'
- this.controllersSectionName = 'Controllers'
- this.renderers = {}
- this.editors = {}
- this.addRenderer(StringRenderer)
- this.addRenderer(NumberRenderer)
- this.addRenderer(BooleanRenderer)
- this.addRenderer(VectorRenderer)
- this.addRenderer(EulerRenderer)
- this.addRenderer(FormulaRenderer)
- this.addRenderer(ColorRenderer)
- this.addRenderer(TextureRenderer)
- this.addRenderer(Object3DRenderer)
- this.addEditor(StringEditor)
- this.addEditor(NumberEditor)
- this.addEditor(BooleanEditor)
- this.addEditor(VectorEditor)
- this.addEditor(EulerEditor)
- this.addEditor(FormulaEditor)
- this.addEditor(ColorEditor)
- this.addEditor(TextureEditor)
- this.objectPanelElem = document.createElement('div')
- this.bodyElem.appendChild(this.objectPanelElem)
- this.objectPanelElem.className = 'inspector_card'
- this.toolBarElem = document.createElement('div')
- this.objectPanelElem.appendChild(this.toolBarElem)
- this.toolBarElem.className = 'inspector_toolbar'
- this.listButton = Controls.addButton(
- this.toolBarElem,
- 'list',
- null,
- event => {
- this.listPanelElem.style.display = ''
- this.objectPanelElem.style.display = 'none'
- },
- 'list'
- )
- I18N.set(this.listButton, 'title', 'button.list')
- I18N.set(this.listButton, 'alt', 'button.list')
- this.previousButton = Controls.addButton(
- this.toolBarElem,
- 'previous',
- null,
- event => {
- if (this.objectIndex > 0) {
- this.showProperties(this.objects[this.objectIndex - 1])
- this.centerObject()
- }
- },
- 'previous'
- )
- I18N.set(this.previousButton, 'title', 'button.previous')
- I18N.set(this.previousButton, 'alt', 'button.previous')
- this.indexElem = document.createElement('span')
- this.toolBarElem.appendChild(this.indexElem)
- this.nextButton = Controls.addButton(
- this.toolBarElem,
- 'next',
- null,
- event => {
- if (this.objectIndex < this.objects.length - 1) {
- this.showProperties(this.objects[this.objectIndex + 1])
- this.centerObject()
- }
- },
- 'next'
- )
- I18N.set(this.nextButton, 'title', 'button.next')
- I18N.set(this.nextButton, 'alt', 'button.next')
- this.selectButton = Controls.addButton(
- this.toolBarElem,
- 'select',
- null,
- event => {
- this.application.selection.set(this.object)
- },
- 'select'
- )
- I18N.set(this.selectButton, 'title', 'button.select')
- I18N.set(this.selectButton, 'alt', 'button.select')
- this.propertiesElem = document.createElement('div')
- this.objectPanelElem.appendChild(this.propertiesElem)
- this.propertiesElem.className = 'inspector_properties'
- this.listPanelElem = document.createElement('div')
- this.bodyElem.appendChild(this.listPanelElem)
- this.listPanelElem.className = 'inspector_card'
- this.objectIndex = -1
- this.anchoredSectionName = null
- this.edition = {
- object: null,
- propertyName: null,
- renderer: null,
- editor: null,
- propElem: null
- }
- application.addEventListener('selection', event => {
- this.showSelectedObjects(event.objects)
- if (event.objects.length === 1) {
- this.showProperties(event.objects[0])
- }
- })
- application.addEventListener('scene', event => {
- if (event.type === 'nodeChanged' && this.object && event.objects.includes(this.object) && event.source !== this) {
- if (event.properties instanceof Array) {
- for (let propertyName of event.properties) {
- this.updateProperty(this.object, propertyName)
- }
- } else {
- this.showProperties(this.object)
- }
- }
- })
- this.title = 'tool.inspector.label'
- }
- showSelectedObjects(objects) {
- this.listPanelElem.style.display = ''
- this.objectPanelElem.style.display = 'none'
- this.propertiesElem.innerHTML = ''
- this.listPanelElem.innerHTML = ''
- this.objects = objects
- this.objectIndex = objects.length >= 0 ? 0 : -1
- const infoElem = document.createElement('div')
- infoElem.className = 'inspector_info'
- I18N.set(infoElem, 'innerHTML', 'message.objects_selected', objects.length)
- this.application.i18n.update(infoElem)
- this.listPanelElem.appendChild(infoElem)
- const selectionTree = new Tree(this.listPanelElem)
- for (let i = 0; i < objects.length; i++) {
- let object = objects[i]
- let label = object.name || object.id
- let className = this.getObjectClassNames(object)
- selectionTree.addNode(
- label,
- event => {
- this.showProperties(object)
- this.centerObject()
- },
- className
- )
- }
- }
- showProperties(object) {
- if (this.edition.object) {
- // edition in progress
- this.stopEdition()
- }
- let objectChanged
- if (this.object !== object) {
- this.object = object
- objectChanged = true
- } else {
- objectChanged = false
- }
- this.objectIndex = this.objects.indexOf(object)
- this.updateToolBar()
- this.propertiesElem.innerHTML = ''
- this.objectPanelElem.style.display = ''
- this.listPanelElem.style.display = 'none'
- let anchoredSectionElem = null
- if (object) {
- let topListElem = document.createElement('ul')
- topListElem.className = 'inspector'
- this.propertiesElem.appendChild(topListElem)
- if (this.state[this.objectSectionName] === undefined) {
- this.state[this.objectSectionName] = 'expanded'
- }
- // object
- let objListElem = this.createSection(this.objectSectionName, topListElem, [this.createClearAnchorSectionAction()])
- this.createReadOnlyProperty(objListElem, object, 'id')
- for (let propertyName in object) {
- if (this.isSupportedProperty(propertyName)) {
- if (this.isReadOnlyProperty(propertyName)) {
- this.createReadOnlyProperty(objListElem, object, propertyName)
- } else {
- this.createWriteableProperty(objListElem, object, propertyName)
- }
- }
- }
- if (object instanceof Solid) {
- this.createWriteableProperty(objListElem, object, 'edgesVisible')
- this.createWriteableProperty(objListElem, object, 'facesVisible')
- this.createWriteableProperty(objListElem, object, 'castShadow')
- this.createWriteableProperty(objListElem, object, 'receiveShadow')
- }
- let material = object.material
- if (material) {
- // material
- if (this.state[this.materialSectionName] === undefined) {
- this.state[this.materialSectionName] = 'expanded'
- }
- let materialListElem = this.createSection(this.materialSectionName, topListElem, [this.createChangeMaterialAction(object)])
- for (let propertyName of Inspector.mainMaterialProperties) {
- if (propertyName in material) {
- if (this.isReadOnlyProperty(propertyName) || material === Solid.EdgeMaterial || material === Solid.FaceMaterial) {
- this.createReadOnlyProperty(materialListElem, material, propertyName)
- } else {
- if (propertyName === 'map') {
- this.createWriteableProperty(materialListElem, material, propertyName, this.renderers.TextureRenderer, this.editors.TextureEditor)
- } else {
- this.createWriteableProperty(materialListElem, material, propertyName)
- }
- }
- }
- }
- }
- // builder
- if (this.state[this.builderSectionName] === undefined) {
- this.state[this.builderSectionName] = 'expanded'
- }
- let builderListElem = this.createSection(this.builderSectionName, topListElem, [this.createSetBuilderAction(object)])
- let builder = object.builder
- if (builder) {
- this.createReadOnlyProperty(builderListElem, builder.constructor, 'type', builder.constructor.name)
- for (let propertyName in builder) {
- let propertyValue = builder[propertyName]
- if (this.isSupportedProperty(propertyName)) {
- if (propertyValue !== null) {
- this.createWriteableProperty(builderListElem, builder, propertyName)
- }
- }
- }
- }
- // formulas
- if (this.state[this.formulasSectionName] === undefined) {
- this.state[this.formulasSectionName] = 'expanded'
- }
- let formulasListElem = this.createSection(this.formulasSectionName, topListElem, [this.createAddFormulaAction(object)])
- const formulas = Formula.getAll(object)
- for (let path in formulas) {
- this.createWriteableProperty(formulasListElem, formulas, path)
- }
- // geometry
- let geometry = object.geometry
- if (geometry) {
- if (this.state[this.geometrySectionName] === undefined) {
- this.state[this.geometrySectionName] = 'expanded'
- }
- let geomListElem = this.createSection(this.geometrySectionName, topListElem)
- this.createReadOnlyProperty(geomListElem, geometry, 'id')
- this.createReadOnlyProperty(geomListElem, geometry, 'uuid')
- this.createReadOnlyProperty(geomListElem, geometry, 'type')
- if (geometry instanceof SolidGeometry) {
- this.createReadOnlyProperty(geomListElem, geometry, 'vertices', geometry.vertices.length)
- this.createReadOnlyProperty(geomListElem, geometry, 'faces', geometry.faces.length)
- this.createReadOnlyProperty(geomListElem, geometry, 'isManifold')
- this.createReadOnlyProperty(geomListElem, geometry, 'smoothAngle')
- } else if (geometry instanceof THREE.BufferGeometry) {
- for (let name in geometry.attributes) {
- this.createReadOnlyProperty(geomListElem, geometry.attributes[name], name, geometry.attributes[name].array.length)
- }
- }
- }
- // userData
- let userData = object.userData
- if (this.state[this.propertiesSectionName] === undefined) {
- this.state[this.propertiesSectionName] = 'expanded'
- }
- let propListElem = this.createSection(this.propertiesSectionName, topListElem, [this.createAddPropertyAction(object, userData), this.createEditPropertiesAction(object)])
- for (let propertyName in userData) {
- let propertyValue = userData[propertyName]
- if (propertyValue !== null && typeof propertyValue === 'object') {
- let dictName = propertyName
- let dictionary = propertyValue
- if (this.state[dictName] === undefined) {
- this.state[dictName] = 'expanded'
- }
- let dictListElem = this.createSection(dictName, propListElem, [this.createAddPropertyAction(object, dictionary), this.createAnchorSectionAction(object, dictName)])
- for (let dictPropertyName in dictionary) {
- this.createWriteableProperty(dictListElem, dictionary, dictPropertyName)
- }
- if (dictName === this.anchoredSectionName) {
- anchoredSectionElem = dictListElem.parentElement
- }
- } else {
- this.createWriteableProperty(propListElem, userData, propertyName)
- }
- }
- //controllers
- // let controllers = object.controllers
- // if (this.state[this.controllersSectionName] === undefined) {
- // this.state[this.controllersSectionName] = 'expanded'
- // }
- // let controllersListElem = this.createSection(this.controllersSectionName, topListElem, [this.createAddControllerAction(object)])
- // if (controllers) {
- // for (let name in controllers) {
- // let controller = controllers[name]
- // if (this.state[name] === undefined) {
- // this.state[name] = 'expanded'
- // }
- // let controlListElem = this.createSection(name, controllersListElem, [
- // this.createStartControllerAction(controller),
- // this.createStopControllerAction(controller),
- // this.createRemoveControllerAction(controller),
- // this.createAnchorSectionAction(object, name)
- // ])
- // this.createReadOnlyProperty(controlListElem, controller, 'type', controller.constructor.name)
- // this.createReadOnlyProperty(controlListElem, controller, 'started', controller.isStarted())
- // for (let propertyName in controller) {
- // if (propertyName !== 'name' && propertyName !== 'object' && !propertyName.startsWith('_')) {
- // this.createWriteableProperty(controlListElem, controller, propertyName)
- // }
- // }
- // if (name === this.anchoredSectionName) {
- // anchoredSectionElem = controlListElem.parentElement
- // }
- // }
- // }
- this.application.i18n.updateTree(this.objectPanelElem)
- if (objectChanged) {
- this.markAnchoredSection(anchoredSectionElem)
- }
- }
- }
- getObjectClassNames(object) {
- let classList = [object.type]
- if (object.userData.IFC && object.userData.IFC.ifcClassName) {
- classList.push(object.userData.IFC.ifcClassName)
- }
- if (object.builder) {
- classList.push(object.builder.constructor.name)
- }
- return classList.join(' ')
- }
- updateToolBar() {
- this.indexElem.innerHTML = this.objectIndex + 1 + ' / ' + this.objects.length
- const objectCount = this.objects.length
- if (objectCount < 2) {
- this.toolBarElem.style.display = 'none'
- this.propertiesElem.style.top = '0px'
- } else {
- this.toolBarElem.style.display = ''
- this.propertiesElem.style.top = ''
- this.previousButton.disabled = this.objectIndex <= 0
- this.nextButton.disabled = this.objectIndex >= objectCount - 1
- }
- }
- centerObject() {
- const application = this.application
- const object = this.object
- if (object) {
- const container = application.container
- const aspect = container.clientWidth / container.clientHeight
- const camera = application.camera
- ObjectUtils.zoomAll(camera, [object], aspect, true)
- application.notifyObjectsChanged(camera, this)
- }
- }
- createSection(name, parentElem, actions = null) {
- let labelListener = event => {
- let labelElem = event.srcElement || event.target
- labelElem.className = labelElem.className === 'expand' ? 'collapse' : 'expand'
- let listElem = labelElem.parentNode.querySelector('ul')
- listElem.className = listElem.className === 'expanded' ? 'collapsed' : 'expanded'
- let sectionName = labelElem.id.substring(8)
- this.state[sectionName] = listElem.className
- }
- let sectionElem = document.createElement('li')
- sectionElem.className = 'section'
- parentElem.appendChild(sectionElem)
- if (this.state[name] === 'expanded' && this.firstExpandedSectionElem === null) {
- this.firstExpandedSectionElem = sectionElem
- }
- let labelElem = document.createElement('a')
- labelElem.href = '#'
- labelElem.id = 'section-' + name
- labelElem.innerHTML = name
- sectionElem.appendChild(labelElem)
- labelElem.className = this.state[name] === 'collapsed' ? 'expand' : 'collapse'
- labelElem.addEventListener('click', labelListener)
- if (actions instanceof Array) {
- for (let action of actions) {
- let actionElem = document.createElement('button')
- actionElem.className = action.className
- I18N.set(actionElem, 'alt', action.label)
- I18N.set(actionElem, 'title', action.label)
- actionElem.addEventListener('click', action.listener)
- sectionElem.appendChild(actionElem)
- }
- }
- let listElem = document.createElement('ul')
- listElem.className = this.state[name]
- sectionElem.appendChild(listElem)
- return listElem
- }
- createReadOnlyProperty(parentElem, object, propertyName, propertyValue = object[propertyName]) {
- this.createProperty(parentElem, object, propertyName, propertyValue)
- }
- createWriteableProperty(parentElem, object, propertyName, renderer, editor) {
- let propertyValue = object[propertyName]
- editor = editor || this.getEditor(propertyValue)
- this.createProperty(parentElem, object, propertyName, propertyValue, renderer, editor)
- }
- createProperty(parentElem, object, propertyName, propertyValue, renderer = null, editor = null) {
- renderer = renderer || this.getRenderer(propertyValue)
- if (renderer) {
- let propElem = document.createElement('li')
- if (object instanceof THREE.Object3D) {
- propElem.id = 'inspector_' + propertyName
- }
- propElem.className = 'property ' + renderer.getClassName(propertyValue)
- parentElem.appendChild(propElem)
- let labelElem = document.createElement('a')
- labelElem.href = '#'
- labelElem.innerHTML = propertyName + ':'
- labelElem.className = 'label'
- propElem.appendChild(labelElem)
- this.createValueElem(propElem, object, propertyName, propertyValue, renderer, editor)
- if (editor) {
- labelElem.addEventListener(
- 'click',
- event => {
- event.preventDefault()
- this.startEdition(propElem, object, propertyName, renderer, editor)
- },
- false
- )
- propElem.className += ' editable'
- }
- }
- }
- createValueElem(propElem, object, propertyName, propertyValue, renderer, editor) {
- let valueElem = renderer.render(propertyValue, !Boolean(editor))
- if (valueElem) {
- propElem.appendChild(valueElem)
- if (editor) {
- valueElem.addEventListener('click', () => this.startEdition(propElem, object, propertyName, renderer, editor), false)
- }
- }
- }
- createChangeMaterialAction(object) {
- const listener = () => {
- object.material = object.material.clone()
- this.application.notifyObjectsChanged(this.object, '')
- }
- return {
- className: 'button edit',
- label: 'button.change_material',
- listener: listener
- }
- }
- createSetBuilderAction(object) {
- const listener = () => {
- const dialog = new BuilderDialog(this.application, object)
- dialog.show()
- }
- return {
- className: 'button edit',
- label: 'button.object_builder',
- listener: listener
- }
- }
- createAddPropertyAction(object, dictionary) {
- const listener = () => {
- const dialog = new PropertyDialog(this.application, object, dictionary)
- dialog.show()
- }
- return {
- className: 'button add',
- label: 'button.add_property',
- listener: listener
- }
- }
- createEditPropertiesAction(object) {
- const listener = () => {
- const dialog = new PropertiesDialog(this.application, object)
- dialog.show()
- }
- return {
- className: 'button edit',
- label: 'button.edit_properties',
- listener: listener
- }
- }
- createClearAnchorSectionAction() {
- const listener = event => {
- this.anchoredSectionName = null
- this.markAnchoredSection()
- }
- return {
- className: 'button anchor',
- label: 'button.anchor_section',
- listener: listener
- }
- }
- createAnchorSectionAction(object, dictName) {
- const listener = event => {
- this.anchoredSectionName = dictName
- const elem = event.srcElement.parentElement
- this.markAnchoredSection(elem)
- }
- return {
- className: 'button anchor',
- label: 'button.anchor_section',
- listener: listener
- }
- }
- createAddFormulaAction(object) {
- const listener = () => {
- const dialog = new FormulaDialog(this.application, object)
- dialog.show()
- }
- return {
- className: 'button add',
- label: 'button.add_formula',
- listener: listener
- }
- }
- createAddControllerAction(object) {
- const listener = () => {
- const dialog = new ControllerDialog(this.application, object)
- dialog.show()
- }
- return {
- className: 'button add',
- label: 'button.add_controller',
- listener: listener
- }
- }
- createRemoveControllerAction(controller) {
- const listener = () => {
- ConfirmDialog.create('title.remove_controller', 'question.remove_controller', controller.name)
- .setAction(() => {
- controller.stop()
- let object = controller.object
- delete object.controllers[controller.name]
- this.showProperties(object)
- })
- .setI18N(this.application.i18n)
- .show()
- }
- return {
- className: 'button remove',
- label: 'button.remove_controller',
- listener: listener
- }
- }
- createStartControllerAction(controller) {
- const listener = () => {
- if (!controller.isStarted()) {
- controller.start()
- let object = controller.object
- this.showProperties(object)
- }
- }
- return {
- className: 'button start',
- label: 'button.start_controller',
- listener: listener
- }
- }
- createStopControllerAction(controller) {
- const listener = () => {
- if (controller.isStarted()) {
- controller.stop()
- let object = controller.object
- this.showProperties(object)
- }
- }
- return {
- className: 'button stop',
- label: 'button.stop_controller',
- listener: listener
- }
- }
- markAnchoredSection(sectionElem) {
- let anchored = this.propertiesElem.getElementsByClassName('anchored')
- for (let anchoredElem of anchored) {
- anchoredElem.classList.remove('anchored')
- }
- if (sectionElem) {
- sectionElem.firstChild.classList.add('anchored')
- this.propertiesElem.scrollTo(0, sectionElem.offsetTop)
- } else {
- this.propertiesElem.scrollTo(0, 0)
- }
- }
- startEdition(propElem, object, propertyName, renderer, editor) {
- if (this.edition.object !== null) {
- this.stopEdition()
- }
- let propertyValue = object[propertyName]
- this.edition.object = object
- this.edition.propertyName = propertyName
- this.edition.renderer = renderer
- this.edition.editor = editor
- this.edition.propElem = propElem
- let editorElem = editor.edit(object, propertyName, propertyValue)
- if (editorElem) {
- this.application.i18n.updateTree(editorElem)
- propElem.removeChild(propElem.lastChild)
- propElem.appendChild(editorElem)
- if (editorElem.focus) editorElem.focus()
- }
- }
- endEdition() {
- this.stopEdition()
- this.application.notifyObjectsChanged(this.object, this)
- }
- stopEdition() {
- let propElem = this.edition.propElem
- if (propElem === null || this.edition.object === null) return
- propElem.removeChild(propElem.lastChild)
- let propertyValue = this.edition.object[this.edition.propertyName]
- this.createValueElem(propElem, this.edition.object, this.edition.propertyName, propertyValue, this.edition.renderer, this.edition.editor)
- propElem.firstChild.focus()
- this.clearEdition()
- }
- clearEdition() {
- this.edition.object = null
- this.edition.propertyName = null
- this.edition.renderer = null
- this.edition.editor = null
- this.edition.propElem = null
- }
- updateProperty(object, propertyName, renderer) {
- let propElem = document.getElementById('inspector_' + propertyName)
- if (propElem) {
- propElem.removeChild(propElem.lastChild)
- let propertyValue = object[propertyName]
- renderer = renderer || this.getRenderer(propertyValue)
- this.createValueElem(propElem, object, propertyName, propertyValue, renderer)
- }
- }
- isSupportedProperty(propertyName) {
- if (propertyName[0] === '_') return false
- if (propertyName === 'material') return false
- return true
- }
- isReadOnlyProperty(propertyName) {
- if (propertyName === 'id') return true
- if (propertyName === 'uuid') return true
- if (propertyName === 'type') return true
- if (propertyName === 'matrixWorldNeedsUpdate') return true
- if (propertyName === 'needsRebuild') return true
- if (propertyName === 'needsMarking') return true
- if (propertyName.indexOf('is') === 0) return true
- return false
- }
- addRenderer(rendererClass) {
- this.renderers[rendererClass.name] = new rendererClass(this)
- }
- addEditor(editorClass) {
- this.editors[editorClass.name] = new editorClass(this)
- }
- getRenderer(value) {
- for (let rendererName in this.renderers) {
- let renderer = this.renderers[rendererName]
- if (renderer.isSupported(value)) {
- return renderer
- }
- }
- return null
- }
- getEditor(value) {
- for (let editorName in this.editors) {
- let editor = this.editors[editorName]
- if (editor.isSupported(value)) {
- return editor
- }
- }
- return null
- }
- }
- /* PropertyRenderers */
- class PropertyRenderer {
- constructor(inspector) {
- this.inspector = inspector
- }
- isSupported(value, type) {
- return false
- }
- getClassName(value) {
- return ''
- }
- render(
- value,
- disabled // returns elem
- ) {
- return null
- }
- }
- class StringRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'string'
- }
- getClassName(value) {
- return 'string'
- }
- render(text, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- valueElem.innerHTML = text
- return valueElem
- }
- }
- class NumberRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'number'
- }
- getClassName(value) {
- return 'number'
- }
- render(number, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- valueElem.innerHTML = Math.round(number * 1000) / 1000
- return valueElem
- }
- }
- class BooleanRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'boolean'
- }
- getClassName(value) {
- return 'boolean'
- }
- render(value, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- let checkboxElem = document.createElement('input')
- checkboxElem.type = 'checkbox'
- checkboxElem.checked = value
- checkboxElem.tabIndex = -1
- if (disabled) checkboxElem.disabled = true
- valueElem.appendChild(checkboxElem)
- return valueElem
- }
- }
- class VectorRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Vector3
- }
- getClassName(value) {
- return 'vector'
- }
- render(vector, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- let round = function(value) {
- var precision = 1000
- return Math.round(precision * value) / precision
- }
- let out = '(' + round(vector.x) + ', ' + round(vector.y) + ', ' + round(vector.z) + ')'
- valueElem.innerHTML = out
- return valueElem
- }
- }
- class EulerRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Euler
- }
- getClassName(value) {
- return 'euler'
- }
- render(euler, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- let angle = function(value) {
- var precision = 1000
- return Math.round(precision * THREE.MathUtils.radToDeg(value)) / precision
- }
- let out = '(' + angle(euler.x) + 'º, ' + angle(euler.y) + 'º, ' + angle(euler.z) + 'º)'
- valueElem.innerHTML = out
- return valueElem
- }
- }
- class FormulaRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof Formula
- }
- getClassName(value) {
- return 'formula'
- }
- render(formula, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- valueElem.innerHTML = formula.expression
- return valueElem
- }
- }
- class ColorRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Color
- }
- getClassName(value) {
- return 'color'
- }
- render(color, disabled) {
- const rgb = 'rgb(' + Math.round(255 * color.r) + ', ' + Math.round(255 * color.g) + ', ' + Math.round(255 * color.b) + ')'
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (disabled) valueElem.classList.add('disabled')
- let codeElem = document.createElement('span')
- codeElem.innerHTML = rgb
- valueElem.appendChild(codeElem)
- let colorElem = document.createElement('span')
- colorElem.className = 'color'
- colorElem.style.backgroundColor = rgb
- colorElem.alt = rgb
- colorElem.title = rgb
- valueElem.appendChild(colorElem)
- return valueElem
- }
- }
- class Object3DRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Object3D
- }
- getClassName(value) {
- return 'object3D'
- }
- render(object, disabled) {
- let valueElem = document.createElement('a')
- valueElem.href = '#'
- valueElem.className = 'value'
- valueElem.innerHTML = object.name || 'object-' + object.id
- valueElem.addEventListener('click', () => this.inspector.application.selection.set(object))
- return valueElem
- }
- }
- class TextureRenderer extends PropertyRenderer {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Texture
- }
- getClassName(value) {
- return 'texture'
- }
- render(texture, disabled) {
- let valueElem = document.createElement('span')
- valueElem.className = 'value'
- if (texture) {
- if (texture.name) {
- valueElem.innerHTML = texture.name
- } else if (texture.image) {
- valueElem.innerHTML = texture.image.src
- } else {
- valueElem.innerHTML = ''
- }
- }
- return valueElem
- }
- }
- /* PropertyEditors */
- class PropertyEditor {
- constructor(inspector) {
- this.inspector = inspector
- }
- isSupported(value) {
- return false
- }
- edit(
- object,
- propertyName,
- value // returns the editor element
- ) {
- return null
- }
- }
- class StringEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'string'
- }
- edit(object, propertyName, text) {
- let valueElem = document.createElement('input')
- valueElem.className = 'value'
- valueElem.value = text
- valueElem.addEventListener(
- 'keydown',
- event => {
- if (event.keyCode === 13) {
- object[propertyName] = valueElem.value
- this.inspector.endEdition()
- } else if (event.keyCode === 27) {
- this.inspector.stopEdition()
- }
- },
- false
- )
- return valueElem
- }
- }
- class NumberEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'number'
- }
- edit(object, propertyName, number) {
- let valueElem = document.createElement('input')
- valueElem.className = 'value'
- valueElem.value = '' + number
- valueElem.type = 'number'
- valueElem.addEventListener(
- 'keydown',
- event => {
- if (event.keyCode === 13) {
- number = parseFloat(valueElem.value)
- if (!isNaN(number)) {
- object[propertyName] = number
- this.inspector.endEdition()
- }
- } else if (event.keyCode === 27) {
- this.inspector.stopEdition()
- }
- },
- false
- )
- return valueElem
- }
- }
- class BooleanEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return typeof value === 'boolean'
- }
- edit(object, propertyName, value) {
- let checked = value
- object[propertyName] = !checked
- this.inspector.endEdition()
- return null
- }
- }
- class DimensionEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- formatValue(value) {
- return value
- }
- createInstance(x, y, z) {
- return { x: x, y: y, z: z }
- }
- edit(object, propertyName, vector) {
- let dimId = 'dim_edit_'
- const parseDimension = dim => {
- let valueElem = document.getElementById(dimId + dim)
- let value = valueElem.value
- let num = parseFloat(value)
- return isNaN(num) ? vector[dim] : num
- }
- const endEdition = () => {
- let x = parseDimension('x')
- let y = parseDimension('y')
- let z = parseDimension('z')
- object[propertyName].copy(this.createInstance(x, y, z))
- if (object instanceof THREE.Object3D) {
- object.updateMatrix()
- }
- this.inspector.endEdition()
- }
- const keyListener = event => {
- if (event.keyCode === 13) {
- endEdition()
- } else if (event.keyCode === 27) {
- this.inspector.stopEdition()
- }
- }
- const createDimensionEditor = (vector, dim) => {
- let itemElem = document.createElement('li')
- let labelElem = document.createElement('label')
- labelElem.innerHTML = dim + ':'
- labelElem.htmlFor = dimId + dim
- let valueElem = document.createElement('input')
- valueElem.id = dimId + dim
- valueElem.type = 'number'
- valueElem.className = 'value'
- valueElem.value = this.formatValue(vector[dim])
- valueElem.addEventListener('keydown', keyListener, false)
- itemElem.appendChild(labelElem)
- itemElem.appendChild(valueElem)
- return itemElem
- }
- let listElem = document.createElement('ul')
- listElem.className = 'list_3'
- listElem.appendChild(createDimensionEditor(vector, 'x'))
- listElem.appendChild(createDimensionEditor(vector, 'y'))
- listElem.appendChild(createDimensionEditor(vector, 'z'))
- listElem.focus = () => document.getElementById(dimId + 'x').focus()
- return listElem
- }
- }
- class VectorEditor extends DimensionEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Vector3
- }
- createInstance(x, y, z) {
- return new THREE.Vector3(x, y, z)
- }
- }
- class EulerEditor extends DimensionEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Euler
- }
- formatValue(value) {
- const precision = 10000000
- return Math.round(precision * THREE.MathUtils.radToDeg(value)) / precision
- }
- createInstance(x, y, z) {
- let xrad = THREE.MathUtils.degToRad(x)
- let yrad = THREE.MathUtils.degToRad(y)
- let zrad = THREE.MathUtils.degToRad(z)
- return new THREE.Euler(xrad, yrad, zrad, 'XYZ')
- }
- }
- class FormulaEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof Formula
- }
- edit(object, propertyName, formula) {
- const inspector = this.inspector
- const dialog = new FormulaDialog(inspector.application, inspector.object, formula)
- dialog.show()
- this.inspector.clearEdition()
- return null
- }
- }
- class ColorEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Color
- }
- edit(object, propertyName, color) {
- const groupElem = document.createElement('span')
- groupElem.className = 'value'
- const rgb = 'rgb(' + Math.round(255 * color.r) + ', ' + Math.round(255 * color.g) + ', ' + Math.round(255 * color.b) + ')'
- let codeElem = document.createElement('span')
- codeElem.innerHTML = rgb
- codeElem.style.color = '#6060c0'
- codeElem.addEventListener('click', () => {
- colorElem.focus()
- colorElem.click()
- })
- groupElem.appendChild(codeElem)
- let hexString = '#' + color.getHexString()
- const sampleElem = document.createElement('label')
- sampleElem.className = 'color'
- sampleElem.style.backgroundColor = hexString
- sampleElem.alt = rgb
- sampleElem.title = rgb
- sampleElem.style.borderColor = '#6060c0'
- groupElem.appendChild(sampleElem)
- const colorElem = document.createElement('input')
- colorElem.className = 'value'
- colorElem.type = 'color'
- colorElem.value = '#' + color.getHexString()
- colorElem.style.visibility = 'hidden'
- colorElem.style.width = '0'
- colorElem.style.height = '0'
- sampleElem.appendChild(colorElem)
- colorElem.addEventListener('change', () => {
- object[propertyName].set(colorElem.value)
- document.body.removeEventListener('keydown', keyDownListener)
- document.body.removeEventListener('pointerdown', pointerDownListener)
- this.inspector.endEdition()
- })
- const cancel = () => {
- if (colorElem.value.toLowerCase() !== '#' + color.getHexString()) return // ignore: it is a change
- document.body.removeEventListener('keydown', keyDownListener)
- document.body.removeEventListener('pointerdown', pointerDownListener)
- this.inspector.stopEdition()
- }
- const keyDownListener = event => {
- if (event.keyCode === 27) cancel()
- }
- const pointerDownListener = event => {
- if (event.srcElement.parentNode !== groupElem) cancel()
- }
- document.body.addEventListener('keydown', keyDownListener)
- document.body.addEventListener('pointerdown', pointerDownListener)
- groupElem.focus = () => {
- colorElem.focus()
- colorElem.click()
- }
- return groupElem
- }
- }
- class TextureEditor extends PropertyEditor {
- constructor(inspector) {
- super(inspector)
- }
- isSupported(value) {
- return value instanceof THREE.Texture
- }
- edit(material, propertyName, texture) {
- let valueElem = document.createElement('input')
- valueElem.className = 'value'
- let value = ''
- if (texture) {
- if (texture.name) {
- value = texture.name
- } else if (texture.image) {
- value = texture.image.src
- }
- }
- valueElem.value = value
- valueElem.addEventListener(
- 'keydown',
- event => {
- if (event.keyCode === 13) {
- let imagePath = valueElem.value.trim()
- if (value === imagePath) {
- this.inspector.stopEdition()
- } else {
- if (imagePath === '') {
- if (material[propertyName] !== null) material.needsUpdate = true
- material[propertyName] = null
- this.inspector.endEdition()
- } else {
- const manager = this.inspector.application.loadingManager
- if (material[propertyName] === null) material.needsUpdate = true
- const textureLoader = new THREE.TextureLoader(manager)
- const newTexture = textureLoader.load(imagePath, () => this.inspector.endEdition())
- newTexture.name = imagePath
- material[propertyName] = newTexture
- }
- if (texture) texture.dispose()
- }
- } else if (event.keyCode === 27) {
- this.inspector.stopEdition()
- }
- },
- false
- )
- return valueElem
- }
- }
- export { Inspector }
|