Search in sources :

Example 56 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class BrushTool method createShape.

/**
 * Create a new Geometry using the specified tool, assuming a user click/drag at the provided x & y coordinates.
 * @param e
 *
 * @param x
 * @param y
 * @param useTiles If true, request generating a shape from existing tile objects.
 * @param addToShape If provided, it can be assumed that any new shape ought to be added to this one.
 *                   The purpose is that this method may (optionally) use the shape to refine the one it will generate,
 *                   e.g. to avoid having isolated or jagged boundaries.
 * @return
 */
protected Geometry createShape(MouseEvent e, double x, double y, boolean useTiles, Geometry addToShape) {
    // See if we're on top of a tile
    if (useTiles) {
        List<PathObject> listSelectable = getSelectableObjectList(x, y);
        for (PathObject temp : listSelectable) {
            // if ((temp instanceof PathDetectionObject) && temp.getROI() instanceof PathArea)
            if (temp instanceof PathTileObject && temp.hasROI() && temp.getROI().isArea() && !(temp.getROI() instanceof RectangleROI)) {
                creatingTiledROI = true;
                return temp.getROI().getGeometry();
            }
        }
        // If we're currently creating a tiled, ROI, but now not clicked on a tile, just return
        if (creatingTiledROI)
            return null;
    }
    // Compute a diameter scaled according to the pressure being applied
    double diameter = Math.max(1, getBrushDiameter());
    Geometry geometry;
    if (lastPoint == null) {
        var shapeFactory = new GeometricShapeFactory(getGeometryFactory());
        shapeFactory.setCentre(new Coordinate(x, y));
        shapeFactory.setSize(diameter);
        // shapeFactory.setCentre(new Coordinate(x-diameter/2, y-diameter/2));
        geometry = shapeFactory.createEllipse();
    } else {
        if (lastPoint.distanceSq(x, y) == 0)
            return null;
        var factory = getGeometryFactory();
        geometry = factory.createLineString(new Coordinate[] { new Coordinate(lastPoint.getX(), lastPoint.getY()), new Coordinate(x, y) }).buffer(diameter / 2.0);
    }
    return geometry;
}
Also used : Geometry(org.locationtech.jts.geom.Geometry) PathTileObject(qupath.lib.objects.PathTileObject) PathObject(qupath.lib.objects.PathObject) RectangleROI(qupath.lib.roi.RectangleROI) GeometricShapeFactory(org.locationtech.jts.util.GeometricShapeFactory) Coordinate(org.locationtech.jts.geom.Coordinate)

Example 57 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class BrushTool method mouseDragged.

@Override
public void mouseDragged(MouseEvent e) {
    // Note: if the 'freehand' part of the polygon creation isn't desired, just comment out this whole method
    super.mouseDragged(e);
    ensureCursorType(getRequestedCursor());
    if (!e.isPrimaryButtonDown()) {
        return;
    }
    // Can only modify annotations
    var viewer = getViewer();
    PathObject pathObject = viewer.getSelectedObject();
    if (pathObject == null || !pathObject.isAnnotation() || !pathObject.isEditable())
        return;
    if (pathObject != currentObject) {
        logger.warn("Selected object has changed from {} to {}", currentObject, pathObject);
        return;
    }
    ROI currentROI = pathObject.getROI();
    if (!(currentROI instanceof ROI))
        return;
    ROI shapeROI = currentROI;
    PathObject pathObjectUpdated = getUpdatedObject(e, shapeROI, pathObject, -1);
    if (pathObject != pathObjectUpdated) {
        viewer.setSelectedObject(pathObjectUpdated, PathPrefs.selectionModeProperty().get());
    } else {
        viewer.repaint();
    }
}
Also used : PathObject(qupath.lib.objects.PathObject) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI)

Example 58 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class BrushTool method getUpdatedObject.

private PathObject getUpdatedObject(MouseEvent e, ROI shapeROI, PathObject currentObject, double flatness) {
    Point2D p = mouseLocationToImage(e, true, requestPixelSnapping());
    var viewer = getViewer();
    ImagePlane plane = shapeROI == null ? ImagePlane.getPlane(viewer.getZPosition(), viewer.getTPosition()) : shapeROI.getImagePlane();
    Geometry shapeNew;
    boolean subtractMode = isSubtractMode(e);
    Geometry shapeCurrent = shapeROI == null ? null : shapeROI.getGeometry();
    Geometry shapeDrawn = createShape(e, p.getX(), p.getY(), PathPrefs.useTileBrushProperty().get() && !e.isShiftDown(), subtractMode ? null : shapeCurrent);
    if (shapeDrawn == null)
        return currentObject;
    // Do our pixel snapping now, with the simpler geometry (rather than latter when things are already complex)
    if (requestPixelSnapping())
        shapeDrawn = GeometryTools.roundCoordinates(shapeDrawn);
    lastPoint = p;
    try {
        if (shapeROI != null) {
            // Check to see if any changes are required at all
            if (shapeDrawn == null || (subtractMode && !shapeCurrent.intersects(shapeDrawn)) || (!subtractMode && shapeCurrent.covers(shapeDrawn)))
                return currentObject;
            // TODO: Consider whether a preference should be used rather than the shift key?
            // Anyhow, this will switch to 'dodge' mode, and avoid overlapping existing annotations
            boolean avoidOtherAnnotations = requestParentClipping(e);
            if (subtractMode) {
                // If subtracting... then just subtract
                shapeNew = shapeROI.getGeometry().difference(shapeDrawn);
            } else if (avoidOtherAnnotations) {
                shapeNew = shapeCurrent.union(shapeDrawn);
                shapeNew = refineGeometryByParent(shapeNew);
            } else {
                // Just add, regardless of whether there are other annotations below or not
                var temp = shapeROI.getGeometry();
                try {
                    shapeNew = temp.union(shapeDrawn);
                } catch (Exception e2) {
                    shapeNew = shapeROI.getGeometry();
                }
            }
        } else {
            shapeNew = shapeDrawn;
        }
        // If we aren't snapping, at least remove some vertices
        if (!requestPixelSnapping()) {
            try {
                shapeNew = VWSimplifier.simplify(shapeNew, 0.1);
            } catch (Exception e2) {
                logger.error("Error simplifying ROI: " + e2.getLocalizedMessage(), e2);
            }
        }
        // Make sure we fit inside the image
        shapeNew = GeometryTools.constrainToBounds(shapeNew, 0, 0, viewer.getServerWidth(), viewer.getServerHeight());
        // Sometimes we can end up with a GeometryCollection containing lines/non-areas... if so, remove these
        if (shapeNew instanceof GeometryCollection) {
            shapeNew = GeometryTools.ensurePolygonal(shapeNew);
        }
        ROI roiNew = GeometryTools.geometryToROI(shapeNew, plane);
        if (currentObject instanceof PathAnnotationObject) {
            ((PathAnnotationObject) currentObject).setROI(roiNew);
            return currentObject;
        }
        // shapeNew = new PathAreaROI(new Area(shapeNew.getShape()));
        PathObject pathObjectNew = PathObjects.createAnnotationObject(roiNew, PathPrefs.autoSetAnnotationClassProperty().get());
        if (currentObject != null) {
            pathObjectNew.setName(currentObject.getName());
            pathObjectNew.setColorRGB(currentObject.getColorRGB());
            pathObjectNew.setPathClass(currentObject.getPathClass());
        }
        return pathObjectNew;
    } catch (Exception ex) {
        logger.error("Error updating ROI", ex);
        return currentObject;
    }
}
Also used : Geometry(org.locationtech.jts.geom.Geometry) GeometryCollection(org.locationtech.jts.geom.GeometryCollection) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Point2D(java.awt.geom.Point2D) ImagePlane(qupath.lib.regions.ImagePlane) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI)

Example 59 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class AbstractPathDraggingROITool method mouseReleased.

@Override
public void mouseReleased(MouseEvent e) {
    if (e.getButton() != MouseButton.PRIMARY) {
        return;
    }
    var viewer = getViewer();
    PathObject selectedObject = viewer.getSelectedObject();
    if (selectedObject == null)
        return;
    RoiEditor editor = viewer.getROIEditor();
    ROI currentROI = selectedObject.getROI();
    if (currentROI != null && editor.getROI() == currentROI && editor.hasActiveHandle()) {
        editor.setROI(null);
        // Remove empty ROIs
        if (currentROI.isEmpty()) {
            if (selectedObject.getParent() != null)
                viewer.getHierarchy().removeObject(selectedObject, true);
            viewer.setSelectedObject(null);
        } else {
            commitObjectToHierarchy(e, selectedObject);
        }
    // editor.ensureHandlesUpdated();
    // editor.resetActiveHandle();
    // if (PathPrefs.getReturnToMoveMode())
    // modes.setMode(Modes.MOVE);
    }
}
Also used : PathObject(qupath.lib.objects.PathObject) RoiEditor(qupath.lib.roi.RoiEditor) ROI(qupath.lib.roi.interfaces.ROI)

Example 60 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class AbstractPathTool method getSelectableObjectList.

/**
 * Get a list of all selectable objects overlapping the specified x, y coordinates, ordered by depth in the hierarchy
 * @param x
 * @param y
 * @return
 */
List<PathObject> getSelectableObjectList(double x, double y) {
    var viewer = getViewer();
    PathObjectHierarchy hierarchy = viewer.getHierarchy();
    if (hierarchy == null)
        return Collections.emptyList();
    Collection<PathObject> pathObjects = PathObjectTools.getObjectsForLocation(hierarchy, x, y, viewer.getZPosition(), viewer.getTPosition(), viewer.getMaxROIHandleSize());
    if (pathObjects.isEmpty())
        return Collections.emptyList();
    List<PathObject> pathObjectList = new ArrayList<>(pathObjects);
    if (pathObjectList.size() == 1)
        return pathObjectList;
    Collections.sort(pathObjectList, PathObjectHierarchy.HIERARCHY_COMPARATOR);
    return pathObjectList;
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList)

Aggregations

PathObject (qupath.lib.objects.PathObject)182 ArrayList (java.util.ArrayList)84 ROI (qupath.lib.roi.interfaces.ROI)74 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)61 List (java.util.List)48 BufferedImage (java.awt.image.BufferedImage)37 IOException (java.io.IOException)37 PathClass (qupath.lib.objects.classes.PathClass)37 Collectors (java.util.stream.Collectors)35 PathAnnotationObject (qupath.lib.objects.PathAnnotationObject)34 Map (java.util.Map)33 Logger (org.slf4j.Logger)33 LoggerFactory (org.slf4j.LoggerFactory)33 ImageData (qupath.lib.images.ImageData)31 TMACoreObject (qupath.lib.objects.TMACoreObject)31 Collection (java.util.Collection)29 Collections (java.util.Collections)29 HashMap (java.util.HashMap)28 PathObjectTools (qupath.lib.objects.PathObjectTools)26 Arrays (java.util.Arrays)25