张世豪
2025-12-01 d23f280e37080cb9b5934350cc0fafb2c68421d5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package gecaoji;
 
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
 
import javax.swing.ImageIcon;
 
/** Mower rendering helper. */
public class Gecaoji {
    private static final double ICON_PIXEL_SIZE = 48.0;
    private static final double MIN_SCALE = 1e-6;
    private static final Color FALLBACK_FILL = new Color(0, 150, 0);
 
    private final Image mowerIcon;
    private final Ellipse2D.Double fallbackShape = new Ellipse2D.Double();
    private Point2D.Double position = new Point2D.Double();
    private boolean positionValid;
 
    public Gecaoji() {
        mowerIcon = loadIcon();
        refreshFromDevice();
    }
 
    private Image loadIcon() {
        try {
            ImageIcon icon = new ImageIcon("image/mow.png");
            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());
            return null;
        }
    }
 
    public void refreshFromDevice() {
        Device device = Device.getGecaoji();
        if (device == null) {
            positionValid = false;
            return;
        }
 
        double x = parseCoordinate(device.getRealtimeX());
        double y = parseCoordinate(device.getRealtimeY());
        if (Double.isNaN(x) || Double.isNaN(y)) {
            positionValid = false;
            return;
        }
 
        ensurePosition();
        position.x = x;
        position.y = y;
        positionValid = true;
    }
 
    private void ensurePosition() {
        if (position == null) {
            position = new Point2D.Double();
        }
    }
 
    private double parseCoordinate(String value) {
        if (value == null) {
            return Double.NaN;
        }
        String trimmed = value.trim();
        if (trimmed.isEmpty() || "-1".equals(trimmed)) {
            return Double.NaN;
        }
        try {
            return Double.parseDouble(trimmed);
        } catch (NumberFormatException ex) {
            return Double.NaN;
        }
    }
 
    public void draw(Graphics2D g2d, double scale) {
        if (!positionValid) {
            return;
        }
 
        double worldSize = ICON_PIXEL_SIZE / Math.max(scale, MIN_SCALE);
        if (mowerIcon != null && mowerIcon.getWidth(null) > 0 && mowerIcon.getHeight(null) > 0) {
            drawIcon(g2d, worldSize);
        } else {
            drawFallback(g2d, worldSize);
        }
    }
 
    private void drawIcon(Graphics2D g2d, double worldSize) {
        double iconWidth = mowerIcon.getWidth(null);
        double iconHeight = mowerIcon.getHeight(null);
        double maxSide = Math.max(iconWidth, iconHeight);
        double scaleFactor = worldSize / Math.max(maxSide, MIN_SCALE);
 
        AffineTransform original = g2d.getTransform();
        AffineTransform transformed = new AffineTransform(original);
        transformed.translate(position.x, position.y);
        transformed.scale(scaleFactor, scaleFactor);
        transformed.translate(-iconWidth / 2.0, -iconHeight / 2.0);
        g2d.setTransform(transformed);
        g2d.drawImage(mowerIcon, 0, 0, null);
        g2d.setTransform(original);
    }
 
    private void drawFallback(Graphics2D g2d, double worldSize) {
        double radius = worldSize / 2.0;
        ensurePosition();
        fallbackShape.setFrame(position.x - radius, position.y - radius, worldSize, worldSize);
 
        Color original = g2d.getColor();
        g2d.setColor(FALLBACK_FILL);
        g2d.fill(fallbackShape);
        g2d.setColor(Color.WHITE);
        g2d.draw(fallbackShape);
        g2d.setColor(original);
    }
 
    public boolean hasValidPosition() {
        return positionValid;
    }
 
    public Point2D.Double getPosition() {
        if (!positionValid) {
            return null;
        }
        ensurePosition();
        return new Point2D.Double(position.x, position.y);
    }
 
    public double getWorldRadius(double scale) {
        if (!positionValid) {
            return Double.NaN;
        }
        double worldSize = ICON_PIXEL_SIZE / Math.max(scale, MIN_SCALE);
        return worldSize / 2.0;
    }
}