Select Git revision
AnnotationSvgDrawing.js

Anthony authored
AnnotationSvgDrawing.js 7.07 KiB
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences';
import { VideosReferences } from 'mirador/dist/es/src/plugins/VideosReferences';
import { renderWithPaperScope, PaperContainer } from '@psychobolt/react-paperjs';
import {
EllipseTool,
RectangleTool,
FreeformPathTool,
} from '@psychobolt/react-paperjs-editor';
import { Point } from 'paper';
import flatten from 'lodash/flatten';
import EditTool from './EditTool';
import { mapChildren } from '../utils';
/** */
class AnnotationSvgDrawing extends Component {
/** */
constructor(props) {
super(props);
this.paper = null;
this.getDisplayProps = this.getDisplayProps.bind(this);
this.onPaperResize = this.onPaperResize.bind(this);
this.paperDidMount = this.paperDidMount.bind(this);
this.addPath = this.addPath.bind(this);
}
/** */
componentDidMount() {
const { windowId } = this.props;
this.OSDReference = OSDReferences.get(windowId);
this.onPaperResize();
}
/** Sync drawing canvas size/zoom with annotations canvas */
onPaperResize(ev) {
const { windowId } = this.props;
if (VideosReferences.get(windowId) && this.paper) {
const { canvasOverlay, video } = VideosReferences.get(windowId);
const { height, width } = canvasOverlay.ref.current;
const { videoHeight, videoWidth } = video;
this.paper.view.center = new Point(videoWidth / 2, videoHeight / 2);
this.paper.view.zoom = canvasOverlay.scale;
this.paper.view.viewSize = new this.paper.Size(width, height);
}
}
/** Save paperjs ref once created */
paperDidMount(paper) {
this.paper = paper;
}
/** */
addPath(path) {
const { closed, strokeWidth, updateGeometry } = this.props;
path.closed = closed; // eslint-disable-line no-param-reassign
// Reset strokeWidth for persistence
path.strokeWidth = strokeWidth; // eslint-disable-line no-param-reassign
path.data.state = null; // eslint-disable-line no-param-reassign
const svgExports = flatten(path.project.layers.map((layer) => (
flatten(mapChildren(layer)).map((aPath) => aPath.exportSVG({ asString: true }))
)));
updateGeometry({
svg: svgExports.join(''),
});
}
getDisplayProps() {
const { windowId } = this.props;
const osdref = OSDReferences.get(windowId);
const videoref = VideosReferences.get(windowId);
if (osdref) {
const { viewport } = osdref.current;
const img = osdref.current.world.getItemAt(0);
const center = img.viewportToImageCoordinates(viewport.getCenter(true));
return {
canvasProps: { style: { height: '100%', width: '100%' } },
viewProps: {
center: new Point(center.x, center.y),
rotation: viewport.getRotation(),
scaling: new Point(viewport.getFlip() ? -1 : 1, 1),
zoom: img.viewportToImageZoom(viewport.getZoom()),
},
};
}
if (videoref) {
const { height, width } = videoref.canvasOverlay.ref.current;
return {
canvasProps: {
height,
resize: 'true',
style: {
left: 0, position: 'absolute', top: 0,
},
width,
},
viewProps: {
center: new Point(width / 2, height / 2),
height,
width,
zoom: videoref.canvasOverlay.scale,
},
};
}
throw new Error('Unknown or missing data player, not OpenSeadragon (image viewer) nor the video player');
}
/** */
paperThing() {
const { viewProps, canvasProps } = this.getDisplayProps();
const {
activeTool, fillColor, strokeColor, strokeWidth, svg,
} = this.props;
if (!activeTool || activeTool === 'cursor') {
return null;
}
// // Setup Paper View to have the same center and zoom as the OSD Viewport
// const viewportZoom = this.OSDReference.viewport.getZoom(true);
// const image1 = this.OSDReference.world.getItemAt(0);
// const center = image1.viewportToImageCoordinates(
// this.OSDReference.viewport.getCenter(true),
// );
// const flipped = this.OSDReference.viewport.getFlip();
//
// const viewProps = {
// center: new Point(center.x, center.y),
// rotation: this.OSDReference.viewport.getRotation(),
// scaling: new Point(flipped ? -1 : 1, 1),
// zoom: image1.viewportToImageZoom(viewportZoom),
// };
let ActiveTool = RectangleTool;
switch (activeTool) {
case 'rectangle':
ActiveTool = RectangleTool;
break;
case 'ellipse':
ActiveTool = EllipseTool;
break;
case 'freehand':
ActiveTool = FreeformPathTool;
break;
case 'edit':
ActiveTool = EditTool;
break;
default:
break;
}
return (
<div
className="foo"
style={{
height: '100%', left: 0, position: 'absolute', top: 0, width: '100%',
}}
>
<PaperContainer
canvasProps={{ style: { height: '100%', width: '100%' } }}
viewProps={viewProps}
>
{renderWithPaperScope((paper) => {
const paths = flatten(paper.project.layers.map((layer) => (
flatten(mapChildren(layer)).map((aPath) => aPath)
)));
if (svg && paths.length === 0) {
paper.project.importSVG(svg);
}
paper.settings.handleSize = 10; // eslint-disable-line no-param-reassign
paper.settings.hitTolerance = 10; // eslint-disable-line no-param-reassign
return (
<ActiveTool
pathProps={{
fillColor,
strokeColor,
strokeWidth: strokeWidth / paper.view.zoom,
}}
paper={paper}
onPathAdd={this.addPath}
/>
);
})}
</PaperContainer>
</div>
);
}
/** */
render() {
const { windowId } = this.props;
const osdref = OSDReferences.get(windowId);
const videoref = VideosReferences.get(windowId);
if (!osdref && !videoref) {
throw new Error("Unknown or missing data player, didn't found OpenSeadragon (image viewer) nor the video player");
}
if (osdref && videoref) {
throw new Error('Unhandled case: both OpenSeadragon (image viewer) and video player on the same canvas');
}
const container = osdref
? osdref.current.element
: videoref.ref.current.parentElement;
return (
ReactDOM.createPortal(this.paperThing(), container)
);
}
}
AnnotationSvgDrawing.propTypes = {
activeTool: PropTypes.string,
closed: PropTypes.bool,
edit: PropTypes.bool,
fillColor: PropTypes.string,
strokeColor: PropTypes.string,
strokeWidth: PropTypes.number,
svg: PropTypes.string,
updateGeometry: PropTypes.func.isRequired,
windowId: PropTypes.string.isRequired
}
AnnotationSvgDrawing.defaultProps = {
activeTool: null,
closed: false,
strokeColor: '#cc0000',
strokeWidth: 3,
fillColor: null,
svg: null,
};
export default AnnotationSvgDrawing;