重构不可能正方形绘制循环代码后图形错位,如何修正?
问题描述
现有一段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
相关产品推荐
相关产品推荐

