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

重构不可能正方形绘制循环代码后图形错位,如何修正?

问题描述

现有一段C#代码可通过Painter类的MakeIt、Change方法正确绘制不可能正方形。为简化重复的四边绘制逻辑,将其重构为for循环,循环变量side取值为-1、0、1、2,通过side * Math.PI / 2计算角度,预期按2、1、4、3的顺序绘制出正确图形,但实际图形位置错位。需修改循环代码以实现正确绘制。

原正确代码

using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace RefactorMe
{
    internal class Painter
    {
        private static float x, y;
        private static Graphics graph;

        public static void Initialization(Graphics newGraph)
        {
            graph = newGraph;
            graph.SmoothingMode = SmoothingMode.None;
            graph.Clear(Color.Black);
        }

        public static void Set_position(float x0, float y0)
        { x = x0; y = y0; }

        public static void MakeIt(Pen pen, double lenght, double angle)
        {
            //Takes a step of length lenght in the direction of angle and draws the passed trajectory
            float x1 = (float)(x + (lenght * Math.Cos(angle)));
            float y1 = (float)(y + (lenght * Math.Sin(angle)));
            graph.DrawLine(pen, x, y, x1, y1);
            x = x1;
            y = y1;
        }

        public static void Change(double lenght, double angle)
        {
            x = (float)(x + (lenght * Math.Cos(angle)));
            y = (float)(y + (lenght * Math.Sin(angle)));
        }
    }

    public class ImpossibleSquare
    {
        public static void Draw(int width, int height, double turnAngle, Graphics graph)
        {
            // turnAngle is not used yet, but will be used in the future
            Painter.Initialization(graph);

            var size = Math.Min(width, height);

            var diagonal_length = Math.Sqrt(2) * (size * 0.375f + size * 0.04f) / 2;
            var x0 = (float)(diagonal_length * Math.Cos(Math.PI / 4 + Math.PI)) + width / 2f;
            var y0 = (float)(diagonal_length * Math.Sin(Math.PI / 4 + Math.PI)) + height / 2f;

            Painter.Set_position(x0, y0);
            //Draws 1st side
            Painter.MakeIt(Pens.Yellow, size * 0.375f, 0);
            Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), Math.PI / 4);
            Painter.MakeIt(Pens.Yellow, size * 0.375f, Math.PI);
            Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, Math.PI / 2);

            Painter.Change(size * 0.04f, -Math.PI);
            Painter.Change(size * 0.04f * Math.Sqrt(2), 3 * Math.PI / 4);
            
            //Draws 2nd side
            Painter.MakeIt(Pens.Yellow, size * 0.375f, -Math.PI / 2);
            Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), -Math.PI / 2 + Math.PI / 4);
            Painter.MakeIt(Pens.Yellow, size * 0.375f, -Math.PI / 2 + Math.PI);
            Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, -Math.PI / 2 + Math.PI / 2);

            Painter.Change(size * 0.04f, -Math.PI / 2 - Math.PI);
            Painter.Change(size * 0.04f * Math.Sqrt(2), -Math.PI / 2 + 3 * Math.PI / 4);

            //Draws 3rd side
            Painter.MakeIt(Pens.Yellow, size * 0.375f, Math.PI);
            Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), Math.PI + Math.PI / 4);
            Painter.MakeIt(Pens.Yellow, size * 0.375f, Math.PI + Math.PI);
            Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, Math.PI + Math.PI / 2);

            Painter.Change(size * 0.04f, Math.PI - Math.PI);
            Painter.Change(size * 0.04f * Math.Sqrt(2), Math.PI + 3 * Math.PI / 4);

            //Draws 4th side
            Painter.MakeIt(Pens.Yellow, size * 0.375f, Math.PI / 2);
            Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), Math.PI / 2 + Math.PI / 4);
            Painter.MakeIt(Pens.Yellow, size * 0.375f, Math.PI / 2 + Math.PI);
            Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, Math.PI / 2 + Math.PI / 2);

            Painter.Change(size * 0.04f, Math.PI / 2 - Math.PI);
            Painter.Change(size * 0.04f * Math.Sqrt(2), Math.PI / 2 + 3 * Math.PI / 4);
        }
    }
}

重构后的错误循环代码

for (int side = -1; side < 3; side++)
{
    Painter.MakeIt(Pens.Yellow, size * 0.375f, (side * Math.PI / 2));
    Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + Math.PI / 4);
    Painter.MakeIt(Pens.Yellow, size * 0.375f, (side * Math.PI / 2) + Math.PI);
    Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, (side * Math.PI / 2) + Math.PI / 2);

    Painter.Change(size * 0.04f, (side * Math.PI / 2) - Math.PI);
    Painter.Change(size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + (3 * Math.PI / 4));
}

图形说明

  • 正确图形:呈现标准的不可能正方形视觉错觉,四边的错位衔接符合视觉逻辑
  • 错误图形:各边位置错位,无法形成正确的视觉错觉

解决方案

错误根源在于循环的执行顺序与原代码的绘制顺序不匹配,导致图形衔接错位。原代码按边1→边2→边3→边4的顺序绘制,而重构后的循环按边2→边1→边4→边3的顺序执行,初始位置是边1的起点,直接绘制边2会导致整体位置偏移。

修改循环代码,指定side的正确顺序,使其对应原代码的绘制流程:

// 按原代码的边1、边2、边3、边4顺序指定side取值
int[] sideSequence = { 0, -1, 2, 1 };
foreach (int side in sideSequence)
{
    Painter.MakeIt(Pens.Yellow, size * 0.375f, side * Math.PI / 2);
    Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + Math.PI / 4);
    Painter.MakeIt(Pens.Yellow, size * 0.375f, (side * Math.PI / 2) + Math.PI);
    Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, (side * Math.PI / 2) + Math.PI / 2);

    Painter.Change(size * 0.04f, (side * Math.PI / 2) - Math.PI);
    Painter.Change(size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + 3 * Math.PI / 4);
}

若必须使用for循环而非foreach,可改为:

for (int i = 0; i < 4; i++)
{
    int side = new[] { 0, -1, 2, 1 }[i];
    Painter.MakeIt(Pens.Yellow, size * 0.375f, side * Math.PI / 2);
    Painter.MakeIt(Pens.Yellow, size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + Math.PI / 4);
    Painter.MakeIt(Pens.Yellow, size * 0.375f, (side * Math.PI / 2) + Math.PI);
    Painter.MakeIt(Pens.Yellow, size * 0.375f - size * 0.04f, (side * Math.PI / 2) + Math.PI / 2);

    Painter.Change(size * 0.04f, (side * Math.PI / 2) - Math.PI);
    Painter.Change(size * 0.04f * Math.Sqrt(2), (side * Math.PI / 2) + 3 * Math.PI / 4);
}

这样循环会按照原代码的正确顺序绘制四边,确保图形衔接正确,形成预期的不可能正方形视觉错觉。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:25:29