Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
e42326950This file is pinned to the selected release. Download the original to open it in your design tool.
package com.marginallyclever.makelangelo.plotter.plottercontrols;
import com.marginallyclever.util.PreferencesHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.marginallyclever.makelangelo.Translator;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
/**
* {@link CartesianButtons} is a GUI to provide Pronterface-style buttons for XY
* driving. {@link ActionListener}s can listen for click events on each button.
* Button {@link ActionEvent}s are named for their button. Button are divided
* into quadrants. In each quadrant, outside zones are lower numbers than inside
* zones. Quadrants are numbered counter-clockwise, starting with eastern
* quadrant. So the zone 0 is the +100 east and zone 11 is the -1 south.
* The center button is 12. The outside is zone 0.
* 3
* 4
* 5
* 6 7 8 12 2 1 0
* 11
* 10 (-1)
* 9
* @author Dan Royer
* @since 7.28.0
*/
public class CartesianButtons extends JComponent {
private static final Logger logger = LoggerFactory.getLogger(CartesianButtons.class);
public static final int NUM_ZONES_PER_QUADRANT = 3;
public static final int TOTAL_ZONES = NUM_ZONES_PER_QUADRANT * 4 + 1;
public static final int ZONE_CENTER = NUM_ZONES_PER_QUADRANT * 4;
private int centerRadius = 30;
private int buttonWidth = 30;
private int highlightZone = -1;
private Color highlightColor;
private String[] labels = new String[TOTAL_ZONES];
private ArrayList<ActionListener> listeners = new ArrayList<ActionListener>();
public CartesianButtons() {
super();
assignDefaultLabels();
setMinimumSize(getPreferredSize());
setMaximumSize(getPreferredSize());
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
if (!isEnabled())
return;
// logger.debug("moved");
int zone = getZoneUnderPoint(e.getPoint());
if (highlightZone != zone) {
highlightZone = zone;
highlightColor = getColorButtonHighlight();
repaint();
}
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (!isEnabled())
return;
highlightZone = getZoneUnderPoint(e.getPoint());
highlightColor = getColorButtonSelect();
// logger.debug("pressed");
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
highlightColor = getColorButtonHighlight();
// logger.debug("released");
int zone = getZoneUnderPoint(e.getPoint());
if(zone!=-1 && highlightZone == zone) {
// logger.debug("clicked zone {}",zone);
notifyActionListeners(new ActionEvent(this, zone, "clicked"));
}
highlightZone = -1;
repaint();
}
});
}
private void assignDefaultLabels() {
int j = 0;
int v = 2;
for (int a = 0; a < 4; ++a) {
int n = 100 * (v > 0 ? 1 : -1);
for (int i = NUM_ZONES_PER_QUADRANT - 1; i >= 0; --i) {
labels[j++] = Integer.toString(n);
n /= 10;
}
--v;
}
labels[ZONE_CENTER] = Translator.get("CartesianButtons.buttonCenter");
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle r = this.getBounds();
g.translate(r.width / 2, r.height / 2);
drawAllQuadrantButtons(g);
drawCenterButton(g);
}
private void drawAllQuadrantButtons(Graphics g) {
g.setColor(new Color(1.0f, 0.0f, 0.0f));
int k = 0;
for (int a = 0; a < 4; ++a) {
for (int i = NUM_ZONES_PER_QUADRANT - 1; i >= 0; --i) {
int j = i + 1;
int angle = a * 90;
drawArcingButtonInternal(g, angle - 44, angle + 44, centerRadius + i * buttonWidth,
centerRadius + j * buttonWidth, (k == highlightZone), labels[k]);
k++;
}
}
}
private void drawCenterButton(Graphics g) {
if (highlightZone == NUM_ZONES_PER_QUADRANT * 4 && this.isEnabled()) {
g.setColor(highlightColor);
} else {
g.setColor(getColorControl());
}
g.fillArc(-centerRadius, -centerRadius, centerRadius * 2, centerRadius * 2, 0, 360);
g.setColor(this.isEnabled() ? getColorForegroundText() : getColorDisabledText());
g.drawArc(-centerRadius, -centerRadius, centerRadius * 2, centerRadius * 2, 0, 360);
drawCenteredText(g, labels[ZONE_CENTER], 0, 0);
}
private void drawCenteredText(Graphics g, String string, int x, int y) {
FontMetrics fm = g.getFontMetrics();
Rectangle2D r = fm.getStringBounds(string, g);
int w = (int) r.getWidth();
int h = (int) r.getHeight() - fm.getLeading();
g.setColor(this.isEnabled() ? getColorForegroundText() : getColorDisabledText());
g.drawString(string, x - w / 2, y + h / 2);
}
private Color getColorControl() {
Color c = UIManager.getColor("control");
if (c == null)
c = SystemColor.control;
return c;
}
private Color getColorDisabledText() {
Color c = UIManager.getColor("Button.disabledText");
if (c == null)
c = SystemColor.lightGray;
return c;
}
// UIManager.getColor("Button.darkShadow")
private Color getColorForegroundText() {
Color c = UIManager.getColor("Label.foreground");
if (c == null)
c = SystemColor.darkGray;
return c;
}
private Color getColorButtonHighlight() {
Color c = UIManager.getColor("Button.highlight");
if (c == null)
c = SystemColor.controlHighlight;
return c;
}
private Color getColorButtonSelect() {
Color c = UIManager.getColor("Button.select");
if (c == null)
c = SystemColor.textHighlight;
return c;
}
/**
* @param p
* @return The zone under point p. in each quadrant, outside zones are lower
* numbers than inside zones. Quadrants are numbered counter-clockwise,
* starting with eastern quadrant. Outer buttons have a lower index than inner buttons.
*/
private int getZoneUnderPoint(Point p) {
Rectangle r = this.getBounds();
double dx = p.x - r.width / 2;
double dy = -(p.y - r.height / 2);
int len = (int) Math.sqrt(dx * dx + dy * dy);
if (len >= centerRadius + buttonWidth*NUM_ZONES_PER_QUADRANT)
return -1; // miss
if (len < centerRadius)
return NUM_ZONES_PER_QUADRANT * 4;
double mouseAngle = (Math.toDegrees(Math.atan2(dy, dx) + Math.PI) + 180) % 360;
int quadrant = (int) ((mouseAngle + 45) / 90) % 4;
// 0 west 1 north 2 east 3 south
int zone = (int) ((len - centerRadius) / buttonWidth);
zone = NUM_ZONES_PER_QUADRANT - 1 - zone;
// logger.debug("{}\t{}\t{}\t{}",dx,dy,mouseAngle,quadrant,len);
return quadrant * NUM_ZONES_PER_QUADRANT + zone;
}
private void drawArcingButtonInternal(Graphics g, int startAngle, int endAngle, int r0, int r1, boolean highlight,
String label) {
boolean shouldHighlight = highlight && this.isEnabled();
g.setColor(shouldHighlight ? highlightColor : getColorControl());
g.fillArc(-r1, -r1, r1 * 2, r1 * 2, startAngle, endAngle - startAngle);
g.setColor(getColorControl());
g.fillArc(-r0, -r0, r0 * 2, r0 * 2, startAngle, endAngle - startAngle);
g.setColor(this.isEnabled() ? getColorForegroundText() : getColorDisabledText());
// g.setColor(SystemColor.BLACK);
// g.drawArc(-r0, -r0, r0*2, r0*2, startAngle, endAngle-startAngle);
g.drawArc(-r1, -r1, r1 * 2, r1 * 2, startAngle, endAngle - startAngle);
drawLineInternal(g, startAngle, r0, r1);
drawLineInternal(g, endAngle, r0, r1);
drawLabel(g, (endAngle + startAngle) / 2, (r1 + r0) / 2, label);
}
private void drawLabel(Graphics g, int angle, int radius, String label) {
double r = Math.toRadians(angle);
double s = Math.sin(r);
double c = Math.cos(r);
int x1 = (int) Math.round(c * radius);
int y1 = (int) Math.round(s * radius);
drawCenteredText(g, label, x1, -y1);
}
private void drawLineInternal(Graphics g, int angle, int r0, int r1) {
double r = Math.toRadians(angle);
double s = Math.sin(r);
double c = Math.cos(r);
int x1 = (int) Math.round(c * r0);
int y1 = (int) Math.round(s * r0);
int x2 = (int) Math.round(c * r1);
int y2 = (int) Math.round(s * r1);
g.drawLine(x1, -y1, x2, -y2);
}
public void addActionListener(ActionListener a) {
listeners.add(a);
}
public void removeActionListener(ActionListener a) {
listeners.remove(a);
}
private void notifyActionListeners(ActionEvent ae) {
for (ActionListener a : listeners)
a.actionPerformed(ae);
}
@Override
public Dimension getPreferredSize() {
int w = (centerRadius + NUM_ZONES_PER_QUADRANT * buttonWidth) * 2;
int h = (centerRadius + NUM_ZONES_PER_QUADRANT * buttonWidth) * 2;
return new Dimension(w + 1, h + 1);
}
/**
*
* @param id
* @return quandrant 0-4, or 5 for center button.
*/
public static boolean isCenterZone(int id) {
return id == ZONE_CENTER;
}
/**
*
* @param id
* @return quandrant 0-4, or 5 for center button.
*/
public static int getQuadrant(int id) {
return (int) (id / NUM_ZONES_PER_QUADRANT);
}
/**
*
* @param id
* @return zone number, or -1 for
*/
public static int getZone(int id) {
if (id >= ZONE_CENTER)
return -1;
return (int) (id % NUM_ZONES_PER_QUADRANT);
}
public String getLabel(int id) throws IllegalArgumentException {
if (id < 0 || id >= TOTAL_ZONES)
throw new IllegalArgumentException("must be 0...TOTAL_ZONES-1");
return labels[id];
}
public void setLabel(int id, String arg0) throws IllegalArgumentException {
if (id < 0 || id >= TOTAL_ZONES)
throw new IllegalArgumentException("must be 0...TOTAL_ZONES-1");
labels[id] = arg0;
}
public static void main(String[] args) {
PreferencesHelper.start();
Translator.start();
JFrame frame = new JFrame("CartesianButtons");
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CartesianButtons button = new CartesianButtons();
frame.add(button);
button.addActionListener((e) -> {
logger.debug("{} {}", e.getActionCommand(), button.getLabel(e.getID()));
});
frame.pack();
frame.setVisible(true);
}
}