张世豪
11 小时以前 ed6936545d20cc490145d2936cee4387be2afd53
src/gecaoji/Gecaoji.java
@@ -12,33 +12,48 @@
/** Mower rendering helper. */
public class Gecaoji {
    private static final double ICON_PIXEL_SIZE = 48.0;
    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/mow.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;
        }
    }
    public void refreshFromDevice() {
        // 检查是否正在导航预览模式,如果是则不更新位置
        if (isNavigating()) {
            return;
        }
        Device device = Device.getGecaoji();
        if (device == null) {
            positionValid = false;
@@ -47,8 +62,9 @@
        double x = parseCoordinate(device.getRealtimeX());
        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;
        }
@@ -56,6 +72,23 @@
        position.x = x;
        position.y = y;
        positionValid = true;
        headingDegrees = heading;
    }
    /**
     * 检查是否正在导航预览模式
     * @return 如果正在导航预览返回true,否则返回false
     */
    private boolean isNavigating() {
        try {
            dikuai.daohangyulan nav = dikuai.daohangyulan.getInstance();
            if (nav != null) {
                return nav.isNavigating();
            }
        } catch (Exception e) {
            // 如果获取导航实例失败,返回false(不影响主要功能)
        }
        return false;
    }
    private void ensurePosition() {
@@ -79,32 +112,57 @@
        }
    }
    private double parseHeading(String value) {
        if (value == null) {
            return 0.0;
        }
        String trimmed = value.trim();
        if (trimmed.isEmpty() || "-1".equals(trimmed)) {
            return 0.0;
        }
        try {
            double parsed = Double.parseDouble(trimmed);
            if (!Double.isFinite(parsed)) {
                return 0.0;
            }
            double normalized = parsed % 360.0;
            return normalized < 0 ? normalized + 360.0 : normalized;
        } catch (NumberFormatException ex) {
            return 0.0;
        }
    }
    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);
    double worldSize = (ICON_PIXEL_SIZE * ICON_SCALE_FACTOR) / Math.max(scale, MIN_SCALE);
        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);
        AffineTransform original = g2d.getTransform();
        AffineTransform transformed = new AffineTransform(original);
        transformed.translate(position.x, position.y);
        if (rotationRadians != 0.0) {
            transformed.rotate(rotationRadians);
        }
        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);
    }
@@ -118,6 +176,15 @@
        g2d.fill(fallbackShape);
        g2d.setColor(Color.WHITE);
        g2d.draw(fallbackShape);
    double rotationRadians = Math.toRadians(-headingDegrees);
    double lineLength = radius;
    double dx = lineLength * Math.sin(rotationRadians);
    double dy = lineLength * Math.cos(rotationRadians);
    g2d.drawLine(
        (int) Math.round(position.x),
        (int) Math.round(position.y),
        (int) Math.round(position.x + dx),
        (int) Math.round(position.y + dy));
        g2d.setColor(original);
    }
@@ -133,11 +200,58 @@
        return new Point2D.Double(position.x, position.y);
    }
    /**
     * 设置割草机位置(用于导航预览等场景)
     * @param x X坐标
     * @param y Y坐标
     */
    public void setPosition(double x, double y) {
        ensurePosition();
        position.x = x;
        position.y = y;
        positionValid = true;
    }
    /**
     * 设置割草机方向(用于导航预览等场景)
     * @param headingDegrees 方向角度(度,0-360)
     */
    public void setHeading(double headingDegrees) {
        double normalized = headingDegrees % 360.0;
        if (normalized < 0) {
            normalized += 360.0;
        }
        this.headingDegrees = normalized;
    }
    /**
     * 获取割草机方向
     * @return 方向角度(度,0-360)
     */
    public double getHeading() {
        return headingDegrees;
    }
    public double getWorldRadius(double scale) {
        if (!positionValid) {
            return Double.NaN;
        }
        double worldSize = ICON_PIXEL_SIZE / Math.max(scale, MIN_SCALE);
        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;
    }
}