创建 viewportviewport 是一个人造的坐标系统,可以转换成图像的物理坐标系统。viewport 的扩展可以是您希望的任何东西。例如,x 和 y 轴的起点和终点可以是 -2 和 2,这样 viewport 坐标平面的中心就是 0, 0。这对于三角图形(例如 sin 和 cosine)来说是很好的一个 viewport。或者,这个 viewport 也可以是不对称的,其中 y 值的范围从 -1 到 1,x 值的范围是从 0 到 10,000,这取决于您的需要。
这个 viewport 的其他值可以确保构建一个 400X400 的图像所采用的逻辑与构建一个 4000X2000 的图像所采用的逻辑是相同的。代码负责向这个 viewport 中写入数据,然后这个 viewport 自动实现到图像的物理尺寸的自动映射。
要让您的 viewport 正常工作,您需要将这个 viewport 的范围从 0,0 修改为 1,1,这可以让图形对象回调图形环境,从而将 viewport 的坐标转换成物理坐标。您可以将所有的代码都放到 BoxObject 基类中进行简化。
图 7 显示了有关新添加的代码的两个内容。首先是添加的 tx 和 ty 方法,这会将 x 和 y 坐标从 viewport 转换成物理图像的坐标。第二个是对 BoxObject 增加了 draw 方法,它的派生类应该用来进行制图。BoxObject 在 render 方法中实现 viewport 的转换,并使用物理坐标来调用 draw 方法。使用这种方法,Line、Oval 和 Rectangle 类都可以利用 viewport 坐标,而不需要担心坐标转换的问题。
图 7. 所添加的图形环境 viewport 转换这个新库的代码如清单 7 所示:
清单 7. 具有 viewport 支持的图形库1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
| <?php
class GraphicsEnvironment
{
public $width;
public $height;
public $gdo;
public $colors = array();
public function __construct( $width, $height )
{
$this->width = $width;
$this->height = $height;
$this->gdo = imagecreatetruecolor( $width, $height );
$this->addColor( "white", 255, 255, 255 );
imagefilledrectangle( $this->gdo, 0, 0,
$width, $height,
$this->getColor( "white" ) );
}
public function width() { return $this->width; }
public function height() { return $this->height; }
public function addColor( $name, $r, $g, $b )
{
$this->colors[ $name ] = imagecolorallocate(
$this->gdo,
$r, $g, $b );
}
public function getGraphicObject()
{
return $this->gdo;
}
public function getColor( $name )
{
return $this->colors[ $name ];
}
public function saveAsPng( $filename )
{
imagepng( $this->gdo, $filename );
}
public function tx( $x )
{
return $x * $this->width;
}
public function ty( $y )
{
return $y * $this->height;
}
}
abstract class GraphicsObject
{
abstract public function render( $ge );
abstract public function z();
}
function zsort( $a, $b )
{
if ( $a->z() < $b->z() ) return -1;
if ( $a->z() > $b->z() ) return 1;
return 0;
}
class Group extends GraphicsObject
{
private $z;
protected $members = array();
public function __construct( $z )
{
$this->z = $z;
}
public function add( $member )
{
$this->members []= $member;
}
public function render( $ge )
{
usort( $this->members, "zsort" );
foreach( $this->members as $gobj )
{
$gobj->render( $ge );
}
}
public function z() { return $this->z; }
}
abstract class BoxObject extends GraphicsObject
{
protected $color;
protected $sx;
protected $sy;
protected $ex;
protected $ey;
protected $z;
public function __construct( $z, $color, $sx, $sy, $ex, $ey )
{
$this->z = $z;
$this->color = $color;
$this->sx = $sx;
$this->sy = $sy;
$this->ex = $ex;
$this->ey = $ey;
}
public function render( $ge )
{
$rsx = $ge->tx( $this->sx );
$rsy = $ge->ty( $this->sy );
$rex = $ge->tx( $this->ex );
$rey = $ge->ty( $this->ey );
$this->draw( $rsx, $rsy, $rex, $rey,
$ge->getGraphicObject(),
$ge->getColor( $this->color ) );
}
abstract public function draw( $sx, $sy,
$ex, $ey, $gobj, $color );
public function z() { return $this->z; }
}
class Line extends BoxObject
{
public function draw( $sx, $sy, $ex, $ey,
$gobj, $color )
{
imageline( $gobj, $sx, $sy, $ex, $ey,
$color );
}
}
class Rectangle extends BoxObject
{
public function draw( $sx, $sy, $ex, $ey,
$gobj, $color )
{
imagefilledrectangle( $gobj, $sx, $sy,
$ex, $ey, $color );
}
}
class Oval extends BoxObject
{
public function draw( $sx, $sy, $ex, $ey,
$gobj, $color )
{
$w = $ex - $sx;
$h = $ey - $sy;
imagefilledellipse( $gobj,
$sx + ( $w / 2 ), $sy + ( $h / 2 ),
$w, $h, $color );
}
}
?>
|
GraphicsEnvironment 类中的 viewport 转换代码是高亮显示的,正如 GraphicsObject 中的 render 代码一样,这会回调图形环境来进行坐标转换的工作。
测试代码只需要稍加修改即可(请参看清单 8)。这些对象现在需要在 0,0 和 1,1 之间的 viewport 中进行指定。
清单 8. 使用新 viewport 坐标的测试代码1
2
3
| $g1 = new Group( 0 );
$g1->add( new Oval( 200, "red", 0.1, 0.1, 0.5, 0.5 ) );
$g1->add( new Rectangle( 100, "black", 0.4, 0.4, 0.9, 0.9 ) );
|
这非常不错,但是您可能实际上并不希望使用一个 0,0 与 1,1 之间的 viewport;而是希望使用任意的 viewport —— 例如,在 -1000,-1000 到 1000,1000 之间。要让这成为可能,这个图形环境就需要知道 viewport 的起点和终点。
图 8 显示了更新后的 GraphicsEnvironment 类,它具有几个成员变量,用来存储 viewport 的起点和终点坐标:vsx,vsy 和 vex,vey。图形对象并不需要进行修改。
图 8. 具有灵活 viewport 规范的图形环境清单 9 显示了更新后的 GraphicsEnvironment 代码。
清单 9. 更新后的 GraphicsEnvironment 代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| class GraphicsEnvironment
{
public $vsx;
public $vsy;
public $vex;
public $vey;
public $width;
public $height;
public $gdo;
public $colors = array();
public function __construct( $width, $height,
$vsx, $vsy, $vex, $vey )
{
$this->vsx = $vsx;
$this->vsy = $vsy;
$this->vex = $vex;
$this->vey = $vey;
$this->width = $width;
$this->height = $height;
$this->gdo = imagecreatetruecolor( $width, $height );
$this->addColor( "white", 255, 255, 255 );
imagefilledrectangle( $this->gdo, 0, 0,
$width, $height,
$this->getColor( "white" ) );
}
public function width() { return $this->width; }
public function height() { return $this->height; }
public function addColor( $name, $r, $g, $b )
{
$this->colors[ $name ] = imagecolorallocate(
$this->gdo,
$r, $g, $b );
}
public function getGraphicObject()
{
return $this->gdo;
}
public function getColor( $name )
{
return $this->colors[ $name ];
}
public function saveAsPng( $filename )
{
imagepng( $this->gdo, $filename );
}
public function tx( $x )
{
$r = $this->width / ( $this->vex - $this->vsx );
return ( $x - $this->vsx ) * $r;
}
public function ty( $y )
{
$r = $this->height / ( $this->vey - $this->vsy );
return ( $y - $this->vsy ) * $r;
}
}
|
现在这个构造函数可以利用另外 4 个参数了,它们分别是 viewport 的起点和终点。 tx 和 ty 函数使用新的 viewport 坐标,并将 viewport 坐标转换成物理坐标。
测试代码如清单 10 所示。
清单 10. viewport 测试代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <?php
require_once( "glib.php" );
$ge = new GraphicsEnvironment( 400, 400,
-1000, -1000, 1000, 1000 );
$ge->addColor( "black", 0, 0, 0 );
$ge->addColor( "red", 255, 0, 0 );
$ge->addColor( "green", 0, 255, 0 );
$ge->addColor( "blue", 0, 0, 255 );
$g1 = new Group( 0 );
$g1->add( new Oval( 200, "red", -800, -800, 0, 0 ) );
$g1->add( new Rectangle( 100, "black", -400, -400, 900, 900 ) );
$g1->render( $ge );
$ge->saveAsPng( "test.png" );
?>
|
这段测试代码会在 -1000,-1000 与 1000,000 之间创建一个 viewport。对象会被重新放置,以适合这个新的坐标系统。
测试代码的输出如图 9 所示。
图 9. viewport 绘制的图像转换为一个 400X400 的图像如果您希望图像的大小是 400X200,就可以采用下面的方法:
1
2
| $ge = new GraphicsEnvironment( 400, 200,
-1000, -1000, 1000, 1000 );
|
您会得到一个纵向缩小后的图像,如图 10 所示。
图 10. 图形的 400X200 版本这展示了代码如何自动调整图像的大小来适合所请求的图像。 |