You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何垂直翻转整个awt.Canvas以适配左下角为原点的笛卡尔坐标系

解决方案

核心问题原因

  • 直接调用Graphics2D.setTransform()会覆盖系统默认的坐标变换(包括高DPI屏幕适配、容器偏移等参数),导致坐标计算和实际渲染位置不匹配,出现原点偏移、右侧空白问题
  • AffineTransform的执行顺序与代码书写顺序相反,你当前的变换顺序错误,导致原点平移不到位
  • 全局应用Y轴翻转后,文字渲染也会继承该变换,自然出现倒置
  • 坐标轴绘制逻辑写死了MINWIDTH/MINHEIGHT,没有适配Canvas实际宽高,也是右侧出现多余空白的原因之一

修正方案

1. 正确应用坐标变换

不要直接替换全局变换,改用transform()方法追加自定义变换,同时调整变换顺序,确保原点正确平移到画布左下角:

// 先保存原始的系统默认变换,后面绘制文字要用到
AffineTransform originalTransform = g2.getTransform();
// 自定义变换:代码书写顺序是先缩放再平移,实际执行顺序是先平移再Y轴翻转
AffineTransform cartesianTx = AffineTransform.getScaleInstance(1, -1);
cartesianTx.translate(0, getHeight());
// 追加变换,不覆盖原有系统变换
g2.transform(cartesianTx);

2. 解决文字倒置问题

绘制文字前先临时恢复为原始系统变换,手动把文字的笛卡尔坐标转换为屏幕坐标后再绘制,绘制完成后恢复自定义变换即可:

// 绘制文字前保存当前变换,恢复原始变换
AffineTransform currentTx = g.getTransform();
g.setTransform(originalTransform);
// 笛卡尔坐标(x,y)转屏幕坐标:x不变,y = 画布高度 - 笛卡尔y值
g.drawString("测试文字", x, getHeight() - y);
// 恢复自定义变换,继续绘制图形
g.setTransform(currentTx);

完整修正后的代码

import java.awt.*;
import java.awt.geom.AffineTransform;

public class PlotterView extends Canvas {

    protected int MINWIDTH = 500;
    protected int MINHEIGHT = 400;

    Point[][] lines;

    public PlotterView() {
        Dimension dim = new Dimension(MINWIDTH, MINHEIGHT);
        setPreferredSize(dim);
        setBackground(Color.LIGHT_GRAY);
    }

    protected void plotAxes(Graphics2D g, AffineTransform originalTx) {

        Color defaultColor = g.getColor();  // save to restore defaults in the end
        int originX = 5;   // x origin of both axes - shift right
        int originY = 5;   // y origin of both axis - shift up

        // 绘制文字:临时恢复原始变换
        AffineTransform customTx = g.getTransform();
        g.setTransform(originalTx);
        g.setColor(Color.BLACK);
        // 坐标转换:笛卡尔y值originY转屏幕y为 getHeight() - originY
        g.drawString("X: " + originX + "; Y: " + originY, originX, getHeight() - originY);
        // 恢复自定义变换
        g.setTransform(customTx);

        // X-Axis:用实际宽高代替固定MINWIDTH,适配窗口缩放
        int canvasWidth = getWidth();
        int canvasHeight = getHeight();
        g.setColor(Color.RED);
        g.drawLine(originX, originY, canvasWidth-20, originY);
        g.drawLine(canvasWidth-20, originY, canvasWidth-30, originY-5);
        g.drawLine(canvasWidth-20, originY, canvasWidth-30, originY+5);

        // Y-Axis
        g.setColor(Color.BLUE);
        g.drawLine(originX, originY,  originX, canvasHeight-20);
        g.drawLine(originX, canvasHeight-20, originX-5, canvasHeight-30);
        g.drawLine(originX, canvasHeight-20, originX+5, canvasHeight-30);

        // Restore defaults
        g.setColor(defaultColor);

    }

    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D g2 = (Graphics2D) g;
        // 保存系统原始变换
        AffineTransform originalTx = g2.getTransform();

        // 正确的笛卡尔坐标变换
        AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
        tx.translate(0, getHeight());
        g2.transform(tx);

        plotAxes(g2, originalTx);

        // 绘制完成后恢复原始变换,避免影响后续渲染
        g2.setTransform(originalTx);
    }
}

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 18:24:05