glance
glance copied to clipboard
Include Measurements in screenshots
Hello everyone,
It would be an interesting feature for the screenshots to also include measurements; any lead on what needs to be done in order to implement this? Thank you!
That would be quite useful! Since measurements exist in a separate SVG layer on top of the canvas, we (1) need to convert the SVG into an image (I think the SVG is accessible via view.getReferenceByName('widgetManager').getReferenceByName('svgRoot')
), and then use a hidden canvas to draw the SVG on top of the screenshot image. I haven't researched the details of how to do this, but if you would like to try implementing it I'd be happy to provide feedback.
Thank you for this explanation! I will experiment with this and come back to you.
I was able to include the measurements in the screenshot by changing the generateImage()
function from the ScreenshotDialog to this:
function generateImage() {
const img = new Image();
const measurementImg = new Image();
const measurementSvg = this.$proxyManager
.getActiveView()
.getReferenceByName('widgetManager')
.getReferenceByName('svgRoot');
measurementImg.addEventListener('load', () => {
const ctx = this.canvas.getContext('2d');
ctx.drawImage(measurementImg, 0, 0);
const imageType = `image/${this.fileType.substr(1)}`;
this.imageUrl = this.canvas.toDataURL(imageType);
});
img.addEventListener('load', () => {
const ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.canvas.width = img.width;
this.canvas.height = img.height;
if (!this.transparentBackground) {
ctx.fillStyle = this.backgroundToFillStyle(
this.screenshot.viewData.background
);
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
ctx.drawImage(img, 0, 0);
// Add the measurements
// Set same height and width as image
measurementSvg.setAttribute('width', img.width);
measurementSvg.setAttribute('height', img.height);
const measurementSvgSerialized = new XMLSerializer()
.serializeToString(measurementSvg)
.replace('°', ' deg'); // ! This is the fix to avoid an Encoding error with btoa
// TODO: find nicer fix
const measurementSvgSrc = `data:image/svg+xml;base64,${btoa(
measurementSvgSerialized
)}`; // make it ready to be read as a source in an image
measurementImg.src = measurementSvgSrc; // then draw the measurement
});
img.src = this.screenshot.imgSrc; // 1st draw the data and then the measurements
}
...But it doesn't work with angles, because of the '°' symbol; it then gives an encoding error, I am guessing because of btoa
. Any idea on how this could be worked around? For now, I just changed the '°' symbol to 'deg'.
Running btoa('°')
works for me. But as a broader point, check out the Unicode section of the btoa docs. The general idea is that JS strings are UTF-16, and so chars that can't fit in 1 byte can't be managed by btoa, since btoa operates on a byte-by-byte basis. Though the "°" char in your post is a single byte, so I wonder what else is happening there.
Ah strange! I get this error when opening the src data:image/svg+xml;base64,${btoa(measurementSvgSerialized)}
in another window.
I was able to solve it by using a UTF-8 encoding:
const measurementSvgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(
new XMLSerializer().serializeToString(measurementSvg)
)}`; // make it ready to be read as a source in an image
There's still one thing that I would like to do: I want the screenshot to be cropped around the image, rather than having it inside of a background. Do you have an idea of how I could do this? I also wonder where I can find the coordinates of the image, which I could then use to crop it. Thanks!
What you are proposing will be more difficult, since a scene could have much more than just an image. But if you only constrain yourself to an image, you would need to get the 4 coordinates of your image corners and use vtkPixelSpaceCallbackMapper
to get the display coordinates for the bounding box of your image.