/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as published by
the Free Software Foundation.
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, see <http://www.gnu.org/licenses/>.
*/
package org.cirqwizard.fx;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.scene.transform.Affine;
import org.cirqwizard.geom.Point;
import org.cirqwizard.layers.Board;
import org.cirqwizard.layers.Layer;
import org.cirqwizard.layers.Panel;
import org.cirqwizard.layers.PanelBoard;
public class PanelPane extends Region
{
public static final Color BACKGROUND_COLOR = Color.web("#ddfbdd");
public static final Color PANEL_CONTOUR = Color.BLACK;
public static final Color PIN_COLOR = Color.BLACK;
public static final Color TOP_TRACE_COLOR = Color.color(1, 0, 0, 0.8);
public static final Color BOTTOM_TRACE_COLOR = Color.color(0, 0, 1, 0.8);
public static final Color DRILL_POINT_COLOR = Color.BLACK;
public static final Color SELECTED_BOARD_BACKGROUND_COLOR = Color.web("#eeffee");
public static final Color CONTOUR_COLOR = Color.MAGENTA;
private static final int ZOOM_INCREMENT = 20;
private static final int MIN_ZOOM = 30;
private static final int PADDING = 5000;
private static final int CONTOUR_WIDTH = 100;
private static final int PIN_DIAMETER = 3000;
private org.cirqwizard.layers.Panel panel;
private int zoom;
private int width;
private int height;
private boolean rendered = false;
private SimpleObjectProperty<PanelBoard> selectedBoard = new SimpleObjectProperty<>();
private Point2D mouseClickPoint;
private Point initialBoardLocation;
private BoardDragListener boardDragListener;
private Canvas canvas;
public Panel getPanel()
{
return panel;
}
public void setPanel(Panel panel)
{
this.panel = panel;
selectedBoard.setValue(null);
}
public PanelPane()
{
canvas = new Canvas();
getChildren().add(canvas);
addEventFilter(MouseEvent.ANY, event ->
{
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED))
{
mouseClickPoint = new Point2D(event.getX(), event.getY());
selectBoard(getBoardForCoordinates(event.getX(), event.getY()));
if (selectedBoard.getValue() != null)
initialBoardLocation = new Point(selectedBoard.getValue().getX(), selectedBoard.getValue().getY());
}
else if (event.getEventType().equals(MouseEvent.MOUSE_DRAGGED))
{
Point2D delta = new Point2D(event.getX() - mouseClickPoint.getX(), -(event.getY() - mouseClickPoint.getY()));
if (selectedBoard.getValue() != null)
{
selectedBoard.getValue().setX((int)(initialBoardLocation.getX() + delta.getX() * zoom));
selectedBoard.getValue().setY((int)(initialBoardLocation.getY() + delta.getY() * zoom));
render();
}
}
else if (event.getEventType().equals(MouseEvent.MOUSE_RELEASED))
{
if (selectedBoard.getValue() != null)
boardDragListener.boardDragged();
}
});
}
public void setBoardDragListener(BoardDragListener boardDragListener)
{
this.boardDragListener = boardDragListener;
}
public void render()
{
if (panel == null || panel.getSize() == null)
return;
width = panel.getSize().getWidth() + PADDING * 2;
height = panel.getSize().getHeight() + PADDING * 2;
canvas.setWidth(width / zoom);
canvas.setHeight(height / zoom);
GraphicsContext g = canvas.getGraphicsContext2D();
g.setTransform(new Affine());
g.setFill(BACKGROUND_COLOR);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
double scale = 1.0 / zoom;
g.scale(scale, -scale);
g.translate(0, -canvas.getHeight() * zoom);
renderContour(g);
for (Point p : panel.getPinLocations())
renderPin(g, p.getX(), p.getY());
g.translate(PADDING, PADDING);
PanelBoard selectedBoardValue = selectedBoard.getValue();
if (selectedBoardValue != null)
{
g.setFill(SELECTED_BOARD_BACKGROUND_COLOR);
g.fillRect(selectedBoardValue.getX(), selectedBoardValue.getY(), selectedBoardValue.getBoard().getWidth(),
selectedBoardValue.getBoard().getHeight());
}
renderLayer(g, Board.LayerType.BOTTOM, BOTTOM_TRACE_COLOR);
renderLayer(g, Board.LayerType.TOP, TOP_TRACE_COLOR);
renderLayer(g, Board.LayerType.DRILLING, DRILL_POINT_COLOR);
renderLayer(g, Board.LayerType.MILLING, CONTOUR_COLOR);
rendered = true;
}
private void renderLayer(GraphicsContext g, Board.LayerType layerType, Color color)
{
panel.getBoards().stream().
forEach(board ->
{
g.translate(board.getX(), board.getY());
g.setStroke(color);
g.setFill(color);
Layer layer = board.getBoard().getLayer(layerType);
if (layer != null)
layer.getElements().stream().forEach(e -> e.render(g));
g.translate(-board.getX(), -board.getY());
});
}
private void renderContour(GraphicsContext g)
{
g.setStroke(PANEL_CONTOUR);
g.setLineWidth(CONTOUR_WIDTH);
g.strokeRect(PADDING, PADDING, panel.getSize().getWidth(), panel.getSize().getHeight());
}
private void renderPin(GraphicsContext g, int x, int y)
{
g.setFill(PIN_COLOR);
g.fillArc(PADDING + x - PIN_DIAMETER / 2, PADDING + y - PIN_DIAMETER / 2,
PIN_DIAMETER / 2, PIN_DIAMETER / 2, 0, 360, ArcType.ROUND);
}
public void zoomIn()
{
zoom -= ZOOM_INCREMENT;
zoom = Math.max(zoom, MIN_ZOOM);
render();
}
public void zoomOut()
{
zoom += ZOOM_INCREMENT;
render();
}
public void zoomToFit(double width, double height, boolean force)
{
if (rendered && !force)
return;
if (panel == null || panel.getSize() == null || width == 0 || height == 0)
return;
double xScale = (panel.getSize().getWidth() + PADDING * 2) / width;
double yScale = (panel.getSize().getHeight() + PADDING * 2) / height;
zoom = (int) Math.max(xScale, yScale);
render();
}
private PanelBoard getBoardForCoordinates(double x, double y)
{
return panel.getBoards().stream().filter(b -> getBoardRectangle(b).contains(x, y)).findFirst().orElse(null);
}
private Rectangle2D getBoardRectangle(PanelBoard board)
{
return new Rectangle2D((board.getX() + PADDING) / zoom,
(-board.getY() + - board.getBoard().getHeight() + height - PADDING) / zoom,
board.getBoard().getWidth() / zoom, board.getBoard().getHeight() / zoom);
}
public void selectBoard(PanelBoard board)
{
this.selectedBoard.setValue(board);
render();
}
public PanelBoard getSelectedBoard()
{
return selectedBoard.get();
}
public SimpleObjectProperty<PanelBoard> selectedBoardProperty()
{
return selectedBoard;
}
}