Display systems and decorator implementation #7899
-
|
I am trying to implement the x, y, z - axis lines in my viewer. I am having trouble with determining length of the lines to draw as axes dynamically. My goal is to have the lines to be drawn up to and touching the edges of the current screen/viewport. As I zoom in/out or rotate the view, the lines still should be touching the screen borders. My question is: is this possible and if yes, what is the best way to go about it. if not, why and what is the best way to get closest to our rendering goals. So far, I was able to use decorators with this implementation: const axisBuilder = context.createGraphicBuilder(GraphicType.WorldOverlay);
// Get the frustum corners in world coordinates
const frustum = vp.getFrustum();
const frustumCorners = frustum.points.map((corner) => vp.worldToView(corner));
// Calculate the maximum extents for each axis
const xExtent = Math.max(
...frustumCorners.map((corner) => Math.abs(corner.x - projectOrigin.x))
);
const yExtent = Math.max(
...frustumCorners.map((corner) => Math.abs(corner.y - projectOrigin.y))
);
const zExtent = Math.max(
...frustumCorners.map((corner) => Math.abs(corner.z - projectOrigin.z))
);
// Draw the X-axis
const xStart = projectOrigin.plusScaled(Vector3d.unitX(), -xExtent);
const xEnd = projectOrigin.plusScaled(Vector3d.unitX(), xExtent);
axisBuilder.setSymbology(ColorDef.red, ColorDef.red, 2);
axisBuilder.addLineString([xStart, xEnd]);
// Draw the Y-axis
const yStart = projectOrigin.plusScaled(Vector3d.unitY(), -yExtent);
const yEnd = projectOrigin.plusScaled(Vector3d.unitY(), yExtent);
axisBuilder.setSymbology(ColorDef.green, ColorDef.green, 2);
axisBuilder.addLineString([yStart, yEnd]);
// Draw the Z-axis
const zStart = projectOrigin.plusScaled(Vector3d.unitZ(), -zExtent);
const zEnd = projectOrigin.plusScaled(Vector3d.unitZ(), zExtent);
axisBuilder.setSymbology(ColorDef.blue, ColorDef.blue, 2);
axisBuilder.addLineString([zStart, zEnd]);
context.addDecorationFromBuilder(axisBuilder);However, this is not exactly what I want. if the camera is completely perpendicular to one of the axes, it is working as expected, even when zooming in and out: However, if rotated to be off angle from of the axes, the lines don't necessarily extend up to the edges as wanted: Is this by design? how do I achieve intended effect? I have created a sandbox with the above code for further context: https://www.itwinjs.org/sandbox/AnmolShrestha/florencia-the-bronze-rodent-102 |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 1 reply
-
|
I have not had time to look at this extensively yet, but my first gut reaction is near/far clip planes of the frustum could be involved in the behavior. @eringram was looking briefly at this too, last week. We will look for some more information when time permits. |
Beta Was this translation helpful? Give feedback.
-
|
itwinjs.org won't let me sign in to fork and modify your sandbox, but consider transforming your lines from world to view coordinates and drawing them as view overlays. |
Beta Was this translation helpful? Give feedback.
-
|
The following change to your sandbox will illustrate how the project extents are limiting the near and far planes, clipping the lengths of your lines. With this change, your lines will be longer, but still clip at the frustum planes if you zoom/rotate far enough. const onIModelAppInit = async () => {
// Create view settings
IModelApp.viewManager.onViewOpen.addOnce((viewport) => {
const view = viewport.view.isSpatialView() ? viewport.view : undefined;
if (view) {
view.getGroundExtents = () => {
const extents = Range3d.fromJSON(view.iModel.projectExtents);
extents.scaleAboutCenterInPlace(10);
return extents;
};
}
// Add the decorator to the view manager so that the app is ready to draw.
IModelApp.viewManager.addDecorator(new AxesDecorator(viewport));
});
};The following is my attempt to draw your lines in view coordinates. It has its own issues. // Draw the X-axis
const xStart = projectOrigin.plusScaled(Vector3d.unitX(), -xExtent);
const xEnd = projectOrigin.plusScaled(Vector3d.unitX(), xExtent);
vp.worldToView(xStart, xStart);
vp.worldToView(xEnd, xEnd);
xStart.z = xEnd.z = 0;
axisBuilder.setSymbology(ColorDef.red, ColorDef.red, 2);
axisBuilder.addLineString([xStart, xEnd]);
// Draw the Y-axis
const yStart = projectOrigin.plusScaled(Vector3d.unitY(), -yExtent);
const yEnd = projectOrigin.plusScaled(Vector3d.unitY(), yExtent);
vp.worldToView(yStart, yStart);
vp.worldToView(yEnd, yEnd);
yStart.z = yEnd.z = 0;
axisBuilder.setSymbology(ColorDef.green, ColorDef.green, 2);
axisBuilder.addLineString([yStart, yEnd]);
// Draw the Z-axis
const zStart = projectOrigin.plusScaled(Vector3d.unitZ(), -zExtent);
const zEnd = projectOrigin.plusScaled(Vector3d.unitZ(), zExtent);
vp.worldToView(zStart, zStart);
vp.worldToView(zEnd, zEnd);
zStart.z = zEnd.z = 0;
axisBuilder.setSymbology(ColorDef.blue, ColorDef.blue, 2);
axisBuilder.addLineString([zStart, zEnd]);
context.addDecorationFromBuilder(axisBuilder);
}Why are you trying to do this and have you considered alternative solutions? Are you familiar with the ACS triad view flag that displays a decoration representing the axes? |
Beta Was this translation helpful? Give feedback.
-
|
@anmolshres98 I was able to get your desired behavior working as a view overlay, working off of Paul's comment. If you want to extend the axis lines all the way to the edge of the viewport, you can use two points, such as the origin (in view coordinates) and the origin plus an axis's unit vector, to calculate the slope of the line you want to draw. Then you can use that slope to find where the line intersects the edge of the viewport (the left and right side of the ViewRect). Here's a function I wrote to do that given the viewport, origin in world coordinates, and unit vector of one of the axes: function GetAxisPoints(vp: ScreenViewport, origin: Point3d, unitVector: Vector3d): Point3d[] {
const point = origin.plus(unitVector);
const originView = vp.worldToView(origin);
vp.worldToView(point, point);
originView.z = point.z = 0;
const vpWidth = vp.viewRect.right;
const slope = (originView.y - point.y) / (originView.x - point.x);
const yIntercept = point.y - (slope * point.x);
const start = new Point3d(0, yIntercept, 0);
const end = new Point3d(
vpWidth,
slope * vpWidth + yIntercept,
0
);
return [start, end];
}This code is probably not optimized and there's also an issue where the axes disappear when you look at it from some head-on angles (maybe a dividing by zero problem) but this might help you extend the lines at least. Sandbox: https://www.itwinjs.org/sandboxes/ErinIngram/dot-the-gold-cat-607/ |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for response everyone. Really appreciate it. @pmconne Drawing the axes lines are part of the effort in our app where we are trying to highlight a point. Drawing the axes is the not the final solution we might land at and acs triads are the starting point for our efforts. We are looking to see if we can add more to the triad flags on our app. For now, we want to see what our options are and quickly experiment on what they look like. |
Beta Was this translation helpful? Give feedback.




@anmolshres98 I was able to get your desired behavior working as a view overlay, working off of Paul's comment.
If you want to extend the axis lines all the way to the edge of the viewport, you can use two points, such as the origin (in view coordinates) and the origin plus an axis's unit vector, to calculate the slope of the line you want to draw. Then you can use that slope to find where the line intersects the edge of the viewport (the left and right side of the ViewRect).
Here's a function I wrote to do that given the viewport, origin in world coordinates, and unit vector of one of the axes: