-
Notifications
You must be signed in to change notification settings - Fork 25
Measures support (with toggle button) #819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
asmith26
wants to merge
24
commits into
jupytercad:main
Choose a base branch
from
asmith26:measures_support_ruler
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
e08bc30
Add measure support
asmith26 87aa35b
Measure -> Measurement
asmith26 1a5b1c6
Merge branch 'jupytercad:main' into measures_support
asmith26 f08452f
Add comment
asmith26 7c23ef9
Add toggle measurement button
asmith26 1570e31
Increase size of ruler image
asmith26 e14281f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 79cd9fc
Merge branch 'jupytercad:main' into measures_support_ruler
asmith26 bc5f431
Fix test
asmith26 dcac414
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 35fa2ee
Update failing snapshots
asmith26 e8fcd72
Update snapshots
asmith26 3194256
Update snapshots
asmith26 58269ed
Update snapshots
asmith26 73ed175
React -> Class
asmith26 cae7af3
Remove redundant code, add comment
asmith26 a4e7535
Fix measurements not refreshing on delete/move
asmith26 4abf570
Fix: align bounding box to the object only when measuring a single ob…
asmith26 5707339
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] d2cd181
Update comment
asmith26 56fdfca
Hide 0 axes
asmith26 9329bbe
Improved dashed line
asmith26 c4377ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 6e6f135
Update snapshot
asmith26 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| /** | ||
| * This file defines a class for rendering measurements of a 3D object. | ||
| * It uses `three.js` to create dimension lines and labels for a given bounding box. | ||
| */ | ||
| import * as THREE from 'three'; | ||
| import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'; | ||
|
|
||
| /** | ||
| * A class that displays the dimensions of a THREE.Box3. | ||
| * It creates visual annotations (lines and labels) for the X, Y, and Z dimensions. | ||
| * The measurement can be axis-aligned or oriented by providing a position and quaternion. | ||
| */ | ||
| export class Measurement { | ||
| private _group: THREE.Group; | ||
| private _box: THREE.Box3; | ||
| private _quaternion?: THREE.Quaternion; | ||
| private _position?: THREE.Vector3; | ||
|
|
||
| /** | ||
| * Constructor for the Measurement class. | ||
| * @param box The bounding box to measure. | ||
| * @param position Optional position to apply to the measurement group for oriented measurements. | ||
| * @param quaternion Optional quaternion to apply to the measurement group for oriented measurements. | ||
| */ | ||
| constructor( | ||
| box: THREE.Box3, | ||
| position?: THREE.Vector3, | ||
| quaternion?: THREE.Quaternion | ||
| ) { | ||
| this._box = box; | ||
| this._position = position; | ||
| this._quaternion = quaternion; | ||
| this._group = new THREE.Group(); | ||
| this.createAnnotations(); | ||
| } | ||
|
|
||
| /** | ||
| * Removes all annotations from the scene. | ||
| */ | ||
| clearAnnotations() { | ||
| this._group.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Creates the dimension lines and labels for the bounding box. | ||
| */ | ||
| createAnnotations() { | ||
| if (!this._box) { | ||
| return; | ||
| } | ||
|
|
||
| const size = new THREE.Vector3(); | ||
| this._box.getSize(size); | ||
|
|
||
| const min = this._box.min; | ||
| const max = this._box.max; | ||
|
|
||
| // Create dimension lines only for dimensions with a size greater than a small epsilon. | ||
| // This is useful for hiding zero-dimension measurements for 2D objects like edges. | ||
| if (size.x > 1e-6) { | ||
| this.createDimensionLine( | ||
| new THREE.Vector3(min.x, min.y, min.z), | ||
| new THREE.Vector3(max.x, min.y, min.z), | ||
| 'X', | ||
| size.x | ||
| ); | ||
| } | ||
| if (size.y > 1e-6) { | ||
| this.createDimensionLine( | ||
| new THREE.Vector3(max.x, min.y, min.z), | ||
| new THREE.Vector3(max.x, max.y, min.z), | ||
| 'Y', | ||
| size.y | ||
| ); | ||
| } | ||
| if (size.z > 1e-6) { | ||
| this.createDimensionLine( | ||
| new THREE.Vector3(max.x, max.y, min.z), | ||
| new THREE.Vector3(max.x, max.y, max.z), | ||
| 'Z', | ||
| size.z | ||
| ); | ||
| } | ||
|
|
||
| // The annotations are created for an axis-aligned box at the origin, so transform | ||
| // the group to match the object's actual position and orientation (if provided). | ||
| if (this._quaternion) { | ||
| this._group.quaternion.copy(this._quaternion); | ||
| } | ||
| if (this._position) { | ||
| this._group.position.copy(this._position); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Creates a single dimension line with a label. | ||
| * @param start The start point of the line. | ||
| * @param end The end point of the line. | ||
| * @param axis The axis name ('X', 'Y', or 'Z'). | ||
| * @param value The length of the dimension. | ||
| */ | ||
| createDimensionLine( | ||
| start: THREE.Vector3, | ||
| end: THREE.Vector3, | ||
| axis: string, | ||
| value: number | ||
| ) { | ||
| // Create the dashed line | ||
| const material = new THREE.LineDashedMaterial({ | ||
| color: 0x000000, | ||
| linewidth: 1, | ||
| scale: 1, | ||
| dashSize: 0.1, | ||
| gapSize: 0.1, | ||
| depthTest: false, // Render lines on top of other objects for better visibility | ||
| depthWrite: false, | ||
| transparent: true | ||
| }); | ||
| const geometry = new THREE.BufferGeometry().setFromPoints([start, end]); | ||
| // Create a thin halo (solid) line behind the dashed line to improve | ||
| // contrast when measurements pass over objects. | ||
| const haloMat = new THREE.LineBasicMaterial({ | ||
| color: 0xffffff, | ||
| linewidth: 4, | ||
| depthTest: false, | ||
| depthWrite: false, | ||
| transparent: true, | ||
| opacity: 0.85 | ||
| }); | ||
| const halo = new THREE.Line(geometry.clone(), haloMat); | ||
| halo.renderOrder = 0; // Ensure halo renders just before the dashed line | ||
| this._group.add(halo); | ||
|
|
||
| const line = new THREE.Line(geometry, material); | ||
| line.computeLineDistances(); | ||
| line.renderOrder = 1; // Ensure dashed line renders on top of the halo and other objects | ||
| this._group.add(line); | ||
|
|
||
| // Create the label | ||
| const labelDiv = document.createElement('div'); | ||
| labelDiv.className = 'measurement-label'; | ||
| labelDiv.textContent = `${axis}: ${value.toFixed(2)}`; | ||
| labelDiv.style.color = 'black'; | ||
| labelDiv.style.fontSize = '12px'; | ||
| labelDiv.style.backgroundColor = 'rgba(255, 255, 255, 0.7)'; | ||
| labelDiv.style.padding = '2px 5px'; | ||
| labelDiv.style.borderRadius = '3px'; | ||
|
|
||
| const label = new CSS2DObject(labelDiv); | ||
|
|
||
| // Position the label at the midpoint of the line | ||
| const midPoint = new THREE.Vector3() | ||
| .addVectors(start, end) | ||
| .multiplyScalar(0.5); | ||
| label.position.copy(midPoint); | ||
|
|
||
| this._group.add(label); | ||
| } | ||
|
|
||
| /** | ||
| * Getter for the THREE.Group containing the measurement annotations. | ||
| * This group can be added to a THREE.Scene to be rendered. | ||
| */ | ||
| public get group(): THREE.Group { | ||
| return this._group; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.