test-pattern.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <?php
  2. require_once("pixel.php");
  3. Class XML_Node {
  4. private $properties = array();
  5. private $children = array();
  6. private $name;
  7. private $close;
  8. // server with php 5.3.0 has no '...' token
  9. //public function __construct($name = "kisses", $close = true, ... $properties) {
  10. public function __construct($name = "kisses", $close = true, $properties = array()) {
  11. //$this->add(... $properties);
  12. $this->add($properties);
  13. $this->name = $name;
  14. $this->close = $close;
  15. }
  16. public function append(XML_Node $child) { $this->children[] = $child; }
  17. //public function add(... $properties) {
  18. public function add($properties) {
  19. foreach($properties as $arr) $this->properties[] = $arr;
  20. }
  21. /* convert properties to a string, allow "late-binding", or when one wants
  22. * to set a property after initially setting it to another value, a good
  23. * example of this is a programmer "building" a wall and not knowing
  24. * beforehand what the final size of the wall is, part of the program
  25. * might calculate how tall something is based on how many bricks the
  26. * programmer used. This is sub-optimal, but at the moment I am not sure
  27. * how many bricks are needed, so unless someone can say this glut is going
  28. * to stay.
  29. *
  30. * -- better code would just build this string once rather than every
  31. * time the Class is coerced
  32. */
  33. private function propertyStr() {
  34. $str = array();
  35. foreach($this->properties as $arr) {
  36. // convert properties with values into HTML
  37. if(is_array($arr)) {
  38. // preserve native PHP datatype quotes, this way "1.0" is a string
  39. if("string" === gettype($arr[1]) || is_object($arr[1])) {
  40. $arr[1] = '"' . htmlentities($arr[1]) . '"';
  41. } else if(is_object($arr[1])) {}
  42. $arr = "{$arr[0]}={$arr[1]}";
  43. } else { /* just leave property alone */ }
  44. $str[] = $arr;
  45. }
  46. return (0<count($str) ?' ' :'') . implode(' ', $str);
  47. }
  48. public function __tostring() {
  49. $arr = array("<{$this->name}{$this->propertyStr()}>");
  50. foreach($this->children as $v) $arr[] = "\t" . strval($v);
  51. // either close comes at the end or is just a single node with terminating '/>'
  52. if($this->close) $arr[] = "</{$this->name}>";
  53. else $arr[0] = substr($arr[0], 0, -1) . " />";
  54. return implode(PHP_EOL, $arr);
  55. }
  56. static public function stanza($version = "1.0") {
  57. $s = new XML_Node("?xml", false, array(
  58. array("version", $version),
  59. array("standalone", "no")
  60. ));
  61. // XML requires '?' instead of '/' in '/>' tag terminator
  62. return substr($s, 0, -2) . '?>';
  63. }
  64. }
  65. Class Printable {
  66. public $value;
  67. public function __construct($v = 0) { $this->value = $v; }
  68. public function __tostring() { return strval($this->value); }
  69. }
  70. Class Coordinate {
  71. public $x;
  72. public $y;
  73. public function __construct($x = 0, $y = 0) {
  74. $this->x = $x;
  75. $this->y = $y;
  76. }
  77. }
  78. Class Coord extends Coordinate { /* alias */ }
  79. Class Paint {
  80. public $xml = array();
  81. private $svg;
  82. private $height;
  83. private $width;
  84. public function __construct() {
  85. $this->xml[] = XML_Node::stanza();
  86. // make references of the height and width properties for the SVG node
  87. $this->height = new Printable();
  88. $this->width = new Printable();
  89. $this->svg = new XML_Node("svg", true, array(
  90. // height and width as references
  91. array("width", /* needs to be ref */ $this->width),
  92. array("height", /* needs to be ref */ $this->height),
  93. array("version", "1.1"),
  94. array("xmlns", "http://www.w3.org/2000/svg"),
  95. ));
  96. // store a reference to the SVG, reference updates are tracked
  97. $this->xml[] = $this->svg;
  98. }
  99. private function expandCanvas($h = 0, $w = 0) {
  100. $this->height->value = max($this->height->value, $h);
  101. $this->width->value = max($this->width->value, $w);
  102. }
  103. public function addCircle(Coord $origin = NULL, Pixel $fill = NULL, $radius = 20) {
  104. if(NULL === $origin) $origin = new Coord();
  105. if(NULL === $fill) $fill = new Pixel(/*red*/ 255,0,0, 0.5);
  106. $y = $x = $radius;
  107. $circle = new XML_Node("circle", false, array(
  108. array("cx", strval($x + $origin->x)),
  109. array("cy", strval($y + $origin->y)),
  110. array("r", strval($radius)),
  111. array("fill", "#" . strval($fill))
  112. ));
  113. $this->svg->append($circle);
  114. // height needs to be radius * 2, assuming radius includes stroke
  115. $this->expandCanvas(
  116. /*height*/ $origin->y + ($y * 2),
  117. /*width*/ $origin->x + ($x * 2)
  118. );
  119. }
  120. public function __tostring() {
  121. $s = "";
  122. foreach($this->xml as $n) $s .= strval($n) . PHP_EOL;
  123. return $s;
  124. }
  125. }
  126. /*
  127. header("Content-Type: application: xhtml+xml");
  128. $test = new Paint();
  129. $test->addCircle(new Coord(0,0));
  130. $test->addCircle(new Coord(40,0), new Pixel(255,255,0));
  131. echo $test;
  132. */