Skip to content

Commit

Permalink
don't compute models bounding box
Browse files Browse the repository at this point in the history
t
eliminate unnecessary reflows from Annotations rendering
  • Loading branch information
sdumetz committed Oct 24, 2024
1 parent dff5bcf commit c73fc9f
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 39 deletions.
7 changes: 3 additions & 4 deletions libs/ff-three/source/HTMLSprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default class HTMLSprite<EventMap extends Object3DEventMap = Object3DEven
* @param anchor The 3D object to which the HTML sprite element is attached.
* @param offset An offset to be added to the anchor 3D object.
*/
renderHTMLElement(element: SpriteElement, container: HTMLElement, camera: Camera, anchor?: Object3D, offset?: Vector3)
renderHTMLElement(element: SpriteElement, bounds: DOMRect, camera: Camera, anchor?: Object3D, offset?: Vector3)
{
anchor = anchor || this;

Expand All @@ -127,9 +127,8 @@ export default class HTMLSprite<EventMap extends Object3DEventMap = Object3DEven
_vec2b.set(_vec3b.x, _vec3b.y);
_vec2a.set(_vec3a.x, _vec3a.y);
_vec2b.sub(_vec2a);

const x = (_vec3b.x + 1) * 0.5 * container.clientWidth;
const y = (1 - _vec3b.y) * 0.5 * container.clientHeight;
const x = (_vec3b.x + 1) * 0.5 * bounds.width;
const y = (1 - _vec3b.y) * 0.5 * bounds.height;

element.setPosition(x, y);

Expand Down
5 changes: 3 additions & 2 deletions libs/ff-three/source/HTMLSpriteGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ export default class HTMLSpriteGroup extends Object3D
if (!this.visible) {
return;
}

//Only get bounds once to prevent forced reflows while looping
const bounds = container.getBoundingClientRect();
const children = this.children as HTMLSprite[];
for (let i = 0, n = children.length; i < n; ++i) {
const child = children[i];
const element = child.getHTMLElement(container);
if (element) {
child.renderHTMLElement(element, container, camera);
child.renderHTMLElement(element, bounds, camera);
}
}
}
Expand Down
34 changes: 17 additions & 17 deletions source/client/annotations/CircleSprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ export default class CircleSprite extends AnnotationSprite
super.update();
}

renderHTMLElement(element: AnnotationElement, container: HTMLElement, camera: UniversalCamera)
renderHTMLElement(element: AnnotationElement, bounds: DOMRect, camera: UniversalCamera)
{
super.renderHTMLElement(element, container, camera, this.anchorMesh, _offset);
super.renderHTMLElement(element, bounds, camera, this.anchorMesh, _offset);

// Override viewAngle calculation using temporary offset
const anchor = this.anchorMesh;
Expand Down Expand Up @@ -140,7 +140,7 @@ export default class CircleSprite extends AnnotationSprite
element.requestUpdate().then(() => {
this.originalHeight = element.offsetHeight;
this.originalWidth = element.offsetWidth;
this.checkTruncate(element, container);
this.checkTruncate(element, bounds);
});
}
else {
Expand All @@ -149,7 +149,7 @@ export default class CircleSprite extends AnnotationSprite
}
}

this.checkTruncate(element, container);
this.checkTruncate(element, bounds);
}
}

Expand All @@ -159,40 +159,40 @@ export default class CircleSprite extends AnnotationSprite
}

// Helper function to check if annotation should truncate
protected checkTruncate(element: AnnotationElement, container: HTMLElement) {
const x = element.getBoundingClientRect().left - container.getBoundingClientRect().left;
const y = element.getBoundingClientRect().top - container.getBoundingClientRect().top;
protected checkTruncate(element: AnnotationElement, bounds: DOMRect) {
const x = element.getBoundingClientRect().left - bounds.left;
const y = element.getBoundingClientRect().top - bounds.top;

const shouldTruncate = y + this.originalHeight >= container.offsetHeight;
const shouldTruncate = y + this.originalHeight >= bounds.height;
if(shouldTruncate !== element.truncated) {
element.truncated = shouldTruncate;
element.requestUpdate().then(() => {
this.checkBounds(element, container);
this.checkBounds(element, bounds);
});
}
else {
this.checkBounds(element, container);
this.checkBounds(element, bounds);
}
}

// Helper function to check and handle annotation overlap with bounds of container
protected checkBounds(element: AnnotationElement, container: HTMLElement) {
const x = element.getBoundingClientRect().left - container.getBoundingClientRect().left;
const y = element.getBoundingClientRect().top - container.getBoundingClientRect().top;
protected checkBounds(element: AnnotationElement, bounds: DOMRect) {
const x = element.getBoundingClientRect().left - bounds.left;
const y = element.getBoundingClientRect().top - bounds.top;

if (x + element.offsetWidth >= container.offsetWidth && !element.classList.contains("sv-align-right")) {
if (x + element.offsetWidth >= bounds.width && !element.classList.contains("sv-align-right")) {
element.classList.add("sv-align-right");
element.requestUpdate();
}
else if (x + element.offsetWidth < container.offsetWidth && element.classList.contains("sv-align-right")){
else if (x + element.offsetWidth < bounds.width && element.classList.contains("sv-align-right")){
element.classList.remove("sv-align-right");
element.requestUpdate();
}
if (y + element.offsetHeight >= container.offsetHeight && !element.classList.contains("sv-align-bottom")) {
if (y + element.offsetHeight >= bounds.height && !element.classList.contains("sv-align-bottom")) {
element.classList.add("sv-align-bottom");
element.requestUpdate();
}
else if (y + element.offsetHeight < container.offsetHeight && element.classList.contains("sv-align-bottom")) {
else if (y + element.offsetHeight < bounds.height && element.classList.contains("sv-align-bottom")) {
element.classList.remove("sv-align-bottom");
element.requestUpdate();
}
Expand Down
23 changes: 11 additions & 12 deletions source/client/annotations/ExtendedSprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export default class ExtendedSprite extends AnnotationSprite
super.update();
}

renderHTMLElement(element: ExtendedAnnotation, container: HTMLElement, camera: Camera)
renderHTMLElement(element: ExtendedAnnotation, bounds: DOMRect, camera: Camera)
{
super.renderHTMLElement(element, container, camera, this.stemLine, _offset);
super.renderHTMLElement(element, bounds, camera, this.stemLine, _offset);

const angleOpacity = math.scaleLimit(this.viewAngle * math.RAD2DEG, 90, 100, 1, 0);
const opacity = this.annotation.data.visible ? angleOpacity : 0;
Expand Down Expand Up @@ -121,7 +121,7 @@ export default class ExtendedSprite extends AnnotationSprite
element.requestUpdate().then(() => {
this.originalHeight = element.offsetHeight;
this.originalWidth = element.offsetWidth;
this.checkTruncate(element, container);
this.checkTruncate(element, bounds);
});
return;
}
Expand All @@ -131,7 +131,7 @@ export default class ExtendedSprite extends AnnotationSprite
}
}

this.checkTruncate(element, container);
this.checkTruncate(element, bounds);
}
}

Expand All @@ -141,16 +141,15 @@ export default class ExtendedSprite extends AnnotationSprite
}

// Helper function to check if annotation should truncate
protected checkTruncate(element: AnnotationElement, container: HTMLElement) {
protected checkTruncate(element: AnnotationElement, bounds: DOMRect) {
const top = this.quadrant == EQuadrant.TopLeft || this.quadrant == EQuadrant.TopRight;
const right = this.quadrant == EQuadrant.TopRight || this.quadrant == EQuadrant.BottomRight;
const x = right ? element.getBoundingClientRect().left - container.getBoundingClientRect().left
: element.getBoundingClientRect().right - container.getBoundingClientRect().left;
const y = top ? element.getBoundingClientRect().bottom - container.getBoundingClientRect().top
: element.getBoundingClientRect().top - container.getBoundingClientRect().top;

const shouldTruncateVert = !top ? y + this.originalHeight >= container.offsetHeight : y - this.originalHeight <= 0;
const shouldTruncateHoriz = right ? x + this.originalWidth >= container.offsetWidth : x - this.originalWidth <= 0;
const x = right ? element.getBoundingClientRect().left - bounds.left
: element.getBoundingClientRect().right - bounds.left;
const y = top ? element.getBoundingClientRect().bottom - bounds.top
: element.getBoundingClientRect().top - bounds.top;
const shouldTruncateVert = !top ? y + this.originalHeight >= bounds.height : y - this.originalHeight <= 0;
const shouldTruncateHoriz = right ? x + this.originalWidth >= bounds.width : x - this.originalWidth <= 0;
const shouldTruncate = shouldTruncateVert || shouldTruncateHoriz;
if(shouldTruncate !== element.truncated) {
element.truncated = shouldTruncate;
Expand Down
4 changes: 2 additions & 2 deletions source/client/annotations/StandardSprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ export default class StandardSprite extends AnnotationSprite
super.update();
}

renderHTMLElement(element: StandardAnnotation, container: HTMLElement, camera: Camera)
renderHTMLElement(element: StandardAnnotation, bounds: DOMRect, camera: Camera)
{
super.renderHTMLElement(element, container, camera, this.stemLine, _offset);
super.renderHTMLElement(element, bounds, camera, this.stemLine, _offset);

const angleOpacity = math.scaleLimit(this.viewAngle * math.RAD2DEG, 90, 100, 1, 0);
const opacity = this.annotation.data.visible ? angleOpacity : 0;
Expand Down
2 changes: 0 additions & 2 deletions source/client/io/ModelReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ export default class ModelReader
material.map.colorSpace = SRGBColorSpace;
}

mesh.geometry.computeBoundingBox();

const uberMat = material.type === "MeshPhysicalMaterial" ? new UberPBRAdvMaterial() : new UberPBRMaterial();

if (material.flatShading) {
Expand Down

0 comments on commit c73fc9f

Please sign in to comment.