坐标系

garfileo
上一篇:坐标变换

今天不再编蹩脚的故事,只是一本正经地画一个坐标系。

坐标系的原点用球体表示。画球的语法为

sphere {
  球心, 半径
  texture { ... }
}

例如:

sphere {
  <0, 0, 0>, 1
  texture { pigment { color Gray20 } }
}

坐标系的每个轴向由一个圆柱体和一个圆锥体组合而成。圆柱体的语法为

cylinder {
  端面的中心, 另一端面的中心,
  半径
  texture { ... }
}

例如

cylinder {
  <0, 0, 0>, x
  0.05
  texture { pigment { color Red } }
}

POV Ray 没有提供绘制直线段的功能,可以用圆柱模拟直线段。

圆锥体(确切地说,是圆台)的语法为

cone {
  端面的中心, 半径
  另一端面的中心, 半径
  texture { ... }
}

例如

cone {
  x, 0.1
  x + 0.3 * x, 0
  texture { pigment { color Red } }
}

下面是一个完整的例子,画出世界坐标系的 X 轴:

#version 3.7;
#include "colors.inc"
global_settings {
  assumed_gamma 1.0
}

object {
  union {
    sphere {
      <0, 0, 0>, 0.1
      texture { pigment { color Gray20 } }
    }
    object { 
      union {
        cylinder {
          <0, 0, 0>, x
          0.05
          texture { pigment { color Red } }
        }
        cone {
          x, 0.1
          x + 0.3 * x, 0
          texture { pigment { color Red } }
        }
      }
    }
  }
}

camera {
  location <0, 0, -3>
  look_at <0, 0, 0>
}

light_source {
  <3, 3, -3>
  color White
}

可得

依上例类推,可以相继画出世界坐标系的 Y 轴和 Z 轴:

object {
  union {
    // 原点
    sphere {
      <0, 0, 0>, 0.1
      texture { pigment { color Gray20 } }
    }
    // X 轴
    object { 
      union {
        cylinder {
          <0, 0, 0>, x
          0.05
          texture { pigment { color Red } }
        }
        cone {
          x, 0.1
          x + 0.3 * x, 0
          texture { pigment { color Red } }
        }
      }
    }
    // Y 轴
    object { 
      union {
        cylinder {
          <0, 0, 0>, y
          0.05
          texture { pigment { color Green } }
        }
        cone {
          y, 0.1
          y + 0.3 * y, 0
          texture { pigment { color Green } }
        }
      }
    }
    // Z 轴
    object { 
      union {
        cylinder {
          <0, 0, 0>, z
          0.05
          texture { pigment { color Blue } }
        }
        cone {
          z, 0.1
          z + 0.3 * z, 0
          texture { pigment { color Blue } }
        }
      }
    }
  }
}

结果为

Z 轴是看不见的,因为它垂直指向屏幕的内部,被原点挡住了。如果修改一下相机的位置,

camera {
  location <3, 3, -3>
  look_at <0, 0, 0>
}

就可以看到 Z 轴。

这种坐标系叫左手系。伸出左手,拇指向右指为 X 轴正向,食指向上指为 Y 轴的正向,中指向前指为 Z 轴的正向(谁也没办法让它向后指)。中学几何课上所用的直角坐标系是右手系。用 POV Ray 画画,若把握不住左手系,往往会南辕北辙。

画一个坐标系是容易的,虽然有些繁琐。画三十三个坐标系,也是容易的,只是差不多三十三倍的繁琐。

作为一名真正勤劳的人,不是极富耐心,去迁就繁琐,而是极富耐心,从代码中寻找一致性,化繁为简。这就是形而上学的用处。凡是能够维持事物原有功能的化繁为简的形而上学,就是有益的。

显然,坐标系的各个轴,除了方位和颜色有区别,它们的形状是相同——圆柱和圆锥的组合。这意味着,起码在形状上,三个轴可以共用一部分代码。这样,就可以构造一个函数,它的变量是坐标轴的方位、长度、粗细以及颜色。基于 POV Ray 的宏 #macro ... #end,可定义该函数:

#macro MakeAxis(Origin, Direction, Length, Thickness, Color)
  #local ArrowLength = 0.3 * Length;
  #local Direction = vnormalize(Direction);
  #local Begin = Origin;
  #local End = Begin + (Length - ArrowLength) * Direction;
  #local ArrowBegin = End;
  #local ArrowEnd = ArrowBegin + ArrowLength * Direction;
  object {
    union {
      cylinder {
        Begin, End
        Thickness
        texture { pigment { color Color } }
      }
      cone {
        ArrowBegin, 2 * Thickness
        ArrowEnd, 0
        texture { pigment { color Color } }
      }
    }
  }
#end

上述代码定义了 MakeAxis 宏,其中 local 语句用于宏内定义一个局部变量 r,作为轴的粗度(圆柱体的半径)。vlength 则是 POV Ray 内部定义的函数,用于计算向量的长度。

有了 MakeAxis 宏,画世界坐标系的代码便可简化为

#declare Origin = <0, 0, 0>;
object {
  union {
    sphere {
      Origin, 0.1
      texture { pigment { color Gray20 } }
    }
    #local Length = 1;
    #local Thickness = 0.05;
    MakeAxis(Origin, x, Length, Thickness, Red)
    MakeAxis(Origin, y, Length, Thickness, Green)
    MakeAxis(Origin, z, Length, Thickness, Blue)
  }
}

可以继续用宏,对代码进行简化。因为基于 MakeAxis,可以定义一个绘制任意一个直角坐标系的宏:

#macro MakeFrame(A, X_A, Y_A, Z_A, Length, Thickness)
  object {
    union {
      sphere {
        A, 2 * Thickness
        texture { pigment { color Gray50 } }
      }
      MakeAxis(A, X_A, Length, Thickness, Red)
      MakeAxis(A, Y_A, Length, Thickness, Green)
      MakeAxis(A, Z_A, Length, Thickness, Blue)
    }
  }
#end

现在,只用 MakeFrame 这个宏就可以绘制世界坐标系:

MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)

如果只是画一个坐标系,MakeFrame 着实鸡肋,但是要画三十三个坐标系,它的威力就能充分得以体现。下面的代码可以绘制三十三个坐标系:

#for(i, -5, 5, 1)
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * x
    translate 4 * i * x
  }
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * y
    translate 4 * (i * x + z)
  }
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * z
    translate 4 * (i * x - z)
  }  
#end

上述的 #for ... #end 代码段,用 C 语言来表述,意思是

for (int i = -5; i <= 5; i++) {
  ... ... ...
}

结果为:


附录

完整的代码如下:

#version 3.7;
#include "colors.inc"
global_settings {
  assumed_gamma 1.0
}

#macro MakeAxis(Origin, Direction, Length, Thickness, Color)
  #local ArrowLength = 0.3 * Length;
  #local Direction = vnormalize(Direction);
  #local Begin = Origin;
  #local End = Begin + (Length - ArrowLength) * Direction;
  #local ArrowBegin = End;
  #local ArrowEnd = ArrowBegin + ArrowLength * Direction;
  object {
    union {
      cylinder {
        Begin, End
        Thickness
        texture { pigment { color Color } }
      }
      cone {
        ArrowBegin, 2 * Thickness
        ArrowEnd, 0
        texture { pigment { color Color } }
      }
    }
  }
#end

#macro MakeFrame(A, X_A, Y_A, Z_A, L, Thickness)
  object {
    union {
      sphere {
        A, 2 * Thickness
        texture { pigment { color Gray50 } }
      }
      MakeAxis(A, X_A, L, Thickness, Red)
      MakeAxis(A, Y_A, L, Thickness, Green)
      MakeAxis(A, Z_A, L, Thickness, Blue)
    }
  }
#end

#for(i, -5, 5, 1)
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * x
    translate 4 * i * x
  }
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * y
    translate 4 * (i * x + z)
  }
  object {
    MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
    rotate 90 * i * z
    translate 4 * (i * x - z)
  }  
#end
  
#declare forenoon = 15 * <1, 1, -1>;
light_source {
  forenoon
  color White
}

camera {
  location forenoon * <2, 0.3, 0>
  look_at <0, 0, 0>
}
下一篇:标准正交基
阅读 785

5.7k 声望
1.8k 粉丝
0 条评论
你知道吗?

5.7k 声望
1.8k 粉丝
文章目录
宣传栏