How-to: Draw a Bounding Box in the Web Viewer

Language: JavaScript

The following JavaScript is a modified version of the Bounding Box example provided in the Developer Examples section of the Quickstart menu. This modified example contains three components:

  1. BoundingBoxMarkup class extended from MarkupItem
  2. Selection event handler
  3. Registering event handler as a Callback

Complete the following steps to add this BoundingBoxMarkup class to any Sample Model in the Quickstart application:

  1. Launch the Quickstart server and choose any of the sample models
  2. Open the web browser console
  3. Add the following BoundingBoxMarkup class by copying it into the console:
//Create bounding box markup item
var BoundingBoxMarkup = /** @class */ (function (_super) {
	__extends(BoundingBoxMarkup, _super);
	function BoundingBoxMarkup(viewer) {
		var _this = _super.call(this) || this;
		_this._box = null;
		_this._boxVertices = new Communicator.Markup.Shape.CircleCollection();
		_this._boxVertexSize = 1.0;
		_this._boxWires = new Communicator.Markup.Shape.LineCollection();
		_this._viewer = viewer;
		return _this;
	}
	BoundingBoxMarkup.prototype.setVertexSize = function (vertexSize) {
		this._boxVertexSize = vertexSize;
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype.setWireWidth = function (wireWidth) {
		this._boxWires.setStrokeWidth(wireWidth);
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype.draw = function () {
		if (!this._box) {
			return;
		}
		this._update();
		var renderer = this._viewer.markupManager.getRenderer();
		renderer.drawCircles(this._boxVertices);
		renderer.drawLines(this._boxWires);
	};
	BoundingBoxMarkup.prototype.setBox = function (box) {
		this._box = box.copy();
	};
	BoundingBoxMarkup.prototype.clearBox = function () {
		this._box = null;
	};
	BoundingBoxMarkup.prototype.setBoxColor = function (color) {
		this._boxVertices.setFillColor(color);
		this._boxVertices.setStrokeColor(color);
		this._boxWires.setStrokeColor(color);
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype._update = function () {
		this._boxVertices.clear();
		this._boxWires.clear();
		
		if (this._box === null) {
			return;
		}
		
		var view = this._viewer.view;
		
		// create points around the selection
		var points = [];
		points.push(new Communicator.Point3(this._box.min.x, this._box.max.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.max.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.min.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.min.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.max.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.max.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.min.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.min.y, this._box.min.z));
		
		points = points.map((point) => view.projectPoint(point));
		
		// generate vertices from points
		var vertices = [];
		vertices = points.map((point) => Communicator.Point2.fromPoint3(point));
				
		// add points		
		vertices.forEach((vertex) => this._boxVertices.addCircle(vertex, this._boxVertexSize));
		
		// create box wireframe
		this._boxWires.addLine(vertices[0], vertices[1]);
		this._boxWires.addLine(vertices[1], vertices[2]);
		this._boxWires.addLine(vertices[2], vertices[3]);
		this._boxWires.addLine(vertices[3], vertices[0]);
		this._boxWires.addLine(vertices[4], vertices[5]);
		this._boxWires.addLine(vertices[5], vertices[6]);
		this._boxWires.addLine(vertices[6], vertices[7]);
		this._boxWires.addLine(vertices[7], vertices[4]);
		this._boxWires.addLine(vertices[0], vertices[4]);
		this._boxWires.addLine(vertices[1], vertices[5]);
		this._boxWires.addLine(vertices[2], vertices[6]);
		this._boxWires.addLine(vertices[3], vertices[7]);
	};
	return BoundingBoxMarkup;
}(Communicator.Markup.MarkupItem));
  1. Next, add an event handler that will trigger when a selection is made:
//Selection function that adds bounding box to selected node
function mySelectionFunc(event) {
	var markupManager = hwv.getMarkupManager();
	var model = hwv.getModel();
	var selection = event.getSelection();
	if (selection.isNodeSelection()) {
		var partId = selection.getNodeId();
		if (model.isNodeLoaded(partId)) {
			model.getNodesBounding([partId]).then(function (boundingBox) {
				_boundingBoxMarkup.setBox(boundingBox);
				_boundingBoxMarkupHandle = hwv.getMarkupManager().registerMarkup(_boundingBoxMarkup);
			});
		}
	}
	else {
		_boundingBoxMarkup.clearBox();
		if (_boundingBoxMarkupHandle !== null) {
			hwv.getMarkupManager().unregisterMarkup(_boundingBoxMarkupHandle);
		}
	}
};
  1. Now register the event handler:
//Set callback for selection function
hwv.setCallbacks({
	selectionArray: function (events) {
		events.forEach((event) => mySelectionFunc(event));
	}
});
  1. Last, initialize a new BoundingBoxMarkup :
//Create variables
var _boundingBoxMarkup = new BoundingBoxMarkup(hwv);

Make a selection on the model. A bounding box should be drawn around the item.

For your convenience in doing a single copy-and-paste operation in the Web Viewer, the code above has been consolidated:

//Create bounding box markup item
var BoundingBoxMarkup = /** @class */ (function (_super) {
	__extends(BoundingBoxMarkup, _super);
	function BoundingBoxMarkup(viewer) {
		var _this = _super.call(this) || this;
		_this._box = null;
		_this._boxVertices = new Communicator.Markup.Shape.CircleCollection();
		_this._boxVertexSize = 1.0;
		_this._boxWires = new Communicator.Markup.Shape.LineCollection();
		_this._viewer = viewer;
		return _this;
	}
	BoundingBoxMarkup.prototype.setVertexSize = function (vertexSize) {
		this._boxVertexSize = vertexSize;
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype.setWireWidth = function (wireWidth) {
		this._boxWires.setStrokeWidth(wireWidth);
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype.draw = function () {
		if (!this._box) {
			return;
		}
		this._update();
		var renderer = this._viewer.markupManager.getRenderer();
		renderer.drawCircles(this._boxVertices);
		renderer.drawLines(this._boxWires);
	};
	BoundingBoxMarkup.prototype.setBox = function (box) {
		this._box = box.copy();
	};
	BoundingBoxMarkup.prototype.clearBox = function () {
		this._box = null;
	};
	BoundingBoxMarkup.prototype.setBoxColor = function (color) {
		this._boxVertices.setFillColor(color);
		this._boxVertices.setStrokeColor(color);
		this._boxWires.setStrokeColor(color);
		this._viewer.markupManager.refreshMarkup();
	};
	BoundingBoxMarkup.prototype._update = function () {
		this._boxVertices.clear();
		this._boxWires.clear();
		
		if (this._box === null) {
			return;
		}
		
		var view = this._viewer.view;
		
		// create points around the selection
		var points = [];
		points.push(new Communicator.Point3(this._box.min.x, this._box.max.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.max.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.min.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.min.y, this._box.max.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.max.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.max.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.max.x, this._box.min.y, this._box.min.z));
		points.push(new Communicator.Point3(this._box.min.x, this._box.min.y, this._box.min.z));
		
		points = points.map((point) => view.projectPoint(point));
		
		// generate vertices from points
		var vertices = [];
		vertices = points.map((point) => Communicator.Point2.fromPoint3(point));
				
		// add points		
		vertices.forEach((vertex) => this._boxVertices.addCircle(vertex, this._boxVertexSize));
		
		// create box wireframe
		this._boxWires.addLine(vertices[0], vertices[1]);
		this._boxWires.addLine(vertices[1], vertices[2]);
		this._boxWires.addLine(vertices[2], vertices[3]);
		this._boxWires.addLine(vertices[3], vertices[0]);
		this._boxWires.addLine(vertices[4], vertices[5]);
		this._boxWires.addLine(vertices[5], vertices[6]);
		this._boxWires.addLine(vertices[6], vertices[7]);
		this._boxWires.addLine(vertices[7], vertices[4]);
		this._boxWires.addLine(vertices[0], vertices[4]);
		this._boxWires.addLine(vertices[1], vertices[5]);
		this._boxWires.addLine(vertices[2], vertices[6]);
		this._boxWires.addLine(vertices[3], vertices[7]);
	};
	return BoundingBoxMarkup;
}(Communicator.Markup.MarkupItem));

//Selection function that adds bounding box to selected node
function mySelectionFunc(event) {
	var markupManager = hwv.getMarkupManager();
	var model = hwv.getModel();
	var selection = event.getSelection();
	if (selection.isNodeSelection()) {
		var partId = selection.getNodeId();
		if (model.isNodeLoaded(partId)) {
			model.getNodesBounding([partId]).then(function (boundingBox) {
				_boundingBoxMarkup.setBox(boundingBox);
				_boundingBoxMarkupHandle = hwv.getMarkupManager().registerMarkup(_boundingBoxMarkup);
			});
		}
	}
	else {
		_boundingBoxMarkup.clearBox();
		if (_boundingBoxMarkupHandle !== null) {
			hwv.getMarkupManager().unregisterMarkup(_boundingBoxMarkupHandle);
		}
	}
};

//Set callback for selection function
hwv.setCallbacks({
	selectionArray: function (events) {
		events.forEach((event) => mySelectionFunc(event));
	}
});

//Create variables
var _boundingBoxMarkup = new BoundingBoxMarkup(hwv);
1 Like

Hi, Gabriel. Thank you for the code demo for drawing bounding box. Could we ask for further code support for adding text with box like the sample of ‘3D Text Insertion’?

Hello @jiangguannan,

Is there a specific part of the 3D Text Insertion example that you have a question on?

The 3D Text Insertion is a bit more complex than just drawing a basic bounding box overlay. As such, it is not a good candidate for a simple copy-and-paste example.

Thanks,
Tino

Hi @tino,
we want to add the markup shown in the below picture, mutiple text with border.

Could you or @gabriel.peragine gabriel help offer a simple copy-and-paste example?

The depth and breadth of the complexity of adding markups (as shown in your screenshot) is significantly beyond a simple cut-and-paste example.

It is a lot more common to convert a CAD file with the existing PMI data already available. As such, once the CAD file is converted to Stream Cache via Converter.exe, the file is simply loaded in the Web Viewer.

I may use the wrong example, we want to add markup in the Web Viewer. We found the example, but we are facing the code problems, which want to get some instruction, how to add the markup in the Web Viewer.

The JavaScript and the HTML for the 3D Text Insertion are available as part of the HC package. You can find the related files in the following directories:

  • HOOPS_Communicator_2024.X.x\web_viewer\examples\scripts\examples\text_insertion.js
  • HOOPS_Communicator_2024.X.x\web_viewer\examples\text_insertion.html

For something more interactive in getting a better understanding of the how the code works, you can just press F12 in Chrome and step through the code:

Thank you very much. It would be very useful.