All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jhotdraw8.draw.handle.TransformHandleKit Maven / Gradle / Ivy

The newest version!
/*
 * @(#)TransformHandleKit.java
 * Copyright © 2023 The authors and contributors of JHotDraw. MIT License.
 */
package org.jhotdraw8.draw.handle;

import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.paint.Color;
import javafx.scene.shape.SVGPath;
import javafx.scene.shape.Shape;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import org.jhotdraw8.draw.DrawingView;
import org.jhotdraw8.draw.css.value.CssPoint2D;
import org.jhotdraw8.draw.css.value.CssRectangle2D;
import org.jhotdraw8.draw.figure.Figure;
import org.jhotdraw8.draw.figure.TransformableFigure;
import org.jhotdraw8.draw.locator.BoundsLocator;
import org.jhotdraw8.draw.locator.Locator;
import org.jhotdraw8.draw.model.DrawingModel;
import org.jhotdraw8.geom.FXTransforms;
import org.jhotdraw8.icollection.VectorList;
import org.jhotdraw8.icollection.immutable.ImmutableList;
import org.jspecify.annotations.Nullable;

import java.util.Collection;
import java.util.function.Function;

import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.jhotdraw8.draw.figure.TransformableFigure.TRANSFORMS;

/**
 * A set of utility methods to create handles which transform a Figure by using
 * its {@code transform} method, if the Figure is transformable.
 * 

* FIXME implement me * * @author Werner Randelshofer */ public class TransformHandleKit { protected static final SVGPath NORTH_SHAPE = new SVGPath(); protected static final SVGPath EAST_SHAPE = new SVGPath(); protected static final SVGPath WEST_SHAPE = new SVGPath(); protected static final SVGPath SOUTH_SHAPE = new SVGPath();//new Rectangle(9, 5); protected static final SVGPath NORTH_EAST_SHAPE = new SVGPath(); protected static final SVGPath NORTH_WEST_SHAPE = new SVGPath(); protected static final SVGPath SOUTH_EAST_SHAPE = new SVGPath(); protected static final SVGPath SOUTH_WEST_SHAPE = new SVGPath(); static { final String circle = "M 9,4.5 A 4.5,4.5 0 1 0 0,4.5 A 4.5,4.5 0 1 0 9,4.5 Z "; NORTH_EAST_SHAPE.setContent(circle + "M 4.5,9 4.5,4.5 0,4.5 "); NORTH_WEST_SHAPE.setContent(circle + "M 9,4.5 4.5,4.5 4.5,9"); SOUTH_EAST_SHAPE.setContent(circle + "M 0,4.5 4.5,4.5 4.5,0"); SOUTH_WEST_SHAPE.setContent(circle + "M 4.5,0 4.5,4.5 9,4.5 "); SOUTH_SHAPE.setContent(circle + "M 0,4.5 9,4.5"); NORTH_SHAPE.setContent(circle + "M 0,4.5 9,4.5"); EAST_SHAPE.setContent(circle + "M 4.5,0 4.5,9"); WEST_SHAPE.setContent(circle + "M 4.5,0 4.5,9"); } private static final Background REGION_BACKGROUND = new Background(new BackgroundFill(Color.WHITE, null, null)); private static final Function REGION_BORDER = color -> new Border( new BorderStroke(color, BorderStrokeStyle.SOLID, null, null) ); /** * Prevent instance creation. */ private TransformHandleKit() { } /** * Creates handles for each corner of a figure and adds them to the provided * collection. * * @param f the figure which will own the handles * @param handles the list to which the handles should be added */ public static void addCornerTransformHandles(TransformableFigure f, Collection handles) { handles.add(southEast(f)); handles.add(southWest(f)); handles.add(northEast(f)); handles.add(northWest(f)); } /** * Fills the given collection with handles at each the north, south, east, * and west of the figure. * * @param f the figure which will own the handles * @param handles the list to which the handles should be added */ public static void addEdgeTransformHandles(TransformableFigure f, Collection handles) { handles.add(south(f)); handles.add(north(f)); handles.add(east(f)); handles.add(west(f)); } /** * Fills the given collection with handles at each the north, south, east, * and west of the figure. * * @param f the figure which will own the handles * @param handles the list to which the handles should be added */ public static void addTransformHandles(TransformableFigure f, Collection handles) { addCornerTransformHandles(f, handles); addEdgeTransformHandles(f, handles); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle south(TransformableFigure owner) { return new SouthHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle southEast(TransformableFigure owner) { return new SouthEastHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle southWest(TransformableFigure owner) { return new SouthWestHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle north(TransformableFigure owner) { return new NorthHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle northEast(TransformableFigure owner) { return new NorthEastHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle northWest(TransformableFigure owner) { return new NorthWestHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle east(TransformableFigure owner) { return new EastHandle(owner); } /** * Creates a handle for the specified figure. * * @param owner the figure which will own the handle * @return the handle */ public static Handle west(TransformableFigure owner) { return new WestHandle(owner); } private abstract static class AbstractTransformHandle extends AbstractResizeTransformHandle { @Nullable ImmutableList startTransforms; public AbstractTransformHandle(Figure owner, Locator locator, Shape shape, Background bg, Function border) { super(owner, locator, shape, bg, border); } @Override public void onMousePressed(MouseEvent event, DrawingView view) { super.onMousePressed(event, view); //To change body of generated methods, choose Tools | Templates. startTransforms = owner.get(TRANSFORMS); } protected void transform(DrawingModel model, Figure o, double x, double y, double width, double height) { if (width == 0 || height == 0) { return; } TransformableFigure owner = (TransformableFigure) o; Bounds oldBounds = startBounds.getConvertedBoundsValue(); ImmutableList oldTransforms = startTransforms == null ? VectorList.of() : startTransforms; double sx = width / oldBounds.getWidth(); double sy = height / oldBounds.getHeight(); double tx = x - oldBounds.getMinX(); double ty = y - oldBounds.getMinY(); Transform transform = new Translate(tx, ty); if (!Double.isNaN(sx) && !Double.isNaN(sy) && !Double.isInfinite(sx) && !Double.isInfinite(sy) && (sx != 1d || sy != 1d)) { transform = FXTransforms.concat(transform, new Scale(sx, sy, oldBounds.getMinX(), oldBounds.getMinY())); } switch (oldTransforms.size()) { case 0: model.set(owner, TRANSFORMS, VectorList.of(transform)); break; default: int last = oldTransforms.size() - 1; model.set(owner, TRANSFORMS, oldTransforms.set(last, FXTransforms.concat(oldTransforms.get(last), transform))); break; } } @Override protected void resize(CssPoint2D newPoint, Figure owner, CssRectangle2D bounds, DrawingModel model, boolean keepAspect) { // FIXME remove this method resize(newPoint.getConvertedValue(), owner, bounds.getConvertedBoundsValue(), model, keepAspect); } } private static class NorthEastHandle extends AbstractTransformHandle { NorthEastHandle(TransformableFigure owner) { super(owner, BoundsLocator.NORTH_EAST, NORTH_EAST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newX = max(bounds.getMinX(), newPoint.getX()); double newY = min(bounds.getMaxY(), newPoint.getY()); double newWidth = newX - bounds.getMinX(); double newHeight = bounds.getMaxY() - newY; if (keepAspect) { double newRatio = newHeight / newWidth; if (newRatio > preferredAspectRatio) { newHeight = newWidth * preferredAspectRatio; } else { newWidth = newHeight / preferredAspectRatio; } } transform(model, owner, bounds.getMinX(), bounds.getMaxY() - newHeight, newWidth, newHeight); } } private static class EastHandle extends AbstractTransformHandle { EastHandle(TransformableFigure owner) { super(owner, BoundsLocator.EAST, EAST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newWidth = max(newPoint.getX(), bounds.getMinX()) - bounds.getMinX(); double newHeight = bounds.getMaxY() - bounds.getMinY(); if (keepAspect) { double newRatio = newHeight / newWidth; newHeight = newWidth * preferredAspectRatio; } transform(model, owner, bounds.getMinX(), (bounds.getMinY() + bounds.getMaxY() - newHeight) * 0.5, newWidth, newHeight); } } private static class NorthHandle extends AbstractTransformHandle { NorthHandle(TransformableFigure owner) { super(owner, BoundsLocator.NORTH, NORTH_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newY = min(bounds.getMaxY(), newPoint.getY()); double newWidth = bounds.getMaxX() - bounds.getMinX(); double newHeight = bounds.getMaxY() - newY; if (keepAspect) { double newRatio = newHeight / newWidth; newWidth = newHeight / preferredAspectRatio; } transform(model, owner, (bounds.getMinX() + bounds.getMaxX() - newWidth) * 0.5, newY, newWidth, newHeight); } } private static class NorthWestHandle extends AbstractTransformHandle { NorthWestHandle(TransformableFigure owner) { super(owner, BoundsLocator.NORTH_WEST, NORTH_WEST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newX = min(bounds.getMaxX(), newPoint.getX()); double newY = min(bounds.getMaxY(), newPoint.getY()); double newWidth = bounds.getMaxX() - newX; double newHeight = bounds.getMaxY() - newY; if (keepAspect) { double newRatio = newHeight / newWidth; if (newRatio > preferredAspectRatio) { newHeight = newWidth * preferredAspectRatio; } else { newWidth = newHeight / preferredAspectRatio; } } transform(model, owner, bounds.getMaxX() - newWidth, bounds.getMaxY() - newHeight, newWidth, newHeight); } } private static class SouthEastHandle extends AbstractTransformHandle { SouthEastHandle(TransformableFigure owner) { super(owner, BoundsLocator.SOUTH_EAST, SOUTH_EAST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.SE_RESIZE; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newX = max(bounds.getMinX(), newPoint.getX()); double newY = max(bounds.getMinY(), newPoint.getY()); double newWidth = newX - bounds.getMinX(); double newHeight = newY - bounds.getMinY(); if (keepAspect) { double newRatio = newHeight / newWidth; if (newRatio > preferredAspectRatio) { newHeight = newWidth * preferredAspectRatio; } else { newWidth = newHeight / preferredAspectRatio; } } transform(model, owner, bounds.getMinX(), bounds.getMinY(), newWidth, newHeight); } } private static class SouthHandle extends AbstractTransformHandle { SouthHandle(TransformableFigure owner) { super(owner, BoundsLocator.SOUTH, SOUTH_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.S_RESIZE; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newY = max(bounds.getMinY(), newPoint.getY()); double newWidth = bounds.getMaxX() - bounds.getMinX(); double newHeight = newY - bounds.getMinY(); if (keepAspect) { newWidth = newHeight / preferredAspectRatio; } transform(model, owner, (bounds.getMinX() + bounds.getMaxX() - newWidth) * 0.5, bounds.getMinY(), newWidth, newHeight); } } private static class SouthWestHandle extends AbstractTransformHandle { SouthWestHandle(TransformableFigure owner) { super(owner, BoundsLocator.SOUTH_WEST, SOUTH_WEST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newX = min(bounds.getMaxX(), newPoint.getX()); double newY = max(bounds.getMinY(), newPoint.getY()); double newWidth = bounds.getMaxX() - min(bounds.getMaxX(), newX); double newHeight = newY - bounds.getMinY(); if (keepAspect) { double newRatio = newHeight / newWidth; if (newRatio > preferredAspectRatio) { newHeight = newWidth * preferredAspectRatio; } else { newWidth = newHeight / preferredAspectRatio; } } transform(model, owner, bounds.getMaxX() - newWidth, bounds.getMinY(), newWidth, newHeight); } } private static class WestHandle extends AbstractTransformHandle { WestHandle(TransformableFigure owner) { super(owner, BoundsLocator.WEST, WEST_SHAPE, REGION_BACKGROUND, REGION_BORDER); } @Override public Cursor getCursor() { return Cursor.CROSSHAIR; } @Override protected void resize(Point2D newPoint, Figure owner, Bounds bounds, DrawingModel model, boolean keepAspect) { double newX = min(bounds.getMaxX(), newPoint.getX()); double newWidth = bounds.getMaxX() - newX; double newHeight = bounds.getHeight(); if (keepAspect) { double newRatio = newHeight / newWidth; newHeight = newWidth * preferredAspectRatio; } transform(model, owner, newX, (bounds.getMinY() + bounds.getMaxY() - newHeight) * 0.5, newWidth, newHeight); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy