+ private static Shape transformAndExpand(Rectangle2D r, AffineTransform t, double padding) {
+
+ if ((t.getShearX() == 0 && t.getShearY() == 0) || t.getScaleX() == 0 && t.getScaleY() == 0) {
+ // Simple case for axis-aligned selection
+ Rectangle2D result = t.createTransformedShape(r).getBounds2D();
+ result.setRect(result.getMinX() - padding, result.getMinY() - padding, result.getWidth() + 2 * padding, result.getHeight() + 2 * padding);
+ return result;
+ } else {
+ // General case
+ Point2D.Double corners[] = new Point2D.Double[4];
+ corners[0] = new Point2D.Double(r.getMinX(), r.getMinY());
+ corners[1] = new Point2D.Double(r.getMinX(), r.getMaxY());
+ corners[2] = new Point2D.Double(r.getMaxX(), r.getMaxY());
+ corners[3] = new Point2D.Double(r.getMaxX(), r.getMinY());
+ t.transform(corners, 0, corners, 0, 4);
+
+ double det = t.getDeterminant();
+ double step = Math.PI / 2;
+ double diagonalPadding = padding * Math.sqrt(2);
+
+ Polygon2D poly = new Polygon2D();
+ for (int edge = 0; edge < 4; edge++) {
+ int p0 = edge % 4;
+ int p1 = (edge + 1) % 4;
+ int p2 = (edge + 2) % 4;
+
+ double a1 = Math.atan2(corners[p1].y - corners[p0].y, corners[p1].x - corners[p0].x);
+ double a2 = Math.atan2(corners[p2].y - corners[p1].y, corners[p2].x - corners[p1].x);
+ if (det > 0) {
+ if (a1 < a2) {
+ a1 += Math.PI * 2;
+ }
+ int dir1 = (int)Math.ceil(a1 / step);
+ int dir2 = (int)Math.floor(a2 / step);
+ for (int dir = dir1; dir > dir2; dir--) {
+ poly.addPoint(new Point2D.Double(
+ corners[p1].x + Math.cos((dir + 0.5) * step) * diagonalPadding,
+ corners[p1].y + Math.sin((dir + 0.5) * step) * diagonalPadding));
+ }
+ } else {
+ if (a1 > a2) {
+ a2 += Math.PI * 2;
+ }
+ int dir1 = (int)Math.floor(a1 / step);
+ int dir2 = (int)Math.ceil(a2 / step);
+ for (int dir = dir1; dir < dir2; dir++) {
+ poly.addPoint(new Point2D.Double(
+ corners[p1].x + Math.cos((dir - 0.5) * step) * diagonalPadding,
+ corners[p1].y + Math.sin((dir - 0.5) * step) * diagonalPadding));
+ }
+ }
+ }
+ return poly;
+ }