-
Notifications
You must be signed in to change notification settings - Fork 24
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
Add measurement tools #7258
Add measurement tools #7258
Conversation
Just for information purposes:The algorithm used for the calculation of the area of the polygon of the area measurement tool works correctly, when the point are exactly on a line: The algorithm was taken from: https://www.mathopenref.com/coordpolygonarea2.html function polygonArea(X, Y, numPoints)
{
area = 0; // Accumulates area
j = numPoints-1;
for (i=0; i<numPoints; i++)
{ area += (X[j]+X[i]) * (Y[j]-Y[i]);
j = i; //j is previous vertex to i
}
return area/2;
}
// The triangle reaches from 1,1 to 4,1 to 1,3 and back to 1,1. Thus the area is h*w/2 = 2*3/2 = 3
X = [1,2,3,4,2.5,1,1]
Y = [1,1,1,1,2,3,2]
polygonArea(X,Y,7)
=> -3
// The result is correct (and negative due to the points being in counter clockwise order).
// The implementation of the algorithm in this pr is secure against this flaw by using the absolute value of the calculated area. |
- add double click to linemeasurement tool, - WIP adding filled area to area measurement tool
const guardFromResettedTool = (func: Function) => { | ||
// TODO: Problem typing is lost | ||
return (...args: any) => { | ||
if (lineMeasurementGeometry.isResetted && isMeasuring) { | ||
isMeasuring = false; | ||
return; | ||
} | ||
func(...args); | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the following three functions need to be guarded against the user having used escape while still in the process of measuring, to interrupt the measurement. When escape was pressed, the lineMeasurementGeometry
carries that information in the field isResetted
.
The guarding function takes care of guarding these three functions against the user using escape while still measuring. But the typing is lost (as far as I would guess) and the code ready quite quirky IMO. Maybe you have a better idea?
LINE_MEASUREMENT: "pointer", | ||
AREA_MEASUREMENT: "pointer", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you think of a better suitable mouse pointer attribute for the new tools?
@philippotto This pr should now be ready for the first review. After the CI is done, I will also ask for further UX feedback in slack. |
- allow panning in line measurement tool - add custom mouse cursors - use area unit for area measurement tool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff :) Will test now!
function mapStateToProps(state: OxalisState): Props { | ||
return { | ||
position: state.uiInformation.measurementTooltipPosition, | ||
flycam: state.flycam, | ||
activeTool: state.uiInformation.activeTool, | ||
datasetScale: state.dataset.dataSource.scale, | ||
}; | ||
} | ||
|
||
const connector = connect(mapStateToProps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is a function components, I'd rather use useSelector
to access the store.
@@ -780,6 +784,171 @@ export class QuickSelectTool { | |||
static onToolDeselected() {} | |||
} | |||
|
|||
export class LineMeasurementTool { | |||
static DOUBLE_CLICK_TIME_THRESHOLD = 600; | |||
static getPlaneMouseControls(_planeId: OrthoView): any { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPlaneMouseControls
of the area measurement tool doesn't accept that parameter. maybe make these interfaces consistent?
return; | ||
} | ||
const currentTime = Date.now(); | ||
if (currentTime - lastLeftClickTime <= this.DOUBLE_CLICK_TIME_THRESHOLD) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it make sense to check that the two clicks were registered roughly at the same spot?
lineMeasurementGeometry.reset(); | ||
lineMeasurementGeometry.hide(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several places where reset and hide is called. How about adding a resetAndHide
helper method?
@@ -61,6 +107,43 @@ export class ContourGeometry { | |||
mesh.geometry.attributes.position.needsUpdate = true; | |||
mesh.geometry.setDrawRange(0, this.vertexBuffer.getLength()); | |||
mesh.geometry.computeBoundingSphere(); | |||
this.connectingLine.geometry.attributes.position.needsUpdate = true; | |||
// this.connectingLine.geometry.setDrawRange(0, 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove?
this.finalizeMesh(); | ||
} | ||
|
||
setTopPoint(pos: Vector3) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I don't really understand what a "top point" is. maybe add a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment and renamed the method to updateLatestPointPosition
.
By top point I meant the latest added point to the array of points (like the latest element on a stack is called top element).
@@ -231,3 +314,113 @@ export class QuickSelectGeometry { | |||
rectangle.material.alphaMap = null; | |||
} | |||
} | |||
|
|||
export class LineMeasurementGeometry { | |||
color: THREE.Color; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a class comment explaining that this is used for displaying "line segments" (not sure whether this is the right term?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I called it connected line segments
🤷
} | ||
|
||
setStartPoint(pos: Vector3, initialOrthoView: OrthoView) { | ||
this.wasReset = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to either add a comment explaining that this method expects an empty vertexBuffer or add this.vertexBuffer.clear() to this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch 👍, I went for the 2nd suggestion, as this is more bullet proof against potential bugs imo.
Overall it feels pretty good 👍 My testing results:
Please remind me again why the measurement geometries are hidden as soon as the user moves? Is it because it would be hard to stick the tooltip to the geometry? If this is the case, one could also think about moving the tooltip content simply into the toolbar 🤔 But I think we'd need actual user feedback for that. Right now, I imagine it to be confusing potentially (could be percepted as bug, too). |
- Add double click to area measurement to cancel the tool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your feedback @philippotto: I think I addressed everything now.
Please remind me again why the measurement geometries are hidden as soon as the user moves?
you mentioned hiding the tool as soon as the camera moves here and here
Is it because it would be hard to stick the tooltip to the geometry?
Jup, that is the case 👍
Let's wait for @normanrz answer 😺
@@ -231,3 +314,113 @@ export class QuickSelectGeometry { | |||
rectangle.material.alphaMap = null; | |||
} | |||
} | |||
|
|||
export class LineMeasurementGeometry { | |||
color: THREE.Color; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I called it connected line segments
🤷
} | ||
|
||
setStartPoint(pos: Vector3, initialOrthoView: OrthoView) { | ||
this.wasReset = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch 👍, I went for the 2nd suggestion, as this is more bullet proof against potential bugs imo.
this.finalizeMesh(); | ||
} | ||
|
||
setTopPoint(pos: Vector3) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment and renamed the method to updateLatestPointPosition
.
By top point I meant the latest added point to the array of points (like the latest element on a stack is called top element).
I think keeping the lines on move would be nice. But how would a user remove them again? Also, Alt+Mouse Move doesn't work for me in this tool mode. That feels like a bug. |
I misunderstood your statement a little 😅 Now the lines are kept (unless the user moves in the z direction of the viewport) and the tooltip follows the line / area mesurement |
Via right or double left click.
For me this works. Maybe the viewports weren't the active part of the wk webpage when you tested? 🤔 |
The sticky tooltip is quite cool 👍 However, I ran into some bugs:
minor problems:
I recorded a video for these issues:
measurement-bugs.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like @normanrz, alt+mousemove doesn't work for me while using the line tool
Ahh, ok now I understand what you mean: It is impossible to move around while measuring with a line 👍. Previously I only tested moving with alt when I was not actively measuring a line.
The alt moving should now be fixed for both measurement tools during the measurement and also while not measuring.
alt+mousemove for the area tool works sort of. it changes to the proper tool and does the moving, but the tool still registers new points around the mouse.
This should be fixed now. The tool should register new points while alt is pressed.
I got into a weird state where the measurement tools got broken. I can reproduce this by starting measuring in XY and then adding points in YZ. see video.
I think this was due to an instant reset of the measurement caused by the distance_measurement_tooltip
. See my comment :)
when moving the mouse fast, I can move it into the tooltip which is a bit weird since I cannot pinpoint the next click in the area of the tooltip. not sure, how to solve this. as a user I can always leave the tooltip and then hidden space is visible again. so, it's not a big issue, but I ran into this quite quickly, which is why I'm writing this.
While measuring, the tooltip now ignores all pointer event, which mitigates this problem as the inputcatcher get the pointer event, even when the mouse hits the tooltip. Thanks to @fm3 for the solution.
This is done by saving whether the measurement tool is measuring at the moment in the store.
I added a tooltip to the distance option for the context menu as the entry could be misunderstood. See for reference https://scm.slack.com/archives/C5AKLAV0B/p1695894445554669
useEffect(() => { | ||
if ( | ||
position != null && | ||
Math.floor(currentPosition[thirdDim]) !== Math.floor(position[thirdDim]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the "trick" hopefully fixing the bug you described. In case the flycam is not at an exact numeric position, the fraction of it causes the !==
to be false. This is due to the value stored in position being already floored by the measurement tool (which sets the value state.uiInformation.measurementToolInfo.lastMeasuredPosition
)
Thus flooring the value should fix that the tooltip auto resets the measurement all the time and only triggers the reset in case the third coordinate actually changes by a full unit.
Store.dispatch(setIsMeasuringAction(false)); | ||
} | ||
const isAltPressed = evt.altKey; | ||
if (isAltPressed || plane !== this.initialPlane || !this.isMeasuring) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the isAltPressed
enabled moving with alt while measuring
const currentTime = Date.now(); | ||
if ( | ||
currentTime - lastLeftClickTime <= DOUBLE_CLICK_TIME_THRESHOLD && | ||
Math.abs(pos.x - lastClickPosition.x) + Math.abs(pos.y - lastClickPosition.y) < 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extract the 6 into a constant?
@@ -784,12 +784,32 @@ export class QuickSelectTool { | |||
static onToolDeselected() {} | |||
} | |||
|
|||
function getDoubleClickGuard() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool abstraction 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome! works very well :)
there are several PR comments where the code is marked as outdated. I assume these comments can be resolved? other than that, only see my one comment about a magic number.
I skimmed through them and nothing of these comments seemed to be important 👍 |
URL of deployed dev instance (used for testing):
Steps to test:
Open an annotation of any dataset
Select the new measurement tool in the toolbar (the new tool should be the right most)
Try measuring a path. Setting path point can be done via left click. Setting the last point via double-/or right-click and terminating the measurement by hitting escape or another right click.
Try copying the measured length from the tooltip via the copy icon.
Start measuring and terminating it while still in the process using escape. This should not lead to any errors or unexpected behaviour. (if so, I have a regression bug 🙈)
Select the area measurement tool. Draw an area via right click. The area should always be closed by another highlighting line to highlight that the measured area is a closed area connecting the current end and start point.
Upon releasing the left mouse drag, the highlighted line should be gone and the connecting line should be in the normal color.
Potential bug: see below: When hitting escape while measuring an area, the area measurement tool is not correctly reset.
Please report any unexpected behavior.
Test the tracing tool. It should still work as expected as this PR fiddled around with the helper geometry used by the tracing tool.
TODOs / Bugs:
[ ] Add fill area for area measurement toolWe decided against that as this is quite the hurdle and thus went for always having a highlighted connection line between the start and end of the drawn area to highlight that the user is measuring an area.Known issue / to discuss:
Issues:
(Please delete unneeded items, merge only when none are left open)