| | |
| | | private static final double ICON_SCALE_FACTOR = 0.8; |
| | | private static final double MIN_SCALE = 1e-6; |
| | | private static final Color FALLBACK_FILL = new Color(0, 150, 0); |
| | | private static final String DEFAULT_ICON_PATH = "image/gecaoji.png"; |
| | | private static final String HANDHELD_ICON_PATH = "image/URT.png"; |
| | | |
| | | private final Image mowerIcon; |
| | | private final Image defaultIcon; |
| | | private final Image handheldIcon; |
| | | private Image activeIcon; |
| | | private boolean handheldIconActive; |
| | | private final Ellipse2D.Double fallbackShape = new Ellipse2D.Double(); |
| | | private Point2D.Double position = new Point2D.Double(); |
| | | private boolean positionValid; |
| | | private double headingDegrees; |
| | | |
| | | public Gecaoji() { |
| | | mowerIcon = loadIcon(); |
| | | defaultIcon = loadIcon(DEFAULT_ICON_PATH); |
| | | handheldIcon = loadIcon(HANDHELD_ICON_PATH); |
| | | activeIcon = defaultIcon != null ? defaultIcon : handheldIcon; |
| | | handheldIconActive = false; |
| | | refreshFromDevice(); |
| | | } |
| | | |
| | | private Image loadIcon() { |
| | | private Image loadIcon(String path) { |
| | | try { |
| | | ImageIcon icon = new ImageIcon("image/gecaoji.png"); |
| | | ImageIcon icon = new ImageIcon(path); |
| | | if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) { |
| | | return null; |
| | | } |
| | | return icon.getImage(); |
| | | } catch (Exception ex) { |
| | | System.err.println("Unable to load mower icon: " + ex.getMessage()); |
| | | System.err.println("Unable to load mower icon from " + path + ": " + ex.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | |
| | | double y = parseCoordinate(device.getRealtimeY()); |
| | | double heading = parseHeading(device.getHeading()); |
| | | if (Double.isNaN(x) || Double.isNaN(y)) { |
| | | positionValid = false; |
| | | // Keep showing the last known mower position when temporary sensor glitches occur. |
| | | return; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | double worldSize = (ICON_PIXEL_SIZE * ICON_SCALE_FACTOR) / Math.max(scale, MIN_SCALE); |
| | | if (mowerIcon != null && mowerIcon.getWidth(null) > 0 && mowerIcon.getHeight(null) > 0) { |
| | | drawIcon(g2d, worldSize); |
| | | Image icon = activeIcon; |
| | | if (icon != null && icon.getWidth(null) > 0 && icon.getHeight(null) > 0) { |
| | | drawIcon(g2d, worldSize, icon); |
| | | } else { |
| | | drawFallback(g2d, worldSize); |
| | | } |
| | | } |
| | | |
| | | private void drawIcon(Graphics2D g2d, double worldSize) { |
| | | double iconWidth = mowerIcon.getWidth(null); |
| | | double iconHeight = mowerIcon.getHeight(null); |
| | | private void drawIcon(Graphics2D g2d, double worldSize, Image icon) { |
| | | double iconWidth = icon.getWidth(null); |
| | | double iconHeight = icon.getHeight(null); |
| | | double maxSide = Math.max(iconWidth, iconHeight); |
| | | double scaleFactor = worldSize / Math.max(maxSide, MIN_SCALE); |
| | | double rotationRadians = Math.toRadians(-headingDegrees); |
| | |
| | | transformed.scale(scaleFactor, scaleFactor); |
| | | transformed.translate(-iconWidth / 2.0, -iconHeight / 2.0); |
| | | g2d.setTransform(transformed); |
| | | g2d.drawImage(mowerIcon, 0, 0, null); |
| | | g2d.drawImage(icon, 0, 0, null); |
| | | g2d.setTransform(original); |
| | | } |
| | | |
| | |
| | | double worldSize = (ICON_PIXEL_SIZE * ICON_SCALE_FACTOR) / Math.max(scale, MIN_SCALE); |
| | | return worldSize / 2.0; |
| | | } |
| | | |
| | | public boolean useHandheldIcon(boolean handheldMode) { |
| | | if (handheldIconActive == handheldMode) { |
| | | return false; |
| | | } |
| | | handheldIconActive = handheldMode; |
| | | Image next = handheldMode |
| | | ? (handheldIcon != null ? handheldIcon : defaultIcon) |
| | | : defaultIcon; |
| | | if (next == null) { |
| | | next = handheldIcon; |
| | | } |
| | | activeIcon = next; |
| | | return true; |
| | | } |
| | | } |