/* * org.openmicroscopy.shoola.util.roi.io.InputServerStrategy * *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.roi.io; import java.awt.Font; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import static org.jhotdraw.draw.AttributeKeys.FILL_COLOR; import static org.jhotdraw.draw.AttributeKeys.FONT_BOLD; import static org.jhotdraw.draw.AttributeKeys.FONT_FACE; import static org.jhotdraw.draw.AttributeKeys.FONT_ITALIC; import static org.jhotdraw.draw.AttributeKeys.FONT_SIZE; import static org.jhotdraw.draw.AttributeKeys.STROKE_CAP; import static org.jhotdraw.draw.AttributeKeys.STROKE_COLOR; import static org.jhotdraw.draw.AttributeKeys.TEXT_COLOR; import static org.jhotdraw.draw.AttributeKeys.STROKE_WIDTH; import org.jhotdraw.draw.AttributeKey; import org.jhotdraw.geom.BezierPath.Node; import org.openmicroscopy.shoola.util.CommonsLangUtils; import ome.model.units.BigResult; import omero.model.Length; import omero.model.enums.UnitsLength; import org.openmicroscopy.shoola.util.roi.ROIComponent; import org.openmicroscopy.shoola.util.roi.exception.NoSuchROIException; import org.openmicroscopy.shoola.util.roi.exception.ROICreationException; import org.openmicroscopy.shoola.util.roi.figures.MeasureBezierFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasureEllipseFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasureLineFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasureMaskFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasurePointFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasureRectangleFigure; import org.openmicroscopy.shoola.util.roi.figures.MeasureTextFigure; import org.openmicroscopy.shoola.util.roi.figures.ROIFigure; import org.openmicroscopy.shoola.util.roi.io.util.SVGTransform; import org.openmicroscopy.shoola.util.roi.model.ROI; import org.openmicroscopy.shoola.util.roi.model.ROIShape; import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKeys; import org.openmicroscopy.shoola.util.roi.model.annotation.MeasurementAttributes; import org.openmicroscopy.shoola.util.roi.model.util.Coord3D; import org.openmicroscopy.shoola.util.ui.drawingtools.figures.PointFigure; import omero.gateway.model.EllipseData; import omero.gateway.model.LineData; import omero.gateway.model.PointData; import omero.gateway.model.ROIData; import omero.gateway.model.RectangleData; import omero.gateway.model.ShapeData; import omero.gateway.model.PolygonData; import omero.gateway.model.MaskData; import omero.gateway.model.PolylineData; import omero.gateway.model.ShapeSettingsData; import omero.gateway.model.TextData; /** * Transforms the ROI server into the corresponding UI objects. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * @since 3.0-Beta4 */ class InputServerStrategy { /** Identifies the transformation attribute. */ public final static AttributeKey<AffineTransform> TRANSFORM = new AttributeKey<AffineTransform>("transform", null, true); /** * The map of the default values of each object along with the keys used. */ private final static Map<AttributeKey, Object> DEFAULT_ATTRIBUTES; static { DEFAULT_ATTRIBUTES = new HashMap<AttributeKey, Object>(); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.FILL_COLOR, ShapeSettingsData.DEFAULT_FILL_COLOUR); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.STROKE_COLOR, ShapeSettingsData.DEFAULT_STROKE_COLOUR); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.TEXT_COLOR, ShapeSettingsData.DEFAULT_FILL_COLOUR); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.FONT_SIZE, ShapeSettingsData.DEFAULT_FONT_SIZE); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.STROKE_WIDTH, ShapeSettingsData.DEFAULT_STROKE_WIDTH); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.TEXT, ROIFigure.DEFAULT_TEXT); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.MEASUREMENTTEXT_COLOUR, ShapeSettingsData.DEFAULT_FILL_COLOUR); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.SHOWMEASUREMENT, Boolean.valueOf(false)); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.SHOWTEXT, Boolean.valueOf(false)); DEFAULT_ATTRIBUTES.put(MeasurementAttributes.SCALE_PROPORTIONALLY, Boolean.valueOf(false)); } /** Holds the ROIs which have been created. */ private List<ROI> roiList; /** The ROIComponent. */ private ROIComponent component; /** * Adds any missing basic attributes from the default attributes map, * to the figure. * * @param figure The figure to handle. */ private void addMissingAttributes(ROIFigure figure) { Map<AttributeKey, Object> attributes=figure.getAttributes(); Iterator<AttributeKey> i = DEFAULT_ATTRIBUTES.keySet().iterator(); AttributeKey key; while (i.hasNext()) { key = i.next(); if (!attributes.containsKey(key)) key.set(figure, DEFAULT_ATTRIBUTES.get(key)); } } /** * Transforms a server ROI into its UI representation. * * @param roi The object to transform. * @param userID The id of the user currently logged in. * @return See above. */ private ROI createROI(ROIData roi, long userID) throws NoSuchROIException, ROICreationException { long id = roi.getId(); boolean edit = roi.canEdit(); if (edit) { edit = roi.getOwner().getId() == userID; } ROI newROI = component.createROI(id, id <= 0, edit, roi.canDelete(), roi.canAnnotate()); newROI.setOwnerID(roi.getOwner().getId()); ROIShape shape; ShapeData shapeData; Iterator<List<ShapeData>> i = roi.getIterator(); List<ShapeData> list; Iterator<ShapeData> j; Coord3D c; while (i.hasNext()) { list = (List<ShapeData>) i.next(); j = list.iterator(); while (j.hasNext()) { shapeData = (ShapeData) j.next(); shape = createROIShape(shapeData, newROI, userID); if (shape != null) { shape.getFigure().setMeasurementUnits( component.getMeasurementUnits()); c = shape.getCoord3D(); if (c != null) { if (!component.containsShape(newROI.getID(), c)) { component.addShape(newROI.getID(), c, shape); } } } } } return newROI; } /** * Transforms the shape into its corresponding the UI object. * * @param data The object to transform. * @param roi The UI ROI hosting the newly created shape. * @param userID The id of the user currently logged in. * @return See above. */ private ROIShape createROIShape(ShapeData data, ROI roi, long userID) { int z = data.getZ(); int t = data.getT(); Coord3D coord = new Coord3D(z, t); ROIFigure fig = createROIFigure(data); fig.setReadOnly(data.isReadOnly()); long id = data.getOwner().getId(); if (id >= 0) fig.setInteractable(id == userID); try { coord.setChannel(data.getC()); } catch (Exception e) { } // Check that the parent element is not a text element, as they have not // got any other text associated with them. addMissingAttributes(fig); ROIShape shape = new ROIShape(roi, coord, fig, fig.getBounds()); shape.setROIShapeID(data.getId()); shape.setData(data); return shape; } /** * Creates a figure corresponding to the passed shape. * * @param shape The shape to transform. * @return See above. */ private ROIFigure createROIFigure(ShapeData shape) { if (shape instanceof RectangleData) { return createRectangleFigure((RectangleData) shape); } else if (shape instanceof EllipseData) { return createEllipseFigure((EllipseData) shape); } else if (shape instanceof LineData) { return createLineFigure((LineData) shape); } else if (shape instanceof PointData) { return createPointFigure((PointData) shape); } else if (shape instanceof PolylineData) { return createPolyOrlineFigure((PolylineData) shape); } else if (shape instanceof PolygonData) { return createPolygonFigure((PolygonData) shape); } else if (shape instanceof MaskData) { return createMaskFigure((MaskData) shape); } else if (shape instanceof TextData) { return createTextFigure((TextData) shape); } return null; } /** * Transforms the passed ellipse into its UI corresponding object. * * @param data The ellipse to transform. * @return See above. */ private MeasureEllipseFigure createEllipseFigure(EllipseData data) { double cx = data.getX(); double cy = data.getY(); double rx = data.getRadiusX(); double ry = data.getRadiusY(); double x = cx-rx; double y = cy-ry; double width = rx*2d; double height = ry*2d; MeasureEllipseFigure fig = new MeasureEllipseFigure(data.getText(), x, y, width, height, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setEllipse(x, y, width, height); fig.setText(data.getText()); fig.setVisible(data.isVisible()); addShapeSettings(fig, data.getShapeSettings()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed ellipse into its UI corresponding object. * * @param data The ellipse to transform. * @return See above. */ private MeasurePointFigure createPointFigure(PointData data) { double r = PointFigure.FIGURE_SIZE/2; double x = Math.abs(data.getX()-r); double y = Math.abs(data.getY()-r); MeasurePointFigure fig = new MeasurePointFigure(data.getText(), x, y, 2*r, 2*r, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setVisible(data.isVisible()); addShapeSettings(fig, data.getShapeSettings()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed textData into its UI corresponding object. * * @param data The ellipse to transform. * @return See above. */ private MeasureTextFigure createTextFigure(TextData data) { double x = data.getX(); double y = data.getY(); MeasureTextFigure fig = new MeasureTextFigure(x, y, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setText(data.getText()); fig.setVisible(data.isVisible()); addShapeSettings(fig, data.getShapeSettings()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed rectangle into its UI corresponding object. * * @param data The rectangle to transform. * @return See above. */ private MeasureRectangleFigure createRectangleFigure(RectangleData data) { double x = data.getX(); double y = data.getY(); double width = data.getWidth(); double height = data.getHeight(); MeasureRectangleFigure fig = new MeasureRectangleFigure(x, y, width, height, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); addShapeSettings(fig, data.getShapeSettings()); fig.setText(data.getText()); fig.setVisible(data.isVisible()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed mask into its UI corresponding object. * * @param data The mask to transform. * @return See above. */ private MeasureMaskFigure createMaskFigure(MaskData data) { double x = data.getX(); double y = data.getY(); double width = data.getWidth(); double height = data.getHeight(); BufferedImage mask = data.getMaskAsBufferedImage(); MeasureMaskFigure fig = new MeasureMaskFigure(x, y, width, height, mask, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setVisible(data.isVisible()); fig.setVisible(true); addShapeSettings(fig, data.getShapeSettings()); fig.setText(data.getText()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed line into its UI corresponding object. * * @param data The line to transform. * @return See above. */ private MeasureLineFigure createLineFigure(LineData data) { double x1 = data.getX1(); double y1 = data.getY1(); double x2 = data.getX2(); double y2 = data.getY2(); MeasureLineFigure fig = new MeasureLineFigure(data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.removeAllNodes(); fig.setVisible(data.isVisible()); fig.addNode(new Node(x1, y1)); fig.addNode(new Node(x2, y2)); addShapeSettings(fig, data.getShapeSettings()); fig.setText(data.getText()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the polygon into its UI corresponding object. * * @param data The polygon to transform. * @return See above. */ private MeasureBezierFigure createPolygonFigure(PolygonData data) { MeasureBezierFigure fig = new MeasureBezierFigure(false, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setVisible(data.isVisible()); List<Point2D.Double> points = data.getPoints(); List<Point2D.Double> points1 = data.getPoints1(); List<Point2D.Double> points2 = data.getPoints2(); List<Integer> mask = data.getMaskPoints(); for (int i = 0; i < points.size(); i++) { fig.addNode(new Node(i, points.get(i), points.get(i), points.get(i))); } addShapeSettings(fig, data.getShapeSettings()); String text = data.getText(); if (text == null || text.trim().length() == 0) text = ROIFigure.DEFAULT_TEXT; fig.setText(text); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} fig.setClosed(true); return fig; } /** * Test to see if the polyline object passed in is actually a line/multi * segment line or a polyline. * * @param data The polyline to transform. * @return See above. */ private ROIFigure createPolyOrlineFigure(PolylineData data) { List<Integer> mask = data.getMaskPoints(); boolean line = true; for (int i = 0 ; i < mask.size(); i++) { if (mask.get(i) != 0) line = false; } if (line) return createLineFromPolylineFigure(data); return createPolylineFromPolylineFigure(data); } /** * Transforms the passed polyline into a line figure are it has only move * to operations. * * @param data The polyline to transform. * @return See above. */ private ROIFigure createLineFromPolylineFigure(PolylineData data) { List<Point2D.Double> points = data.getPoints(); MeasureLineFigure fig = new MeasureLineFigure(data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.removeAllNodes(); fig.setVisible(data.isVisible()); for (int i = 0; i < points.size(); i++) fig.addNode(new Node(points.get(i))); addShapeSettings(fig, data.getShapeSettings()); fig.setText(data.getText()); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) {} return fig; } /** * Transforms the passed polyline into a Polyline shape. * * @param data The polyline to transform. * @return See above. */ private ROIFigure createPolylineFromPolylineFigure(PolylineData data) { List<Point2D.Double> points = data.getPoints(); List<Point2D.Double> points1 = data.getPoints1(); List<Point2D.Double> points2 = data.getPoints2(); List<Integer> mask = data.getMaskPoints(); MeasureBezierFigure fig = new MeasureBezierFigure(false, data.isReadOnly(), data.isClientObject(), data.canEdit(), data.canDelete(), data.canAnnotate()); fig.setVisible(data.isVisible()); for (int i = 0; i < points.size(); i++) fig.addNode(new Node(i, points.get(i), points.get(i), points.get(i))); addShapeSettings(fig, data.getShapeSettings()); String text = data.getText(); if (text == null || text.trim().length() == 0) text = ROIFigure.DEFAULT_TEXT; fig.setText(text); AffineTransform transform; try { transform = SVGTransform.toTransform(data.getTransform()); TRANSFORM.set(fig, transform); } catch (IOException e) { } return fig; } /** * Adds the settings to the figure. * * @param figure The figure to handle. * @param data The settings to set. */ private void addShapeSettings(ROIFigure figure, ShapeSettingsData data) { Double value = ShapeSettingsData.DEFAULT_STROKE_WIDTH; Length l; try { l = data.getStrokeWidth(UnitsLength.PIXEL); if (l != null) { value = l.getValue(); } } catch (Exception e) { if (e instanceof BigResult) { BigResult ex = (BigResult) e; if (ex.result != null) { value = ex.result.doubleValue(); } } } STROKE_WIDTH.set(figure, value); STROKE_COLOR.set(figure, data.getStroke()); FILL_COLOR.set(figure, data.getFill()); Font f = data.getFont(); FONT_FACE.set(figure, f); FONT_SIZE.set(figure, (double) f.getSize()); FONT_ITALIC.set(figure, data.isFontItalic()); FONT_BOLD.set(figure, data.isFontBold()); STROKE_CAP.set(figure, data.getLineCap()); TEXT_COLOR.set(figure, data.getStroke()); } /** Creates a new instance. */ InputServerStrategy() { roiList = new ArrayList<ROI>(); } /** * Converts the server ROIs and adds them to <code>ROIComponent</code>. * * @param rois The ROIs to convert. * @param component ROIComponent. * @param userID The identifier of the user. * @return See above. * @throws ROICreationException if ROI cannot be created. * @throws NoSuchROIException if there is an error creating line connection * figure. */ List<ROI> readROI(Collection rois, ROIComponent component, long userID) throws ROICreationException, NoSuchROIException { if (component == null) throw new IllegalArgumentException("No component."); this.component = component; Iterator i = rois.iterator(); Object o; ROIData roi; while (i.hasNext()) { o = i.next(); if (o instanceof ROIData) { roi = (ROIData) o; roiList.add(createROI(roi, userID)); } } return roiList; } }