# Measurement

## How to measure a model

There are two ways to measure a model. We can measure with the triangulated data, or for models that contain BREP data, we can measure using the data that is embedded when the model is converted. Using the BREP data will give us a more accurate measurement. This will work for faces and edges, however for point to point measurement, we will have to use the triangulated data.

The subentity properties for a face or edge can be retrieved using the getFaceProperty and getEdgeProperty functions.

The subentity properties contains measurement data for the corresponding face or line. Each property object corresponds to the type of edge or line, and the measurement properties that can be accessed will differ accordingly.

For example, a `LineElement`, which corresponds to a Line edge type, will have a length property. Some face or edge types may have properties that are the same. For example, a `CircleElement`, `CylinderElement`, and `ConeElement`, will all have radius, origin, and normal properties.

To see all the subentity property types, please refer to the docs here

When measuring, it is important to take into account the unit scale. The unit scale is a number that represents the size of the unit. A model measured in inches would have a unit scale of 25.4.

All measurements values are in millimeters. To get inches, divide by the unit scale of 25.4. For example, when measuring the length of an edge, a value of 50.8 represents 2 inches.

For more information on unit scaling, see the Model Units Programming Guide.

## Edge / face / point measure

HOOPS Visualize Web has native four measurement operators:

MeasureEdgeLengthOperator - Measures the length of edges. For circles, measures the radius. MeasureFaceFaceAngleOperator - Measures the angles between two faces. MeasureFaceFaceDistanceOperator - Measures the distance between two faces. MeasurePointPointDistanceOperator - Measures the distance between two points.

## How to insert 2D and 3D text

3D text can be inserted by creating a texture. [See the text_insertion example] 2D text can be inserted by creating a markup item with a text shape.

        export class CustomMarkupItem extends Communicator.Markup.MarkupItem {
            private _viewer: Communicator.WebViewer;
            private _position: Communicator.Point3;
            private _textShape: Communicator.Markup.Shape.TextBox = new Communicator.Markup.Shape.TextBox();

            constructor(viewer: Communicator.WebViewer, position: Communicator.Point3, text: string) {
                super();

                this._viewer = viewer;
                this._position = position;
                this._textShape.setTextString(text);

                this._textShape.getBoxPortion().setFillOpacity(1);
                this._textShape.getBoxPortion().setFillColor(new Communicator.Color(255, 255, 255));
            }

            public draw(): void {
                const updatedPosition = Communicator.Point2.fromPoint3(
                    this._viewer.view.projectPoint(this._position),
                );
                this._textShape.setPosition(updatedPosition);

                const renderer = this._viewer.markupManager.getRenderer();
                renderer.drawTextBox(this._textShape);
            }
        }

                const item = new CustomMarkupItem(this._viewer, position, text);
                this._viewer.markupManager.registerMarkup(item);

## Measure operator

In the following sample operator, we select a node, and fetch its subentity properties. In this case, we use the markup item above to add a text box with the length of an edge, or radius of a cylinder.

        export class CustomMeasureOperator {
            private _viewer: Communicator.WebViewer;

            public constructor(viewer: Communicator.WebViewer) {
                this._viewer = viewer;
            }

            public onMouseDown(event: Communicator.Event.MouseInputEvent): void {
                event.setHandled(true);
                const position = event.getPosition();
                this._viewer.view
                    .pickFromPoint(
                        position,
                        new Communicator.PickConfig(Communicator.SelectionMask.Line),
                    )
                    .then((selection) => {
                        this._addMeasurement(selection);
                    });
            }

            private async _addMeasurement(selection: Communicator.Selection.SelectionItem) {
                const faceEntity = selection.getFaceEntity();
                const lineEntity = selection.getLineEntity();

                const nodeId = selection.getNodeId();
                const position = selection.getPosition();
                if (nodeId === null || position === null) {
                    return;
                }

                const unitMultiplier = this._viewer.model.getNodeUnitMultiplier(nodeId);

                if (faceEntity !== null) {
                    const property = await this._viewer.model.getFaceProperty(
                        nodeId,
                        faceEntity.getCadFaceIndex(),
                    );
                    this._addFaceMeasurement(property, position, unitMultiplier);
                } else if (lineEntity !== null) {
                    const property = await this._viewer.model.getEdgeProperty(
                        nodeId,
                        lineEntity.getLineId(),
                    );
                    this._addLineMeasurement(property, position, unitMultiplier);
                }
            }

            private _addFaceMeasurement(
                property: Communicator.SubentityProperties.Face | null,
                position: Communicator.Point3,
                unitMultiplier: number,
            ): void {
                if (property instanceof Communicator.SubentityProperties.CylinderElement) {
                    const text = `Radius: ${Communicator.Util.formatWithUnit(
                        property.radius / unitMultiplier,
                        unitMultiplier,
                    )}`;
                    this._createMarkup(position, text);
                }
            }
            private _addLineMeasurement(
                property: Communicator.SubentityProperties.Edge | null,
                position: Communicator.Point3,
                unitMultiplier: number,
            ): void {
                if (property instanceof Communicator.SubentityProperties.LineElement) {
                    const text = `Length: ${Communicator.Util.formatWithUnit(
                        property.length / unitMultiplier,
                        unitMultiplier,
                    )}`;
                    this._createMarkup(position, text);
                }
            }

            private _createMarkup(position: Communicator.Point3, text: string): void {
                //! [add_markup_item]
                const item = new CustomMarkupItem(this._viewer, position, text);
                this._viewer.markupManager.registerMarkup(item);
                //! [add_markup_item]
            }
        }

## Callbacks

The following callbacks are used to detect when measurement values are created and deleted, or modified.

- measurementBegin
- measurementCreated
- measurementDeleted
- measurementHidden
- measurementLoaded
- measurementShown
- measurementValueSet

Conversation saved
HOOPSY BETA

### Hello! I'm HOOPSY

Your Docs generative AI assistant. Ask me anything about HOOPS Visualize Web or choose a sample question below to start a conversation:

What is HOOPS Visualize Web?
What are the latest release notes for HOOPS Visualize Web?
How do I start evaluating HOOPS Visualize Web?
How do I write my first sample application?
How can I get assistance?

HOOPSY may store conversations according to OpenAI's policy. Responses may not always be accurate.

HOOPSY is thinking...