diff --git a/source/engine/main.js b/source/engine/main.js index 4f29d50..4e7251e 100644 --- a/source/engine/main.js +++ b/source/engine/main.js @@ -65,7 +65,6 @@ import { HasHighpDriverIssue, GetShadingType, ConvertThreeColorToColor, ConvertC import { Camera, CameraIsEqual3D } from './viewer/camera.js'; import { GetIntegerFromStyle, GetDomElementExternalWidth, GetDomElementExternalHeight, GetDomElementInnerDimensions, GetDomElementClientCoordinates, CreateDomElement, AddDomElement, AddDiv, ClearDomElement, InsertDomElementBefore, InsertDomElementAfter, ShowDomElement, IsDomElementVisible, SetDomElementWidth, SetDomElementHeight, GetDomElementOuterWidth, GetDomElementOuterHeight, SetDomElementOuterWidth, SetDomElementOuterHeight, AddCheckbox, AddRangeSlider, AddSelect, AddToggle, CreateDiv } from './viewer/domutils.js'; import { EmbeddedViewer, Init3DViewerElement, Init3DViewerElements } from './viewer/embeddedviewer.js'; -import { MeasureTool } from './viewer/measuretool.js'; import { MouseInteraction, TouchInteraction, ClickDetector, Navigation, NavigationType } from './viewer/navigation.js'; import { UpVector, ShadingModel, Viewer, GetDefaultCamera, TraverseThreeObject, GetShadingTypeOfObject } from './viewer/viewer.js'; import { ViewerGeometry, ViewerExtraGeometry, SetThreeMeshPolygonOffset } from './viewer/viewergeometry.js'; @@ -301,7 +300,6 @@ export { EmbeddedViewer, Init3DViewerElement, Init3DViewerElements, - MeasureTool, MouseInteraction, TouchInteraction, ClickDetector, diff --git a/source/engine/viewer/measuretool.js b/source/website/measuretool.js similarity index 67% rename from source/engine/viewer/measuretool.js rename to source/website/measuretool.js index a9ea40d..89d0660 100644 --- a/source/engine/viewer/measuretool.js +++ b/source/website/measuretool.js @@ -1,20 +1,21 @@ -import { BigEps, IsEqualEps } from '../geometry/geometry.js'; +import { BigEps, IsEqualEps } from '../engine/geometry/geometry.js'; +import { AddDiv } from '../engine/viewer/domutils.js'; export class MeasureTool { - constructor () - { - this.viewer = null; - this.highlightColor = null; - - this.isActive = false; - this.markers = []; - } - - Init (viewer, highlightColor) + constructor (viewer) { this.viewer = viewer; - this.highlightColor = highlightColor; + this.isActive = false; + this.markers = []; + + this.panel = null; + this.button = null; + } + + SetButton (button) + { + this.button = button; } IsActive () @@ -24,9 +25,18 @@ export class MeasureTool SetActive (isActive) { + if (this.isActive === isActive) { + return; + } this.isActive = isActive; - if (!this.isActive) { - this.Clear (); + this.button.SetSelected (isActive); + if (this.isActive) { + this.panel = AddDiv (document.body, 'ov_measure_panel', 'hejj'); + this.UpdatePanel (); + this.Resize (); + } else { + this.ClearMarkers (); + this.panel.remove (); } } @@ -34,20 +44,17 @@ export class MeasureTool { let intersection = this.viewer.GetMeshIntersectionUnderMouse (mouseCoordinates); if (intersection === null) { - this.Clear (); + this.ClearMarkers (); + this.UpdatePanel (); return; } if (this.markers.length === 2) { - this.Clear (); + this.ClearMarkers (); } this.AddMarker (intersection); - } - - GetMarkerCount () - { - return this.markers.length; + this.UpdatePanel (); } AddMarker (intersection) @@ -56,6 +63,53 @@ export class MeasureTool this.GenerateMarker (intersection); } + GenerateMarker (intersection) + { + let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { + return true; + }); + + let coneHeight = boundingSphere.radius / 5.0; + let coneRadius = coneHeight / 2.0; + + let coneGeometry = new THREE.ConeGeometry (coneRadius, coneHeight, 32); + coneGeometry.translate (0.0, -coneHeight / 2.0, 0.0); + coneGeometry.rotateX (-Math.PI / 2); + + let coneMaterial = this.viewer.CreateHighlightMaterial (0xcc0000); + coneMaterial.opacity = 0.6; + coneMaterial.transparent = true; + let cone = new THREE.Mesh (coneGeometry, coneMaterial); + + let faceNormal = this.GetFaceWorldNormal (intersection); + cone.lookAt (faceNormal); + cone.position.set (intersection.point.x, intersection.point.y, intersection.point.z); + + this.viewer.AddExtraObject (cone); + } + + GetFaceWorldNormal (intersection) + { + let normalMatrix = new THREE.Matrix4 (); + intersection.object.updateWorldMatrix (true, false); + normalMatrix.extractRotation (intersection.object.matrixWorld); + let faceNormal = intersection.face.normal.clone (); + faceNormal.applyMatrix4 (normalMatrix); + return faceNormal; + } + + UpdatePanel () + { + if (this.markers.length === 0) { + this.panel.innerHTML = 'Select a point to start measuring.'; + } else if (this.markers.length === 1) { + this.panel.innerHTML = 'Select another point to start measuring.'; + } else { + let calcResult = this.Calculate (); + this.panel.innerHTML = JSON.stringify (calcResult); + } + } + Calculate () { if (this.markers.length !== 2) { @@ -81,44 +135,20 @@ export class MeasureTool return result; } - Clear () + Resize () + { + if (!this.isActive) { + return; + } + let canvas = this.viewer.GetCanvas (); + let rect = canvas.getBoundingClientRect (); + this.panel.style.left = rect.left + 'px'; + this.panel.style.top = rect.top + 'px'; + } + + ClearMarkers () { this.viewer.ClearExtra (); this.markers = []; } - - GenerateMarker (intersection) - { - let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { - return true; - }); - - let coneHeight = boundingSphere.radius / 5.0; - let coneRadius = coneHeight / 2.0; - - let coneGeometry = new THREE.ConeGeometry (coneRadius, coneHeight, 32); - coneGeometry.translate (0.0, -coneHeight / 2.0, 0.0); - coneGeometry.rotateX (-Math.PI / 2); - - let coneMaterial = this.viewer.CreateHighlightMaterial (this.highlightColor); - coneMaterial.opacity = 0.6; - coneMaterial.transparent = true; - let cone = new THREE.Mesh (coneGeometry, coneMaterial); - - let faceNormal = this.GetFaceWorldNormal (intersection); - cone.lookAt (faceNormal); - cone.position.set (intersection.point.x, intersection.point.y, intersection.point.z); - - this.viewer.AddExtraObject (cone); - } - - GetFaceWorldNormal (intersection) - { - let normalMatrix = new THREE.Matrix4 (); - intersection.object.updateWorldMatrix (true, false); - normalMatrix.extractRotation (intersection.object.matrixWorld); - let faceNormal = intersection.face.normal.clone (); - faceNormal.applyMatrix4 (normalMatrix); - return faceNormal; - } } diff --git a/source/website/sidebar.js b/source/website/sidebar.js index 8f3b973..ec6b469 100644 --- a/source/website/sidebar.js +++ b/source/website/sidebar.js @@ -1,14 +1,12 @@ import { ShowDomElement, SetDomElementWidth, SetDomElementHeight, GetDomElementOuterWidth, SetDomElementOuterHeight } from '../engine/viewer/domutils.js'; -import { FeatureSet } from './featureset.js'; import { PanelSet } from './panelset.js'; import { SidebarDetailsPanel } from './sidebardetailspanel.js'; -import { SidebarMeasurePanel } from './sidebarmeasurepanel.js'; import { SidebarSettingsPanel } from './sidebarsettingspanel.js'; import { InstallVerticalSplitter } from './utils.js'; export class Sidebar { - constructor (mainDiv, splitterDiv, settings, measureTool) + constructor (mainDiv, splitterDiv, settings) { this.mainDiv = mainDiv; this.splitterDiv = splitterDiv; @@ -16,13 +14,9 @@ export class Sidebar this.detailsPanel = new SidebarDetailsPanel (this.panelSet.GetContentDiv ()); this.settingsPanel = new SidebarSettingsPanel (this.panelSet.GetContentDiv (), settings); - this.measurePanel = new SidebarMeasurePanel (this.panelSet.GetContentDiv (), measureTool); this.panelSet.AddPanel (this.detailsPanel); this.panelSet.AddPanel (this.settingsPanel); - if (FeatureSet.MeasureTool) { - this.panelSet.AddPanel (this.measurePanel); - } this.panelSet.ShowPanel (this.detailsPanel); } @@ -71,12 +65,6 @@ export class Sidebar } }); - this.measurePanel.Init ({ - onActivatedChange : (isActivated) => { - this.callbacks.onMeasureToolActivedChange (isActivated); - } - }); - InstallVerticalSplitter (this.splitterDiv, this.mainDiv, true, () => { this.callbacks.onResize (); }); @@ -87,11 +75,6 @@ export class Sidebar this.settingsPanel.UpdateSettings (isPhysicallyBased, hasDefaultMaterial); } - UpdateMeasureTool () - { - this.measurePanel.UpdateMeasureTool (); - } - Resize (height) { SetDomElementOuterHeight (this.mainDiv, height); diff --git a/source/website/sidebarmeasurepanel.js b/source/website/sidebarmeasurepanel.js deleted file mode 100644 index 3d64323..0000000 --- a/source/website/sidebarmeasurepanel.js +++ /dev/null @@ -1,114 +0,0 @@ -import { RadDeg } from '../engine/geometry/geometry.js'; -import { AddDiv, ShowDomElement, ClearDomElement } from '../engine/viewer/domutils.js'; -import { SidebarPanel } from './sidebarpanel.js'; - -export class SidebarMeasurePanel extends SidebarPanel -{ - constructor (parentDiv, measureTool) - { - super (parentDiv); - - this.measureTool = measureTool; - this.helpSection = null; - this.resultSection = null; - } - - GetName () - { - return 'Measure'; - } - - GetIcon () - { - return 'measure'; - } - - Init (callbacks) - { - super.Init (callbacks); - - let isActive = false; - let activateButton = AddDiv (this.contentDiv, 'ov_button ov_panel_button', 'Activate'); - activateButton.addEventListener ('click', () => { - isActive = !isActive; - if (isActive) { - activateButton.classList.add ('outline'); - activateButton.innerHTML = 'Deactivate'; - } else { - activateButton.classList.remove ('outline'); - activateButton.innerHTML = 'Activate'; - } - this.callbacks.onActivatedChange (isActive); - }); - - this.helpSection = AddDiv (this.contentDiv, 'ov_sidebar_section'); - this.resultSection = AddDiv (this.contentDiv, 'ov_sidebar_section'); - - this.helpSection.innerHTML = this.GetDefaultHelpText (); - } - - UpdateMeasureTool () - { - ClearDomElement (this.helpSection); - ClearDomElement (this.resultSection); - - ShowDomElement (this.helpSection, true); - ShowDomElement (this.resultSection, false); - - if (this.measureTool.IsActive ()) { - let markerCount = this.measureTool.GetMarkerCount (); - if (markerCount === 0) { - this.helpSection.innerHTML = 'Click on a model point to start measure.'; - } else if (markerCount === 1) { - this.helpSection.innerHTML = 'Click on another model point to see the results.'; - } else if (markerCount === 2) { - ShowDomElement (this.helpSection, false); - ShowDomElement (this.resultSection, true); - - let calculatedValues = this.measureTool.Calculate (); - - AddDiv (this.resultSection, 'ov_sidebar_measure_name', 'Distance of points'); - let pointsDistanceStr = calculatedValues.pointsDistance.toLocaleString (undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 4 - }); - AddDiv (this.resultSection, 'ov_sidebar_measure_value', pointsDistanceStr); - - AddDiv (this.resultSection, 'ov_sidebar_measure_name', 'Distance of parallel faces'); - if (calculatedValues.parallelFacesDistance !== null) { - let facesDistanceStr = calculatedValues.parallelFacesDistance.toLocaleString (undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 4 - }); - AddDiv (this.resultSection, 'ov_sidebar_measure_value', facesDistanceStr); - } else { - AddDiv (this.resultSection, 'ov_sidebar_measure_value', 'Faces are not parallel'); - } - - AddDiv (this.resultSection, 'ov_sidebar_measure_name', 'Angle of faces'); - let facesAngleDegree = calculatedValues.facesAngle * RadDeg; - let facesAngleStr = facesAngleDegree.toLocaleString (undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 4 - }); - AddDiv (this.resultSection, 'ov_sidebar_measure_value', facesAngleStr + '°'); - - } - } else { - this.helpSection.innerHTML = this.GetDefaultHelpText (); - } - } - - GetDefaultHelpText () - { - return `
    -
  1. Activate measure mode with the button above.
  2. -
  3. Click two points in the model to see the results.
  4. -
`; - } - - Clear () - { - - } -} diff --git a/source/website/toolbar.js b/source/website/toolbar.js index c622821..f5d3271 100644 --- a/source/website/toolbar.js +++ b/source/website/toolbar.js @@ -78,7 +78,7 @@ export class Toolbar { let button = new ToolbarButton (image, imageTitle, () => { button.SetSelected (!button.IsSelected ()); - onClick (); + onClick (button.IsSelected ()); }); button.AddDomElements (this.mainDiv); button.SetSelected (isSelected); diff --git a/source/website/website.js b/source/website/website.js index 36acc61..ba9f55d 100644 --- a/source/website/website.js +++ b/source/website/website.js @@ -1,7 +1,6 @@ import { FileSource, GetFileExtension, TransformFileHostUrls } from '../engine/io/fileutils.js'; import { ImportErrorCode, ImportSettings } from '../engine/import/importer.js'; import { Viewer } from '../engine/viewer/viewer.js'; -import { MeasureTool } from '../engine/viewer/measuretool.js'; import { AddDiv, AddDomElement, ShowDomElement, SetDomElementOuterHeight } from '../engine/viewer/domutils.js'; import { CalculatePopupPositionToScreen, ShowListPopup } from './dialogs.js'; import { HandleEvent } from './eventhandler.js'; @@ -20,6 +19,8 @@ import { HasDefaultMaterial, ReplaceDefaultMaterialColor } from '../engine/model import { Direction } from '../engine/geometry/geometry.js'; import { CookieGetBoolVal, CookieSetBoolVal } from './cookiehandler.js'; import { ShadingType } from '../engine/threejs/threeutils.js'; +import { FeatureSet } from './featureset.js'; +import { MeasureTool } from './measuretool.js'; export const WebsiteUIState = { @@ -36,11 +37,11 @@ export class Website this.parameters = parameters; this.settings = new Settings (); this.viewer = new Viewer (); - this.measureTool = new MeasureTool (); + this.measureTool = new MeasureTool (this.viewer); this.hashHandler = new HashHandler (); this.toolbar = new Toolbar (this.parameters.toolbarDiv); this.navigator = new Navigator (this.parameters.navigatorDiv, this.parameters.navigatorSplitterDiv); - this.sidebar = new Sidebar (this.parameters.sidebarDiv, this.parameters.sidebarSplitterDiv, this.settings, this.measureTool); + this.sidebar = new Sidebar (this.parameters.sidebarDiv, this.parameters.sidebarSplitterDiv, this.settings); this.modelLoaderUI = new ThreeModelLoaderUI (); this.themeHandler = new ThemeHandler (); this.highlightColor = new THREE.Color (0x8ec9f0); @@ -56,7 +57,6 @@ export class Website HandleEvent ('theme_on_load', this.settings.themeId === Theme.Light ? 'light' : 'dark'); this.InitViewer (); - this.InitMeasureTool (); this.InitToolbar (); this.InitDragAndDrop (); this.InitSidebar (); @@ -109,6 +109,7 @@ export class Website this.navigator.Resize (contentHeight); this.sidebar.Resize (contentHeight); this.viewer.Resize (contentWidth - safetyMargin, contentHeight); + this.measureTool.Resize (); } OnSmallWidthChanged () @@ -166,8 +167,7 @@ export class Website this.navigator.Clear (); this.sidebar.Clear (); - this.measureTool.Clear (); - this.sidebar.UpdateMeasureTool (); + this.measureTool.SetActive (false); } OnModelLoaded (importResult, threeObject) @@ -189,7 +189,6 @@ export class Website if (this.measureTool.IsActive ()) { this.measureTool.Click (mouseCoordinates); - this.sidebar.UpdateMeasureTool (); return; } @@ -484,11 +483,6 @@ export class Website this.UpdateEnvironmentMap (); } - InitMeasureTool () - { - this.measureTool.Init (this.viewer, this.highlightColor); - } - InitToolbar () { function AddButton (toolbar, imageName, imageTitle, classNames, onClick) @@ -502,6 +496,17 @@ export class Website return button; } + function AddPushButton (toolbar, imageName, imageTitle, classNames, onClick) + { + let button = toolbar.AddImagePushButton (imageName, imageTitle, false, (isSelected) => { + onClick (isSelected); + }); + for (let className of classNames) { + button.AddClass (className); + } + return button; + } + function AddRadioButton (toolbar, imageNames, imageTitles, selectedIndex, classNames, onClick) { let imageData = []; @@ -566,6 +571,15 @@ export class Website this.viewer.SetFixUpVector (false); } }); + if (FeatureSet.MeasureTool) { + AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); + let measureToolButton = AddPushButton (this.toolbar, 'measure', 'Measure', ['only_full_width', 'only_on_model'], (isSelected) => { + HandleEvent ('measure_tool_activated', isSelected ? 'on' : 'off'); + this.navigator.SetSelection (null); + this.measureTool.SetActive (isSelected); + }); + this.measureTool.SetButton (measureToolButton); + } AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); AddButton (this.toolbar, 'export', 'Export model', ['only_full_width', 'only_on_model'], () => { let exportDialog = new ExportDialog ({ @@ -645,7 +659,7 @@ export class Website HandleEvent ('theme_changed', this.settings.themeId === Theme.Light ? 'light' : 'dark'); this.SwitchTheme (this.settings.themeId, true); }, - onMeasureToolActivedChange : (isActivated) => { + /* onMeasureToolActivedChange : (isActivated) => { if (isActivated) { this.navigator.SetSelection (null); this.measureTool.SetActive (true); @@ -653,7 +667,7 @@ export class Website this.measureTool.SetActive (false); } this.sidebar.UpdateMeasureTool (); - }, + },*/ onResize : () => { this.Resize (); }, diff --git a/website/css/sidebar.css b/website/css/sidebar.css index 27aebf5..e4f8768 100644 --- a/website/css/sidebar.css +++ b/website/css/sidebar.css @@ -83,17 +83,6 @@ div.ov_sidebar_content div.ov_sidebar_settings_padded overflow: hidden; } -div.ov_sidebar_content div.ov_sidebar_measure_name -{ - font-weight: bold; - margin-bottom: 5px; -} - -div.ov_sidebar_content div.ov_sidebar_measure_value -{ - margin-bottom: 10px; -} - div.ov_sidebar_content button.pcr-button { width: 30px; diff --git a/website/css/website.css b/website/css/website.css index 9733a36..b408e23 100644 --- a/website/css/website.css +++ b/website/css/website.css @@ -271,6 +271,14 @@ div.ov_bottom_floating_panel div.ov_floating_panel_button float: right; } +div.ov_measure_panel +{ + background: red; + position: absolute; + left : 0px; + top : 0px; +} + @media (hover) {