Skip to content

Commit

Permalink
Made it possible to design in negative space (#2559)
Browse files Browse the repository at this point in the history
  • Loading branch information
breiler committed Jun 27, 2024
1 parent f71f149 commit 0429cf5
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.ugs.nbp.designer.gui.Drawing;
import com.willwinder.ugs.nbp.designer.logic.Controller;

import java.awt.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.Optional;
Expand All @@ -36,69 +43,112 @@ This file is part of Universal Gcode Sender (UGS).
*/
public class GridControl extends AbstractEntity implements Control {

public static final int MINIMUM_SIZE = 300;
public static final int LARGE_GRID_SIZE = 50;
public static final int SMALL_GRID_SIZE = 10;
public static final int LARGE_GRID_SIZE = 100;
public static final int SMALL_GRID_SIZE = 20;
private final Controller controller;

public GridControl(Controller controller) {
this.controller = controller;
}

@Override
public void render(Graphics2D graphics, Drawing drawing) {
double gridSize = LARGE_GRID_SIZE;

Rectangle2D bounds = controller.getDrawing().getRootEntity().getBounds();
int calculatedMinimumWidth = (int) Math.round(Math.floor(bounds.getMaxX() / gridSize) * gridSize + (gridSize * 2));
int calculatedMinimumHeight = (int) Math.round(Math.floor(bounds.getMaxY() / gridSize) * gridSize + (gridSize * 2));

int width = Math.max(calculatedMinimumWidth, MINIMUM_SIZE);
int height = Math.max(calculatedMinimumHeight, MINIMUM_SIZE);

graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
private static void drawZeroLines(Graphics2D graphics, Drawing drawing, int startPosX, int startPosY, int endPosX, int endPosY) {
// Draw zero lines
graphics.setStroke(new BasicStroke((float) (0.5 / drawing.getScale())));
graphics.setColor(Color.LIGHT_GRAY);
graphics.drawLine(0, startPosY, 0, endPosY);
graphics.drawLine(startPosX, 0, endPosX, 0);
}

graphics.setStroke(new BasicStroke(Double.valueOf(0.1 / drawing.getScale()).floatValue()));
private static void drawSmallGrid(Graphics2D graphics, Drawing drawing, int startPosX, int startPosY, int endPosX, int endPosY, int gridSize) {
graphics.setStroke(new BasicStroke((float) (0.2 / drawing.getScale())));
graphics.setColor(Color.LIGHT_GRAY);
for (int x = 0; x <= width; x += SMALL_GRID_SIZE) {
graphics.drawLine(x, 0, x, height);
for (int x = startPosX; x <= endPosX; x += gridSize) {
graphics.drawLine(x, startPosY, x, endPosY);
}

for (int y = 0; y <= height; y += SMALL_GRID_SIZE) {
graphics.drawLine(0, y, width, y);
for (int y = startPosY; y <= endPosY; y += gridSize) {
graphics.drawLine(startPosX, y, endPosX, y);
}
}



private static void drawLargeGridAndText(Graphics2D graphics, Drawing drawing, int startPosX, int startPosY, int endPosX, int endPosY, int gridSize) {
Rectangle2D clipBounds = graphics.getClipBounds();
AffineTransform affineTransform = AffineTransform.getScaleInstance(1 / drawing.getScale(), -1 / drawing.getScale());
affineTransform.rotate(Math.PI/2);
affineTransform.rotate(Math.PI / 2);
Font font = new Font(null, Font.PLAIN, 10).deriveFont(affineTransform);
graphics.setFont(font);
FontMetrics fontMetrics = graphics.getFontMetrics();

graphics.setStroke(new BasicStroke(Double.valueOf(0.2 / drawing.getScale()).floatValue()));
for (int x = 0; x <= width; x += LARGE_GRID_SIZE) {
graphics.drawLine(x, 0, x, height);
graphics.setStroke(new BasicStroke((float) (0.3 / drawing.getScale())));
graphics.setColor(Color.LIGHT_GRAY);

int fontOffset = (int) Math.round(6 / drawing.getScale());

for (int x = startPosX; x <= endPosX; x += gridSize) {
if (x < clipBounds.getMinX() || x > clipBounds.getMaxX()) {
continue;
}
graphics.drawLine(x, startPosY, x, endPosY);

String text = x + " mm";
int y = -fontMetrics.stringWidth(text);
graphics.drawString(text, x - (int) Math.round(3 / drawing.getScale()), y - (int) Math.round(8 / drawing.getScale()));
int y = -fontMetrics.stringWidth(text) - fontOffset;
graphics.drawString(text, x + fontOffset, y);
}


affineTransform = AffineTransform.getScaleInstance(1 / drawing.getScale(), -1 / drawing.getScale());
font = new Font(null, Font.PLAIN, 10).deriveFont(affineTransform);
graphics.setFont(font);
fontMetrics = graphics.getFontMetrics();

for (int y = 0; y <= height; y += LARGE_GRID_SIZE) {
graphics.drawLine(0, y, width, y);
for (int y = startPosY; y <= endPosY; y += gridSize) {
if (y < clipBounds.getMinY() || y > clipBounds.getMaxY()) {
continue;
}
graphics.drawLine(startPosX, y, endPosX, y);

String text = y + " mm";
int x = -fontMetrics.stringWidth(text);
graphics.drawString(text, x - (int) Math.round(8 / drawing.getScale()), y - (int) Math.round(3 / drawing.getScale()));
int x = -fontMetrics.stringWidth(text);
graphics.drawString(text, x - fontOffset, y + fontOffset);
}
}

@Override
public void render(Graphics2D graphics, Drawing drawing) {
double gridSize = LARGE_GRID_SIZE;


Rectangle2D bounds = controller.getDrawing().getRootEntity().getBounds();
double gridMargin = (gridSize * 100);


int startPosX = (int) Math.min(-gridMargin, (Math.floor(bounds.getMinX() / gridSize) * gridSize + gridMargin));
int startPosY = (int) Math.min(-gridMargin, (Math.floor(bounds.getMinY() / gridSize) * gridSize + gridMargin));
int endPosX = (int) Math.max(gridMargin, (Math.floor(bounds.getMaxX() / gridSize) * gridSize + gridMargin));
int endPosY = (int) Math.max(gridMargin, (Math.floor(bounds.getMaxY() / gridSize) * gridSize + gridMargin));

int width = endPosX - startPosX;
int height = endPosY - startPosY;
graphics.setColor(Color.WHITE);
graphics.fillRect(startPosX, startPosY, width, height);

int smallGridSize;
int largeGridSize;
if (drawing.getScale() < 1.3) {
smallGridSize = SMALL_GRID_SIZE;
largeGridSize = LARGE_GRID_SIZE;
} else if (drawing.getScale() < 4) {
smallGridSize = SMALL_GRID_SIZE / 2;
largeGridSize = LARGE_GRID_SIZE / 2;
} else if (drawing.getScale() < 12) {
smallGridSize = SMALL_GRID_SIZE / 4;
largeGridSize = LARGE_GRID_SIZE / 4;
} else {
smallGridSize = SMALL_GRID_SIZE / 10;
largeGridSize = LARGE_GRID_SIZE / 10;
}
drawSmallGrid(graphics, drawing, startPosX, startPosY, endPosX, endPosY, smallGridSize);
drawLargeGridAndText(graphics, drawing, startPosX, startPosY, endPosX, endPosY, largeGridSize);
drawZeroLines(graphics, drawing, startPosX, startPosY, endPosX, endPosY);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ This file is part of Universal Gcode Sender (UGS).
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
Expand All @@ -72,6 +73,9 @@ public class Drawing extends JPanel {
private final transient Set<DrawingListener> listeners = Sets.newConcurrentHashSet();
private final transient Throttler refreshThrottler;
private double scale;
private AffineTransform transform;
private long lastUpdate;
private final transient Rectangle2D currentBounds = new Rectangle(0, 0, 8, 8);

public Drawing(Controller controller) {
refreshThrottler = new Throttler(this::refresh, 2000);
Expand Down Expand Up @@ -220,8 +224,10 @@ public void setScale(double scale) {

@Override
public Dimension getMinimumSize() {
Rectangle2D bounds = globalRoot.getBounds();
return new Dimension((int) (bounds.getMaxX() * scale) + (MARGIN * 2), (int) (bounds.getMaxY() * scale) + (MARGIN * 2));
int margin = (int) (MARGIN * 2 * scale);
int width = (int) (currentBounds.getWidth() * scale) + margin;
int height = (int) (currentBounds.getHeight() * scale) + margin;
return new Dimension(width, height);
}

@Override
Expand All @@ -242,10 +248,17 @@ public void addListener(DrawingListener listener) {
}

public AffineTransform getTransform() {
AffineTransform transform = AffineTransform.getScaleInstance(1, -1);
transform.translate(0, -getHeight());
transform.translate(MARGIN, MARGIN);
transform.scale(scale, scale);
// Don't update this every time or else it will be hard to move entites outside the canvas
if (System.currentTimeMillis() > lastUpdate + 50) {

transform = AffineTransform.getScaleInstance(1, -1);
transform.translate(0, -getHeight());
transform.scale(scale, scale);
transform.translate(MARGIN / 4d, MARGIN / 4d);
transform.translate(-getBounds().getMinX(), -getBounds().getMinY());
lastUpdate = System.currentTimeMillis();
}

return transform;
}

Expand All @@ -263,4 +276,16 @@ public List<Control> getControls() {
public void clear() {
entitiesRoot.removeAll();
}


@Override
public Rectangle getBounds() {
Rectangle2D bounds = globalRoot.getBounds();
double minX = Math.min(currentBounds.getMinX(), bounds.getMinX());
double minY = Math.min(currentBounds.getMinY(), bounds.getMinY());
double maxX = Math.max(currentBounds.getMaxX(), bounds.getMaxX());
double maxY = Math.max(currentBounds.getMaxY(), bounds.getMaxY());
currentBounds.setRect(minX, minY, maxX - minX, maxY - minY);
return currentBounds.getBounds();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.ugs.nbp.lib.lookup.CentralLookup;
import com.willwinder.universalgcodesender.model.BackendAPI;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.Box;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

/**
* A simple container that contains a Drawing instance and keeps it
Expand All @@ -37,6 +50,7 @@ This file is part of Universal Gcode Sender (UGS).
*/
public class DrawingContainer extends JPanel implements ComponentListener, MouseWheelListener {

public static final double CENTER_ZOOM_SCALE_FACTOR = 0.8;
private final transient Controller controller;
private JScrollPane scrollPane;
private JPanel buttonPanel;
Expand Down Expand Up @@ -82,6 +96,7 @@ protected void processMouseWheelEvent(MouseWheelEvent e) {
scrollPane.getHorizontalScrollBar().setUnitIncrement(5);
scrollPane.setPreferredSize(getSize());
scrollPane.setBounds(0, 0, getWidth(), getHeight());
scrollPane.setWheelScrollingEnabled(false);

buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setOpaque(false);
Expand Down Expand Up @@ -135,19 +150,22 @@ public void componentHidden(ComponentEvent e) {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
BackendAPI backend = CentralLookup.getDefault().lookup(BackendAPI.class);
double scaleFactor = (e.getPreciseWheelRotation() * 0.1) * (backend.getSettings().isInvertMouseZoom() ? -1d : 1d);
controller.getDrawing().setScale(controller.getDrawing().getScale() + scaleFactor);
Rectangle viewRect = scrollPane.getViewport().getViewRect();
Dimension size = scrollPane.getViewport().getView().getSize();

double currentViewPortCenterX = (scrollPane.getHorizontalScrollBar().getValue() + (scrollPane.getWidth() / 2d)) / controller.getDrawing().getScale();
double currentViewPortCenterY = (scrollPane.getVerticalScrollBar().getValue() + (scrollPane.getHeight() / 2d)) / controller.getDrawing().getScale();
// Get the mouse position relative to the center
double mouseX = (e.getPoint().getX() - viewRect.getCenterX()) * CENTER_ZOOM_SCALE_FACTOR;
double mouseY = (e.getPoint().getY() - viewRect.getCenterY()) * CENTER_ZOOM_SCALE_FACTOR;

double mouseX = e.getPoint().getX() / controller.getDrawing().getScale();
double mouseY = e.getPoint().getY() / controller.getDrawing().getScale();
// Get the current view position in percent
double previousXScrollbarPercent = (viewRect.getX() + mouseX) / size.getWidth();
double previousYScrollbarPercent = (viewRect.getY() + mouseY) / size.getHeight();

double x = ((mouseX - currentViewPortCenterX) * controller.getDrawing().getScale()) * scaleFactor;
double y = ((mouseY - currentViewPortCenterY) * controller.getDrawing().getScale()) * scaleFactor;
// Apply the scaling
double scaleFactor = (e.getPreciseWheelRotation() * controller.getDrawing().getScale() * 0.1) * (backend.getSettings().isInvertMouseZoom() ? -1d : 1d);
controller.getDrawing().setScale(controller.getDrawing().getScale() + scaleFactor);

scrollPane.getHorizontalScrollBar().setValue(scrollPane.getHorizontalScrollBar().getValue() + (int) Math.round(x + 0.5));
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getValue() + (int) Math.round(y + 0.5));
scrollPane.getHorizontalScrollBar().setValue((int) Math.round((controller.getDrawing().getMinimumSize().getWidth() * previousXScrollbarPercent)));
scrollPane.getVerticalScrollBar().setValue((int) Math.round((controller.getDrawing().getMinimumSize().getHeight() * previousYScrollbarPercent)));
}
}

0 comments on commit 0429cf5

Please sign in to comment.