use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PathHierarchyPaintingHelper method paintPoints.
private static void paintPoints(ROI pathPoints, Graphics2D g2d, double radius, Color colorStroke, Stroke stroke, Color colorFill, double downsample) {
PointsROI pathPointsROI = pathPoints instanceof PointsROI ? (PointsROI) pathPoints : null;
if (pathPointsROI != null && PathPrefs.showPointHullsProperty().get()) {
ROI convexHull = pathPointsROI.getConvexHull();
if (convexHull != null) {
Color colorHull = colorFill != null ? colorFill : colorStroke;
colorHull = ColorToolsAwt.getColorWithOpacity(colorHull, 0.1);
if (colorHull != null)
paintShape(RoiTools.getShape(convexHull), g2d, null, null, colorHull);
// getConvexHull().draw(g, null, colorHull);
}
}
RectangularShape ellipse;
// double radius = pathPointsROI == null ? PointsROI.defaultPointRadiusProperty().get() : pathPointsROI.getPointRadius();
// Ensure that points are drawn with at least a radius of one, after any transforms have been applied
double scale = Math.max(1, downsample);
radius = (Math.max(1 / scale, radius));
// Get clip bounds
Rectangle2D bounds = g2d.getClipBounds();
if (bounds != null) {
bounds.setRect(bounds.getX() - radius, bounds.getY() - radius, bounds.getWidth() + radius * 2, bounds.getHeight() + radius * 2);
}
// Don't fill if we have a small radius, and use a rectangle instead of an ellipse (as this repaints much faster)
Graphics2D g = g2d;
if (radius / downsample < 0.5) {
if (colorStroke == null)
colorStroke = colorFill;
colorFill = null;
ellipse = new Rectangle2D.Double();
// Use opacity to avoid obscuring points completely
int rule = AlphaComposite.SRC_OVER;
float alpha = (float) (radius / downsample);
var composite = g.getComposite();
if (composite instanceof AlphaComposite) {
var temp = (AlphaComposite) composite;
rule = temp.getRule();
alpha = temp.getAlpha() * alpha;
}
// If we are close to completely transparent, do not paint
if (alpha < 0.01f)
return;
composite = AlphaComposite.getInstance(rule, alpha);
g = (Graphics2D) g2d.create();
g.setComposite(composite);
// ellipse = new Ellipse2D.Double();
} else
ellipse = new Ellipse2D.Double();
g.setStroke(stroke);
for (Point2 p : pathPoints.getAllPoints()) {
if (bounds != null && !bounds.contains(p.getX(), p.getY()))
continue;
ellipse.setFrame(p.getX() - radius, p.getY() - radius, radius * 2, radius * 2);
if (colorFill != null) {
g.setColor(colorFill);
g.fill(ellipse);
}
if (colorStroke != null) {
g.setColor(colorStroke);
g.draw(ellipse);
}
}
if (g != g2d)
g.dispose();
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PointIO method readPointsObjectFromString.
/**
* Helper method for readPointsObjectList(), will be removed in future releases.
* @param s
* @return
*/
@Deprecated
private static PathObject readPointsObjectFromString(String s) {
List<Point2> pointsList = new ArrayList<>();
Scanner scanner = new Scanner(s);
String name = scanner.nextLine().split("\t")[1].trim();
Integer color = Integer.parseInt(scanner.nextLine().split("\t")[1]);
// Skip the coordinate count line...
int count = Integer.parseInt(scanner.nextLine().split("\t")[1]);
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line.length() == 0)
break;
String[] splits = line.split("\t");
double x = Double.parseDouble(splits[0]);
double y = Double.parseDouble(splits[1]);
pointsList.add(new Point2(x, y));
}
scanner.close();
if (count != pointsList.size())
logger.warn("Warning: {} points expected, {} points found", count, pointsList.size());
ROI points = ROIs.createPointsROI(pointsList, ImagePlane.getDefaultPlane());
PathObject pathObject = PathObjects.createAnnotationObject(points);
if (name != null && name.length() > 0 && !"null".equals(name))
pathObject.setName(name);
pathObject.setColorRGB(color);
return pathObject;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PathObjectTools method containsROI.
/**
* Test whether one ROI is can completely contain a second ROI.
* Returns false if either ROI is null.
* <p>
* Note: This is not a perfect test, since it really only checks if the vertices of the child ROI fall within the parent - it is possible
* that connecting lines stray outside the parent, yet it still returns true. This behavior may change in later versions.
* <p>
* TODO: Consider improving 'containsROI' method accuracy.
*
* @param parentROI
* @param childROI
* @return
*/
@Deprecated
public static boolean containsROI(final ROI parentROI, final ROI childROI) {
// Check for nulls... just to be sure
if (parentROI == null || childROI == null || !parentROI.isArea() || childROI.isEmpty() || parentROI.isEmpty())
return false;
// Check points
if (childROI != null && childROI.isPoint()) {
for (Point2 p : childROI.getAllPoints()) {
if (!parentROI.contains(p.getX(), p.getY()))
return false;
}
return true;
}
// Check areas - child can't have a larger area
if (childROI.isArea()) {
if (childROI.getArea() > parentROI.getArea())
return false;
}
// Check bounds dimensions
if (childROI.getBoundsWidth() > parentROI.getBoundsWidth() || childROI.getBoundsHeight() > parentROI.getBoundsHeight())
return false;
// Check bounds
double px = parentROI.getBoundsX();
double py = parentROI.getBoundsY();
double px2 = px + parentROI.getBoundsWidth();
double py2 = py + parentROI.getBoundsHeight();
double cx = childROI.getBoundsX();
double cy = childROI.getBoundsY();
double cx2 = px + childROI.getBoundsWidth();
double cy2 = py + childROI.getBoundsHeight();
if (!(cx >= px && cx2 <= px2 && cy >= py && cy2 <= py2))
return false;
// Check shapes
for (Point2 p : childROI.getAllPoints()) {
if (!parentROI.contains(p.getX(), p.getY()))
return false;
}
if (parentROI.isArea() && childROI.isArea())
return parentROI.getGeometry().covers(childROI.getGeometry());
// }
return true;
// logger.info("Doing standard (AWT) test...");
// return PathROIToolsAwt.containsShape(parentArea, (PathShape)childROI);
// return false;
// // Check for lines
// if (childROI instanceof PathLineROI) {
// PathLineROI line = (PathLineROI)childROI;
// return parentROI.contains(line.getX1(), line.getY1()) && parentROI.contains(line.getX2(), line.getY2());
// }
//
// // If we have areas, check these
// if (parentROI instanceof PathArea && childROI instanceof PathArea) {
// double area = ((PathArea)parentROI).getArea();
// if (!Double.isNaN(area) && area < ((PathArea)childROI).getArea())
// return false;
// }
//
// // Check bounds
// // if (!parentROI.getBounds2D().contains(childROI.getBounds2D())
// Rectangle2D childBounds = childROI.getBounds2D();
// if (!parentROI.intersects(childBounds))
// return false;
//
// // If we have shapes, do a proper test
// if ((parentROI instanceof PathShape) && (childROI instanceof PathShape)) {
// PathShape parentShape = (PathShape)parentROI;
// if (parentShape.contains(childBounds))
// return true;
// PathShape childShape = (PathShape)childROI;
// Area areaDifference = parentShape.getShapeAsArea();
// areaDifference.subtract(childShape.getShapeAsArea());
// return areaDifference.isEmpty();
// }
// return true;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PathObjectTools method mergePointsForSelectedObjectClasses.
// private static void addPathObjectsRecursively(Collection<PathObject> pathObjectsInput, Collection<PathObject> pathObjects, Class<? extends PathObject> cls) {
// Collection<PathObject> buffer = null;
// for (PathObject childObject : pathObjectsInput) {
// if (cls == null || cls.isInstance(childObject)) {
// pathObjects.add(childObject);
// }
// if (childObject.hasChildren()) {
// if (buffer == null)
// buffer = new ArrayList<>();
// else
// buffer.clear();
// childObject.getChildObjects(buffer);
// addPathObjectsRecursively(buffer, pathObjects, cls);
// }
// }
// }
// /**
// * Split annotations containing multi-point ROIs into separate single-point ROIs.
// *
// * @param hierarchy the object hierarchy
// * @param selectedOnly if true, consider only annotations that are currently selected; if false, consider all point annotations in the hierarchy
// * @return true if changes are made to the hierarchy, false otherwise
// */
// public static boolean splitPoints(PathObjectHierarchy hierarchy, boolean selectedOnly) {
// if (hierarchy == null) {
// logger.debug("No hierarchy available, cannot split points!");
// return false;
// }
// return splitPoints(hierarchy, selectedOnly ? hierarchy.getSelectionModel().getSelectedObjects() : hierarchy.getAnnotationObjects());
// }
//
// /**
// * Split annotations containing multi-point ROIs into separate single-point ROIs.
// *
// * @param hierarchy the object hierarchy
// * @param pathObjects a collection of point annotations to split; non-points and non-annotations will be ignored
// * @return pathObjects if changes are made to the hierarchy, false otherwise
// */
// public static boolean splitPoints(PathObjectHierarchy hierarchy, Collection<PathObject> pathObjects) {
// var points = pathObjects.stream().filter(p -> p.isAnnotation() && p.getROI().isPoint() && p.getROI().getNumPoints() > 1).collect(Collectors.toList());
// if (points.isEmpty()) {
// logger.debug("No (multi)point ROIs available to split!");
// return false;
// }
// List<PathObject> newObjects = new ArrayList<>();
// for (PathObject pathObject : points) {
// ROI p = pathObject.getROI();
// ImagePlane plane = p.getImagePlane();
// PathClass pathClass = pathObject.getPathClass();
// for (Point2 p2 : p.getAllPoints()) {
// PathObject temp = PathObjects.createAnnotationObject(ROIs.createPointsROI(p2.getX(), p2.getY(), plane), pathClass);
// newObjects.add(temp);
// }
// }
// hierarchy.removeObjects(points, true);
// hierarchy.addPathObjects(newObjects);
// // Reset the selection
// hierarchy.getSelectionModel().clearSelection();
// return true;
// }
/**
* Merge point annotations sharing the same {@link PathClass} and {@link ImagePlane} as the selected annotations,
* creating multi-point annotations for all matching points and removing the (previously-separated) annotations.
*
* @param hierarchy object hierarchy to modify
* @return true if changes are made to the hierarchy, false otherwise
*/
public static boolean mergePointsForSelectedObjectClasses(PathObjectHierarchy hierarchy) {
var pathClasses = hierarchy.getSelectionModel().getSelectedObjects().stream().filter(p -> p.isAnnotation() && p.getROI().isPoint()).map(p -> p.getPathClass()).collect(Collectors.toSet());
boolean changes = false;
for (PathClass pathClass : pathClasses) changes = changes || mergePointsForClass(hierarchy, pathClass);
return changes;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class CountingPane method copyCoordinatesToClipboard.
public static void copyCoordinatesToClipboard(PathObject pathObject) {
// PathObject pathObject = viewer.getPathObjectHierarchy().getSelectionModel().getSelectedPathObject();
if (pathObject == null || !pathObject.hasROI() || !(pathObject.getROI() instanceof PointsROI)) {
Dialogs.showErrorMessage("Copy points to clipboard", "No points selected!");
return;
}
StringBuilder sb = new StringBuilder();
String name = pathObject.getDisplayedName();
PointsROI points = (PointsROI) pathObject.getROI();
for (Point2 p : points.getAllPoints()) sb.append(name).append("\t").append(p.getX()).append("\t").append(p.getY()).append("\n");
StringSelection stringSelection = new StringSelection(sb.toString());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
}
Aggregations