Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402]
1 <?php namespace RedeyeVentures\GeoPattern; 2 3 use RedeyeVentures\GeoPattern\SVGElements\Polyline; 4 use RedeyeVentures\GeoPattern\SVGElements\Rectangle; 5 use RedeyeVentures\GeoPattern\SVGElements\Group; 6 7 class GeoPattern { 8 9 protected $string; 10 protected $baseColor; 11 protected $color; 12 protected $generator; 13 14 protected $hash; 15 protected $svg; 16 17 protected $patterns = [ 18 'octogons', 19 'overlapping_circles', 20 'plus_signs', 21 'xes', 22 'sine_waves', 23 'hexagons', 24 'overlapping_rings', 25 'plaid', 26 'triangles', 27 'squares', 28 'concentric_circles', 29 'diamonds', 30 'tessellation', 31 'nested_squares', 32 'mosaic_squares', 33 'triangles_rotated', 34 'chevrons', 35 ]; 36 const FILL_COLOR_DARK = '#222'; 37 const FILL_COLOR_LIGHT = '#ddd'; 38 const STROKE_COLOR = '#000'; 39 const STROKE_OPACITY = '0.02'; 40 const OPACITY_MIN = '0.02'; 41 const OPACITY_MAX = '0.15'; 42 43 function __construct($options=array()) 44 { 45 // Set string if provided. If not, set default. 46 if (isset($options['string'])) { 47 $this->setString($options['string']); 48 } else { 49 $this->setString(time()); 50 } 51 52 // Set base color if provided. If not, set default. 53 if (isset($options['baseColor'])) { 54 $this->setBaseColor($options['baseColor']); 55 } else { 56 $this->setBaseColor('#933c3c'); 57 } 58 59 // Set color if provided. 60 if (isset($options['color'])) { 61 $this->setColor($options['color']); 62 } 63 64 // Set generator if provided. If not, leave null. 65 if (isset($options['generator'])) 66 $this->setGenerator($options['generator']); 67 68 $this->svg = new SVG(); 69 } 70 71 // Fluent Interfaces 72 public function setString($string) 73 { 74 $this->string = $string; 75 $this->hash = sha1($this->string); 76 return $this; 77 } 78 79 /** 80 * @return string 81 */ 82 public function getString() 83 { 84 return $this->string; 85 } 86 87 public function setBaseColor($baseColor) 88 { 89 if(preg_match('/^#[a-f0-9]{6}$/i', $baseColor)) //hex color is valid 90 { 91 $this->baseColor = $baseColor; 92 return $this; 93 } 94 throw new \InvalidArgumentException("$baseColor is not a valid hex color."); 95 } 96 97 public function setColor($color) 98 { 99 if(preg_match('/^#[a-f0-9]{6}$/i', $color)) //hex color is valid 100 { 101 $this->color = $color; 102 return $this; 103 } 104 throw new \InvalidArgumentException("$color is not a valid hex color."); 105 } 106 107 public function setGenerator($generator) 108 { 109 $generator = strtolower($generator); 110 if (in_array($generator, $this->patterns) || is_null($generator)) { 111 $this->generator = $generator; 112 return $this; 113 } 114 throw new \InvalidArgumentException("$generator is not a valid generator type."); 115 } 116 117 public function toSVG() 118 { 119 $this->svg = new SVG(); 120 $this->generateBackground(); 121 $this->generatePattern(); 122 return (string) $this->svg; 123 } 124 125 public function toBase64() 126 { 127 return base64_encode($this->toSVG()); 128 } 129 130 public function toDataURI() 131 { 132 return "data:image/svg+xml;base64,{$this->toBase64()}"; 133 } 134 135 public function toDataURL() 136 { 137 return "url(\"{$this->toDataURI()}\")"; 138 } 139 140 public function __toString() { 141 return $this->toSVG(); 142 } 143 144 // Generators 145 protected function generateBackground() 146 { 147 $hueOffset = $this->map($this->hexVal(14, 3), 0, 4095, 0, 359); 148 $satOffset = $this->hexVal(17, 1); 149 $baseColor = $this->hexToHSL($this->baseColor); 150 $color = $this->color; 151 152 $baseColor['h'] = $baseColor['h'] - $hueOffset; 153 154 155 if ($satOffset % 2 == 0) 156 $baseColor['s'] = $baseColor['s'] + $satOffset/100; 157 else 158 $baseColor['s'] = $baseColor['s'] - $satOffset/100; 159 160 if (isset($color)) 161 $rgb = $this->hexToRGB($color); 162 else 163 $rgb = $this->hslToRGB($baseColor['h'], $baseColor['s'], $baseColor['l']); 164 165 $this->svg->addRectangle(0, 0, "100%", "100%", ['fill' => "rgb({$rgb['r']}, {$rgb['g']}, {$rgb['b']})"]); 166 } 167 168 protected function generatePattern() 169 { 170 if (is_null($this->generator)) 171 $pattern = $this->patterns[$this->hexVal(20, 1)]; 172 else 173 $pattern = $this->generator; 174 175 $function = 'geo'.str_replace(' ', '', ucwords(str_replace('_', ' ', $pattern))); 176 177 if (method_exists($this, $function)) 178 $this->$function(); 179 } 180 181 // Pattern Makers 182 protected function geoHexagons() 183 { 184 $scale = $this->hexVal(0, 1); 185 $sideLength = $this->map($scale, 0, 15, 8, 60); 186 $hexHeight = $sideLength * sqrt(3); 187 $hexWidth = $sideLength * 2; 188 $hex = $this->buildHexagonShape($sideLength); 189 $this->svg->setWidth(($hexWidth * 3) + ($sideLength * 3)) 190 ->setHeight($hexHeight * 6); 191 192 $i = 0; 193 for ($y = 0; $y <= 5; $y++) { 194 for ($x = 0; $x <= 5; $x++) { 195 $val = $this->hexVal($i, 1); 196 $dy = ($x % 2 == 0) ? ($y * $hexHeight) : ($y*$hexHeight + $hexHeight / 2); 197 $opacity = $this->opacity($val); 198 $fill = $this->fillColor($val); 199 $styles = [ 200 'stroke' => self::STROKE_COLOR, 201 'stroke-opacity' => self::STROKE_OPACITY, 202 'fill-opacity' => $opacity, 203 'fill' => $fill, 204 ]; 205 206 $onePointFiveXSideLengthMinusHalfHexWidth = $x * $sideLength * 1.5 - $hexWidth / 2; 207 $dyMinusHalfHexHeight = $dy - $hexHeight / 2; 208 $this->svg->addPolyline($hex, array_merge($styles, ['transform' => "translate($onePointFiveXSideLengthMinusHalfHexWidth, $dyMinusHalfHexHeight)"])); 209 210 // Add an extra one at top-right, for tiling. 211 if ($x == 0) { 212 $onePointFiveSideLengthSixMinusHalfHexWidth = 6 * $sideLength * 1.5 - $hexWidth / 2; 213 $this->svg->addPolyline($hex, array_merge($styles, ['transform' => "translate($onePointFiveSideLengthSixMinusHalfHexWidth, $dyMinusHalfHexHeight)"])); 214 } 215 216 // Add an extra row at the end that matches the first row, for tiling. 217 if ($y == 0) { 218 $dy2 = ($x % 2 == 0) ? (6 * $hexHeight) : (6 * $hexHeight + $hexHeight / 2); 219 $dy2MinusHalfHexHeight = $dy2 - $hexHeight / 2; 220 $this->svg->addPolyline($hex, array_merge($styles, ['transform' => "translate($onePointFiveXSideLengthMinusHalfHexWidth, $dy2MinusHalfHexHeight)"])); 221 } 222 223 // Add an extra one at bottom-right, for tiling. 224 if ($x == 0 && $y == 0) { 225 $onePointFiveSideLengthSixMinusHalfHexWidth = 6 * $sideLength * 1.5 - $hexWidth / 2; 226 $fiveHexHeightPlusHalfHexHeight = 5 * $hexHeight + $hexHeight / 2; 227 $this->svg->addPolyline($hex, array_merge($styles, ['transform' => "translate($onePointFiveSideLengthSixMinusHalfHexWidth, $fiveHexHeightPlusHalfHexHeight)"])); 228 } 229 230 $i++; 231 } 232 } 233 } 234 235 protected function geoSineWaves() 236 { 237 $period = floor($this->map($this->hexVal(0, 1), 0, 15, 100, 400)); 238 $quarterPeriod = $period / 4; 239 $xOffset = $period / 4 * 0.7; 240 $amplitude = floor($this->map($this->hexVal(1, 1), 0, 15, 30, 100)); 241 $waveWidth = floor($this->map($this->hexVal(2, 1), 0, 15, 3, 30)); 242 $amplitudeString = number_format($amplitude); 243 $halfPeriod = number_format($period / 2); 244 $halfPeriodMinusXOffset = number_format($period / 2 - $xOffset); 245 $periodMinusXOffset = number_format($period - $xOffset); 246 $twoAmplitude = number_format(2 * $amplitude); 247 $onePointFivePeriodMinusXOffset = number_format($period * 1.5 - $xOffset); 248 $onePointFivePeriod = number_format($period * 1.5); 249 $str = "M0 $amplitudeString C $xOffset 0, $halfPeriodMinusXOffset 0, $halfPeriod $amplitudeString S $periodMinusXOffset $twoAmplitude, $period $amplitudeString S $onePointFivePeriodMinusXOffset 0, $onePointFivePeriod, $amplitudeString"; 250 251 $this->svg->setWidth($period) 252 ->setHeight($waveWidth*36); 253 for ($i = 0; $i <= 35; $i++) { 254 $val = $this->hexVal($i, 1); 255 $opacity = $this->opacity($val); 256 $fill = $this->fillColor($val); 257 $styles = [ 258 'fill' => 'none', 259 'stroke' => $fill, 260 'style' => [ 261 'opacity' => $opacity, 262 'stroke-width' => "{$waveWidth}px" 263 ] 264 ]; 265 266 $iWaveWidthMinusOnePointFiveAmplitude = $waveWidth * $i - $amplitude * 1.5; 267 $iWaveWidthMinusOnePointFiveAmplitudePlusThirtySixWaveWidth = $waveWidth * $i - $amplitude * 1.5 + $waveWidth * 36; 268 $this->svg->addPath($str, array_merge($styles, ['transform' => "translate(-$quarterPeriod, $iWaveWidthMinusOnePointFiveAmplitude)"])); 269 $this->svg->addPath($str, array_merge($styles, ['transform' => "translate(-$quarterPeriod, $iWaveWidthMinusOnePointFiveAmplitudePlusThirtySixWaveWidth)"])); 270 271 } 272 } 273 274 protected function geoChevrons() 275 { 276 $chevronWidth = $this->map($this->hexVal(0, 1), 0, 15, 30, 80); 277 $chevronHeight = $this->map($this->hexVal(0, 1), 0, 15, 30, 80); 278 $chevron = $this->buildChevronShape($chevronWidth, $chevronHeight); 279 280 $this->svg->setWidth($chevronWidth*6) 281 ->setHeight($chevronHeight*6*0.66); 282 283 $i = 0; 284 for ($y = 0; $y <= 5; $y++) { 285 for ($x = 0; $x <= 5; $x++) { 286 $val = $this->hexVal($i, 1); 287 $opacity = $this->opacity($val); 288 $fill = $this->fillColor($val); 289 $styles = [ 290 'stroke' => self::STROKE_COLOR, 291 'stroke-opacity' => self::STROKE_OPACITY, 292 'stroke-width' => '1', 293 'fill-opacity' => $opacity, 294 'fill' => $fill, 295 ]; 296 297 $group = new Group(); 298 $group->addItem($chevron[0]) 299 ->addItem($chevron[1]); 300 301 $xChevronWidth = $x * $chevronWidth; 302 $yPointSixSixChevronHeightMinusHalfChevronHeight = $y * $chevronHeight * 0.66 - $chevronHeight / 2; 303 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xChevronWidth,$yPointSixSixChevronHeightMinusHalfChevronHeight)"])); 304 // Add an extra row at the end that matches the first row, for tiling. 305 if ($y == 0) { 306 $sixPointSixSixChevronHeightMinusHalfChevronHeight = 6 * $chevronHeight * 0.66 - $chevronHeight / 2; 307 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xChevronWidth,$sixPointSixSixChevronHeightMinusHalfChevronHeight)"])); 308 } 309 310 $i++; 311 } 312 } 313 314 } 315 316 protected function geoPlusSigns() 317 { 318 $squareSize = $this->map($this->hexVal(0, 1), 0, 15, 10, 25); 319 $plusSize = $squareSize * 3; 320 $plusShape = $this->buildPlusShape($squareSize); 321 322 $this->svg->setWidth($squareSize*12) 323 ->setHeight($squareSize*12); 324 325 $i = 0; 326 for ($y = 0; $y <= 5; $y++) { 327 for ($x = 0; $x <= 5; $x++) { 328 $val = $this->hexVal($i, 1); 329 $opacity = $this->opacity($val); 330 $fill = $this->fillColor($val); 331 $dx = ($y % 2 == 0) ? 0 : 1; 332 333 $styles = [ 334 'fill' => $fill, 335 'stroke' => self::STROKE_COLOR, 336 'stroke-opacity' => self::STROKE_OPACITY, 337 'style' => [ 338 'fill-opacity' => $opacity, 339 ], 340 ]; 341 342 $group = new Group(); 343 $group->addItem($plusShape[0]) 344 ->addItem($plusShape[1]); 345 346 $t1 = $x * $plusSize - $x * $squareSize + $dx * $squareSize - $squareSize; 347 $t2 = $y * $plusSize - $y * $squareSize - $plusSize / 2; 348 349 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($t1, $t2)"])); 350 351 // Add an extra column on the right for tiling. 352 if ($x == 0) { 353 $xT1 = 4 * $plusSize - $x * $squareSize + $dx * $squareSize - $squareSize; 354 $xT2 = $y * $plusSize - $y * $squareSize - $plusSize / 2; 355 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xT1, $xT2)"])); 356 } 357 358 // Add an extra row on the bottom that matches the first row, for tiling. 359 if ($y == 0) { 360 $yT1 = $x * $plusSize - $x * $squareSize + $dx * $squareSize - $squareSize; 361 $yT2 = 4 * $plusSize - $y * $squareSize - $plusSize /2; 362 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($yT1, $yT2)"])); 363 } 364 365 // Add an extra one at top-right and bottom-right, for tiling. 366 if ($x == 0 && $y == 0) { 367 $xyT1 = 4 * $plusSize - $x * $squareSize + $dx * $squareSize - $squareSize; 368 $xyT2 = 4 * $plusSize - $y * $squareSize - $plusSize / 2; 369 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xyT1, $xyT2)"])); 370 } 371 372 $i++; 373 } 374 } 375 } 376 377 protected function geoXes() 378 { 379 $squareSize = $this->map($this->hexVal(0, 1), 0, 15, 10, 25); 380 $xSize = $squareSize * 3 * 0.943; 381 $xShape = $this->buildPlusShape($squareSize); 382 383 $this->svg->setWidth($xSize*3) 384 ->setHeight($xSize*3); 385 386 $i = 0; 387 for ($y = 0; $y <= 5; $y++) { 388 for ($x = 0; $x <= 5; $x++) { 389 $val = $this->hexVal($i, 1); 390 $opacity = $this->opacity($val); 391 $fill = $this->fillColor($val); 392 $dy = ($x % 2 == 0) ? ($y * $xSize - $xSize * 0.5) : ($y * $xSize - $xSize * 0.5 + $xSize / 4); 393 394 $styles = [ 395 'fill' => $fill, 396 'style' => [ 397 'opacity' => $opacity, 398 ], 399 ]; 400 401 $group = new Group(); 402 $group->addItem($xShape[0]) 403 ->addItem($xShape[1]); 404 405 $t1 = $x * $xSize / 2 - $xSize / 2; 406 $t2 = $dy - $y * $xSize / 2; 407 $halfXSize = $xSize / 2; 408 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($t1, $t2) rotate(45, $halfXSize, $halfXSize)"])); 409 410 // Add an extra column on the right for tiling. 411 if ($x == 0) { 412 $xT1 = 6 * $xSize / 2 - $xSize / 2; 413 $xT2 = $dy - $y * $xSize / 2; 414 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xT1, $xT2) rotate(45, $halfXSize, $halfXSize)"])); 415 } 416 417 // Add an extra row on the bottom that matches the first row, for tiling. 418 if ($y == 0) { 419 $dy = ($x % 2 == 0) ? (6 * $xSize - $xSize / 2) : (6 * $xSize - $xSize / 2 + $xSize / 4); 420 $yT1 = $x * $xSize / 2 - $xSize / 2; 421 $yT2 = $dy - 6 * $xSize / 2; 422 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($yT1, $yT2) rotate(45, $halfXSize, $halfXSize)"])); 423 } 424 425 // These can hang off the bottom, so put a row at the top for tiling. 426 if ($y == 5) { 427 $y2T1 = $x * $xSize / 2 - $xSize / 2; 428 $y2T2 = $dy - 11 * $xSize / 2; 429 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($y2T1, $y2T2) rotate(45, $halfXSize, $halfXSize)"])); 430 } 431 432 // Add an extra one at top-right and bottom-right, for tiling. 433 if ($x == 0 && $y == 0) { 434 $xyT1 = 6 * $xSize / 2 - $xSize / 2; 435 $xyT2 = $dy - 6 * $xSize / 2; 436 $this->svg->addGroup($group, array_merge($styles, ['transform' => "translate($xyT1, $xyT2) rotate(45, $halfXSize, $halfXSize)"])); 437 } 438 439 $i++; 440 } 441 } 442 } 443 444 protected function geoOverlappingCircles() 445 { 446 $scale = $this->hexVal(0, 1); 447 $diameter = $this->map($scale, 0, 15, 25, 200); 448 $radius = $diameter/2; 449 450 $this->svg->setWidth($radius*6) 451 ->setHeight($radius*6); 452 453 $i = 0; 454 for ($y = 0; $y <= 5; $y++) { 455 for ($x = 0; $x <= 5; $x++) { 456 $val = $this->hexVal($i, 1); 457 $opacity = $this->opacity($val); 458 $fill = $this->fillColor($val); 459 $styles = [ 460 'fill' => $fill, 461 'style' => [ 462 'opacity' => $opacity, 463 ], 464 ]; 465 466 $this->svg->addCircle($x*$radius, $y*$radius, $radius, $styles); 467 468 // Add an extra one at top-right, for tiling. 469 if ($x == 0) 470 $this->svg->addCircle(6*$radius, $y*$radius, $radius, $styles); 471 472 // Add an extra row at the end that matches the first row, for tiling. 473 if ($y == 0) 474 $this->svg->addCircle($x*$radius, 6*$radius, $radius, $styles); 475 476 // Add an extra one at bottom-right, for tiling. 477 if ($x == 0 && $y == 0) 478 $this->svg->addCircle(6*$radius, 6*$radius, $radius, $styles); 479 480 $i++; 481 } 482 } 483 } 484 485 protected function geoOctogons() 486 { 487 $squareSize = $this->map($this->hexVal(0, 1), 0, 15, 10, 60); 488 $tile = $this->buildOctogonShape($squareSize); 489 490 $this->svg->setWidth($squareSize*6) 491 ->setHeight($squareSize*6); 492 493 $i = 0; 494 for ($y = 0; $y <= 5; $y++) { 495 for ($x = 0; $x <= 5; $x++) { 496 $val = $this->hexVal($i, 1); 497 $opacity = $this->opacity($val); 498 $fill = $this->fillColor($val); 499 500 $xSquareSize = $x * $squareSize; 501 $ySquareSize = $y * $squareSize; 502 503 $this->svg->addPolyline($tile, [ 504 'fill' => $fill, 505 'fill-opacity' => $opacity, 506 'stroke' => self::STROKE_COLOR, 507 'stroke-opacity' => self::STROKE_OPACITY, 508 'transform' => "translate($xSquareSize, $ySquareSize)", 509 ]); 510 511 $i++; 512 } 513 } 514 515 } 516 517 protected function geoSquares() 518 { 519 $squareSize = $this->map($this->hexVal(0, 1), 0, 15, 10, 60); 520 521 $this->svg->setWidth($squareSize*6) 522 ->setHeight($squareSize*6); 523 524 $i = 0; 525 for ($y = 0; $y <= 5; $y++) { 526 for ($x = 0; $x <= 5; $x++) { 527 $val = $this->hexVal($i, 1); 528 $opacity = $this->opacity($val); 529 $fill = $this->fillColor($val); 530 531 $this->svg->addRectangle($x*$squareSize, $y*$squareSize, $squareSize, $squareSize, [ 532 'fill' => $fill, 533 'fill-opacity' => $opacity, 534 'stroke' => self::STROKE_COLOR, 535 'stroke-opacity' => self::STROKE_OPACITY, 536 ]); 537 538 $i++; 539 } 540 } 541 542 } 543 544 protected function geoConcentricCircles() 545 { 546 $scale = $this->hexVal(0, 1); 547 $ringSize = $this->map($scale, 0, 15, 10, 60); 548 $strokeWidth = $ringSize / 5; 549 550 $this->svg->setWidth(($ringSize + $strokeWidth)*6) 551 ->setHeight(($ringSize + $strokeWidth)*6); 552 553 $i = 0; 554 for ($y = 0; $y <= 5; $y++) { 555 for ($x = 0; $x <= 5; $x++) { 556 $val = $this->hexVal($i, 1); 557 $opacity = $this->opacity($val); 558 $fill = $this->fillColor($val); 559 560 $cx = $x * $ringSize + $x * $strokeWidth + ($ringSize + $strokeWidth) / 2; 561 $cy = $y * $ringSize + $y * $strokeWidth + ($ringSize + $strokeWidth) / 2; 562 $halfRingSize = $ringSize / 2; 563 564 $this->svg->addCircle($cx, $cy, $halfRingSize, [ 565 'fill' => 'none', 566 'stroke' => $fill, 567 'style' => [ 568 'opacity' => $opacity, 569 'stroke-width' => "{$strokeWidth}px", 570 ], 571 ]); 572 573 $val = $this->hexVal(39-$i, 1); 574 $opacity = $this->opacity($val); 575 $fill = $this->fillColor($val); 576 577 $quarterRingSize = $ringSize / 4; 578 579 $this->svg->addCircle($cx, $cy, $quarterRingSize, [ 580 'fill' => $fill, 581 'fill-opacity' => $opacity, 582 ]); 583 584 $i++; 585 } 586 } 587 } 588 589 protected function geoOverlappingRings() 590 { 591 $scale = $this->hexVal(0, 1); 592 $ringSize = $this->map($scale, 0, 15, 10, 60); 593 $strokeWidth = $ringSize / 4; 594 595 $this->svg->setWidth($ringSize*6) 596 ->setHeight($ringSize*6); 597 598 $i = 0; 599 for ($y = 0; $y <= 5; $y++) { 600 for ($x = 0; $x <= 5; $x++) { 601 $val = $this->hexVal($i, 1); 602 $opacity = $this->opacity($val); 603 $fill = $this->fillColor($val); 604 605 $styles = [ 606 'fill' => 'none', 607 'stroke' => $fill, 608 'style' => [ 609 'opacity' => $opacity, 610 'stroke-width' => "{$strokeWidth}px", 611 ], 612 ]; 613 614 $ringSizeMinusHalfStrokeWidth = $ringSize - $strokeWidth / 2; 615 616 $this->svg->addCircle($x*$ringSize, $y*$ringSize, $ringSizeMinusHalfStrokeWidth, $styles); 617 618 // Add an extra one at top-right, for tiling. 619 if ($x == 0) 620 $this->svg->addCircle(6*$ringSize, $y*$ringSize, $ringSizeMinusHalfStrokeWidth, $styles); 621 622 // Add an extra row at the end that matches the first row, for tiling. 623 if ($y == 0) 624 $this->svg->addCircle($x*$ringSize, 6*$ringSize, $ringSizeMinusHalfStrokeWidth, $styles); 625 626 // Add an extra one at bottom-right, for tiling. 627 if ($x == 0 && $y == 0) 628 $this->svg->addCircle(6*$ringSize, 6*$ringSize, $ringSizeMinusHalfStrokeWidth, $styles); 629 630 $i++; 631 } 632 } 633 } 634 635 protected function geoTriangles() 636 { 637 $scale = $this->hexVal(0, 1); 638 $sideLength = $this->map($scale, 0 ,15, 15, 80); 639 $triangleHeight = $sideLength / 2 * sqrt(3); 640 $triangle = $this->buildTriangleShape($sideLength, $triangleHeight); 641 642 $this->svg->setWidth($sideLength * 3) 643 ->setHeight($triangleHeight * 6); 644 645 $i = 0; 646 for ($y = 0; $y <= 5; $y++) { 647 for ($x = 0; $x <= 5; $x++) { 648 $val = $this->hexVal($i, 1); 649 $opacity = $this->opacity($val); 650 $fill = $this->fillColor($val); 651 652 $styles = [ 653 'fill' => $fill, 654 'fill-opacity' => $opacity, 655 'stroke' => self::STROKE_COLOR, 656 'stroke-opacity' => self::STROKE_OPACITY, 657 ]; 658 659 $rotation = ''; 660 if ($y % 2 == 0) 661 $rotation = ($x % 2 == 0) ? 180 : 0; 662 else 663 $rotation = ($x % 2 != 0) ? 180 : 0; 664 665 $halfSideLength = $sideLength / 2; 666 $halfTriangleHeight = $triangleHeight / 2; 667 $yTriangleHeight = $triangleHeight * $y; 668 669 $t1 = $x * $sideLength * 0.5 - $sideLength / 2; 670 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($t1, $yTriangleHeight) rotate($rotation, $halfSideLength, $halfTriangleHeight)"])); 671 672 // Add an extra one at top-right, for tiling. 673 if ($x == 0) 674 { 675 $xT1 = 6 * $sideLength * 0.5 - $sideLength / 2; 676 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xT1, $yTriangleHeight) rotate($rotation, $halfSideLength, $halfTriangleHeight)"])); 677 } 678 679 $i++; 680 } 681 } 682 683 } 684 685 protected function geoTrianglesRotated() 686 { 687 $scale = $this->hexVal(0, 1); 688 $sideLength = $this->map($scale, 0 ,15, 15, 80); 689 $triangleWidth = $sideLength / 2 * sqrt(3); 690 $triangle = $this->buildRotatedTriangleShape($sideLength, $triangleWidth); 691 692 $this->svg->setWidth($triangleWidth * 6) 693 ->setHeight($sideLength * 3); 694 695 $i = 0; 696 for ($y = 0; $y <= 5; $y++) { 697 for ($x = 0; $x <= 5; $x++) { 698 $val = $this->hexVal($i, 1); 699 $opacity = $this->opacity($val); 700 $fill = $this->fillColor($val); 701 702 $styles = [ 703 'fill' => $fill, 704 'fill-opacity' => $opacity, 705 'stroke' => self::STROKE_COLOR, 706 'stroke-opacity' => self::STROKE_OPACITY, 707 ]; 708 709 $rotation = ''; 710 if ($y % 2 == 0) 711 $rotation = ($x % 2 == 0) ? 180 : 0; 712 else 713 $rotation = ($x % 2 != 0) ? 180 : 0; 714 715 $halfSideLength = $sideLength / 2; 716 $halfTriangleWidth = $triangleWidth / 2; 717 $xTriangleWidth = $x * $triangleWidth; 718 719 $t1 = $y * $sideLength * 0.5 - $sideLength / 2; 720 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xTriangleWidth, $t1) rotate($rotation, $halfTriangleWidth, $halfSideLength)"])); 721 722 // Add an extra one at top-right, for tiling. 723 if ($y == 0) 724 { 725 $yT1 = 6 * $sideLength * 0.5 - $sideLength / 2; 726 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xTriangleWidth, $yT1) rotate($rotation, $halfTriangleWidth, $halfSideLength)"])); 727 } 728 729 $i++; 730 } 731 } 732 733 } 734 735 protected function geoDiamonds() 736 { 737 $diamondWidth = $this->map($this->hexVal(0, 1), 0, 15, 10, 50); 738 $diamondHeight = $this->map($this->hexVal(1, 1), 0, 15, 10, 50); 739 $diamond = $this->buildDiamondShape($diamondWidth, $diamondHeight); 740 741 $this->svg->setWidth($diamondWidth*6) 742 ->setHeight($diamondHeight*3); 743 744 $i = 0; 745 for ($y = 0; $y <= 5; $y++) { 746 for ($x = 0; $x <= 5; $x++) { 747 $val = $this->hexVal($i, 1); 748 $opacity = $this->opacity($val); 749 $fill = $this->fillColor($val); 750 751 $styles = [ 752 'fill' => $fill, 753 'fill-opacity' => $opacity, 754 'stroke' => self::STROKE_COLOR, 755 'stroke-opacity' => self::STROKE_OPACITY, 756 ]; 757 758 $dx = ($y % 2 == 0) ? 0 : ($diamondWidth / 2); 759 760 $t1 = $x * $diamondWidth - $diamondWidth / 2 + $dx; 761 $t2 = $diamondHeight / 2 * $y - $diamondHeight / 2; 762 $this->svg->addPolyline($diamond, array_merge($styles, ['transform' => "translate($t1, $t2)"])); 763 764 // Add an extra one at top-right, for tiling. 765 if ($x == 0) 766 { 767 $xT1 = 6 * $diamondWidth - $diamondWidth / 2 + $dx; 768 $xT2 = $diamondHeight / 2 * $y - $diamondHeight / 2; 769 $this->svg->addPolyline($diamond, array_merge($styles, ['transform' => "translate($xT1, $xT2)"])); 770 } 771 772 // Add an extra row at the end that matches the first row, for tiling. 773 if ($y == 0) 774 { 775 $yT1 = $x * $diamondWidth - $diamondWidth / 2 + $dx; 776 $yT2 = $diamondHeight / 2 * 6 - $diamondHeight / 2; 777 $this->svg->addPolyline($diamond, array_merge($styles, ['transform' => "translate($yT1, $yT2)"])); 778 } 779 780 // Add an extra one at bottom-right, for tiling. 781 if ($x == 0 && $y == 0) 782 { 783 $xyT1 = 6 * $diamondWidth - $diamondWidth / 2 + $dx; 784 $xyT2 = $diamondHeight / 2 * 6 - $diamondHeight / 2; 785 $this->svg->addPolyline($diamond, array_merge($styles, ['transform' => "translate($xyT1, $xyT2)"])); 786 } 787 788 $i++; 789 } 790 } 791 } 792 793 protected function geoNestedSquares() 794 { 795 $blockSize = $this->map($this->hexVal(0, 1), 0, 15, 4, 12); 796 $squareSize = $blockSize * 7; 797 $dimension = ($squareSize + $blockSize) * 6 + $blockSize * 6; 798 799 $this->svg->setWidth($dimension) 800 ->setHeight($dimension); 801 802 $i = 0; 803 for ($y = 0; $y <= 5; $y++) { 804 for ($x = 0; $x <= 5; $x++) { 805 $val = $this->hexVal($i, 1); 806 $opacity = $this->opacity($val); 807 $fill = $this->fillColor($val); 808 809 $styles = [ 810 'fill' => 'none', 811 'stroke' => $fill, 812 'style' => [ 813 'opacity' => $opacity, 814 'stroke-width' => "{$blockSize}px", 815 ], 816 ]; 817 818 $rX = $x * $squareSize + $x * $blockSize * 2 + $blockSize / 2; 819 $rY = $y * $squareSize + $y * $blockSize * 2 + $blockSize / 2; 820 821 $this->svg->addRectangle($rX, $rY, $squareSize, $squareSize, $styles); 822 823 $val = $this->hexVal(39-$i, 1); 824 $opacity = $this->opacity($val); 825 $fill = $this->fillColor($val); 826 827 $styles = [ 828 'fill' => 'none', 829 'stroke' => $fill, 830 'style' => [ 831 'opacity' => $opacity, 832 'stroke-width' => "{$blockSize}px", 833 ], 834 ]; 835 836 $rX2 = $x * $squareSize + $x * $blockSize * 2 + $blockSize / 2 + $blockSize * 2; 837 $rY2 = $y * $squareSize + $y * $blockSize * 2 + $blockSize / 2 + $blockSize * 2; 838 839 $this->svg->addRectangle($rX2, $rY2, $blockSize * 3, $blockSize * 3, $styles); 840 841 $i++; 842 } 843 } 844 } 845 846 protected function geoMosaicSquares() 847 { 848 $triangleSize = $this->map($this->hexVal(0, 1), 0, 15, 15, 50); 849 850 $this->svg->setWidth($triangleSize*8) 851 ->setHeight($triangleSize*8); 852 853 $i = 0; 854 for ($y = 0; $y <= 3; $y++) { 855 for ($x = 0; $x <= 3; $x++) { 856 if ($x % 2 == 0) 857 { 858 if ($y % 2 == 0) 859 $this->drawOuterMosaicTile($x*$triangleSize*2, $y*$triangleSize*2, $triangleSize, $this->hexVal($i, 1)); 860 else 861 $this->drawInnerMosaicTile($x*$triangleSize*2, $y*$triangleSize*2, $triangleSize, [$this->hexVal($i, 1), $this->hexVal($i+1, 1)]); 862 } 863 else 864 { 865 if ($y % 2 == 0) 866 $this->drawInnerMosaicTile($x*$triangleSize*2, $y*$triangleSize*2, $triangleSize, [$this->hexVal($i, 1), $this->hexVal($i+1, 1)]); 867 else 868 $this->drawOuterMosaicTile($x*$triangleSize*2, $y*$triangleSize*2, $triangleSize, $this->hexVal($i, 1)); 869 } 870 $i++; 871 } 872 } 873 874 } 875 876 protected function geoPlaid() 877 { 878 $height = 0; 879 $width = 0; 880 881 // Horizontal Stripes 882 $i = 0; 883 $times = 0; 884 while ($times++ <= 18) 885 { 886 $space = $this->hexVal($i, 1); 887 $height += $space + 5; 888 889 $val = $this->hexVal($i+1, 1); 890 $opacity = $this->opacity($val); 891 $fill = $this->fillColor($val); 892 $stripeHeight = $val + 5; 893 894 $this->svg->addRectangle(0, $height, "100%", $stripeHeight, [ 895 'opacity' => $opacity, 896 'fill' => $fill, 897 ]); 898 $height += $stripeHeight; 899 $i += 2; 900 } 901 902 // Vertical Stripes 903 $i = 0; 904 $times = 0; 905 while ($times++ <= 18) 906 { 907 $space = $this->hexVal($i, 1); 908 $width += $space + 5; 909 910 $val = $this->hexVal($i+1, 1); 911 $opacity = $this->opacity($val); 912 $fill = $this->fillColor($val); 913 $stripeWidth = $val + 5; 914 915 $this->svg->addRectangle($width, 0, $stripeWidth, "100%", [ 916 'opacity' => $opacity, 917 'fill' => $fill, 918 ]); 919 $width += $stripeWidth; 920 $i += 2; 921 } 922 923 $this->svg->setWidth($width) 924 ->setHeight($height); 925 926 } 927 928 protected function geoTessellation() 929 { 930 $sideLength = $this->map($this->hexVal(0, 1), 0, 15, 5, 40); 931 $hexHeight = $sideLength * sqrt(3); 932 $hexWidth = $sideLength * 2; 933 $triangleHeight = $sideLength / 2 * sqrt(3); 934 $triangle = $this->buildRotatedTriangleShape($sideLength, $triangleHeight); 935 $tileWidth = $sideLength * 3 + $triangleHeight * 2; 936 $tileHeight = ($hexHeight * 2) + ($sideLength * 2); 937 938 $this->svg->setWidth($tileWidth) 939 ->setHeight($tileHeight); 940 941 // Doing these variables up here, so we only have to calculate them once. 942 $halfSideLength = $sideLength / 2; 943 $negativeHalfSideLength = -$sideLength / 2; 944 $halfTriangleHeight = $triangleHeight / 2; 945 $halfHexHeight = $hexHeight / 2; 946 $tileHeightPlusHalfSideLength = $tileHeight + $sideLength / 2; 947 $halfTileHeightMinusHalfSideLength = $tileHeight / 2 - $sideLength / 2; 948 $halfTileWidthPlusHalfSideLength = $tileWidth / 2 + $sideLength / 2; 949 $tileWidthMinusHalfTileWidthMinusHalfSideLength = $tileWidth - $tileWidth/2 - $sideLength/2; 950 $tileWidthMinusHalfSideLength = $tileWidth - $sideLength / 2; 951 $tileHeightMinusHalfHexHeight = $tileHeight - $hexHeight / 2; 952 $negativeTileWidthPlusHalfSideLength = -$tileWidth + $sideLength / 2; 953 $halfTileHeightMinusHalfSideLengthMinusSideLength = $tileHeight/2-$sideLength/2-$sideLength; 954 $negativeTileHeightPlusHalfTileHeightMinusHalfSideLengthMinusSideLength = -$tileHeight+$tileHeight/2-$sideLength/2-$sideLength; 955 $negativeTileHeightPlusHalfSideLength = -$tileHeight + $sideLength / 2; 956 for ($i = 0; $i <= 19; $i++) { 957 $val = $this->hexVal($i, 1); 958 $opacity = $this->opacity($val); 959 $fill = $this->fillColor($val); 960 961 $styles = [ 962 'stroke' => self::STROKE_COLOR, 963 'stroke-opacity' => self::STROKE_OPACITY, 964 'fill' => $fill, 965 'fill-opacity' => $opacity, 966 'stroke-width' => 1, 967 ]; 968 969 switch ($i) { 970 case 0: # all 4 corners 971 $this->svg->addRectangle(-$sideLength/2, -$sideLength/2, $sideLength, $sideLength, $styles); 972 $this->svg->addRectangle($tileWidth-$sideLength/2, -$sideLength/2, $sideLength, $sideLength, $styles); 973 $this->svg->addRectangle(-$sideLength/2, $tileHeight-$sideLength/2, $sideLength, $sideLength, $styles); 974 $this->svg->addRectangle($tileWidth-$sideLength/2, $tileHeight-$sideLength/2, $sideLength, $sideLength, $styles); 975 break; 976 case 1: # center / top square 977 $this->svg->addRectangle($hexWidth/2+$triangleHeight, $hexHeight/2, $sideLength, $sideLength, $styles); 978 break; 979 case 2: # side squares 980 $this->svg->addRectangle(-$sideLength/2, $tileHeight/2-$sideLength/2, $sideLength, $sideLength, $styles); 981 $this->svg->addRectangle($tileWidth-$sideLength/2, $tileHeight/2-$sideLength/2, $sideLength, $sideLength, $styles); 982 break; 983 case 3: # center / bottom square 984 $this->svg->addRectangle($hexWidth/2+$triangleHeight, $hexHeight*1.5+$sideLength, $sideLength, $sideLength, $styles); 985 break; 986 case 4: # left top / bottom triangle 987 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($halfSideLength, $negativeHalfSideLength) rotate(0, $halfSideLength, $halfTriangleHeight)"])); 988 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($halfSideLength, $tileHeightPlusHalfSideLength) rotate(0, $halfSideLength, $halfTriangleHeight) scale(1, -1)"])); 989 break; 990 case 5: # right top / bottom triangle 991 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($tileWidthMinusHalfSideLength, $negativeHalfSideLength) rotate(0, $halfSideLength, $halfTriangleHeight) scale(-1, 1)"])); 992 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($tileWidthMinusHalfSideLength, $tileHeightPlusHalfSideLength) rotate(0, $halfSideLength, $halfTriangleHeight) scale(-1, -1)"])); 993 break; 994 case 6: # center / top / right triangle 995 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($halfTileWidthPlusHalfSideLength, $halfHexHeight)"])); 996 break; 997 case 7: # center / top / left triangle 998 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($tileWidthMinusHalfTileWidthMinusHalfSideLength, $halfHexHeight) scale(-1, 1)"])); 999 break; 1000 case 8: # center / bottom / right triangle 1001 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($halfTileWidthPlusHalfSideLength, $tileHeightMinusHalfHexHeight) scale(1, -1)"])); 1002 break; 1003 case 9: # center / bottom / left triangle 1004 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($tileWidthMinusHalfTileWidthMinusHalfSideLength, $tileHeightMinusHalfHexHeight) scale(-1, -1)"])); 1005 break; 1006 case 10: # left / middle triangle 1007 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($halfSideLength, $halfTileHeightMinusHalfSideLength)"])); 1008 break; 1009 case 11: # right / middle triangle 1010 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($tileWidthMinusHalfSideLength, $halfTileHeightMinusHalfSideLength) scale(-1, 1)"])); 1011 break; 1012 case 12: # left / top square 1013 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "translate($halfSideLength, $halfSideLength) rotate(-30, 0, 0)"])); 1014 break; 1015 case 13: # right / top square 1016 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(-1, 1) translate($negativeTileWidthPlusHalfSideLength, $halfSideLength) rotate(-30, 0, 0)"])); 1017 break; 1018 case 14: # left / center-top square 1019 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "translate($halfSideLength, $halfTileHeightMinusHalfSideLengthMinusSideLength) rotate(30, 0, $sideLength)"])); 1020 break; 1021 case 15: # right / center-top square 1022 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(-1, 1) translate($negativeTileWidthPlusHalfSideLength, $halfTileHeightMinusHalfSideLengthMinusSideLength) rotate(30, 0, $sideLength)"])); 1023 break; 1024 case 16: # left / center-top square 1025 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(1, -1) translate($halfSideLength, $negativeTileHeightPlusHalfTileHeightMinusHalfSideLengthMinusSideLength) rotate(30, 0, $sideLength)"])); 1026 break; 1027 case 17: # right / center-bottom square 1028 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(-1, -1) translate($negativeTileWidthPlusHalfSideLength, $negativeTileHeightPlusHalfTileHeightMinusHalfSideLengthMinusSideLength) rotate(30, 0, $sideLength)"])); 1029 break; 1030 case 18: # left / bottom square 1031 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(1, -1) translate($halfSideLength, $negativeTileHeightPlusHalfSideLength) rotate(-30, 0, 0)"])); 1032 break; 1033 case 19: # right / bottom square 1034 $this->svg->addRectangle(0, 0, $sideLength, $sideLength, array_merge($styles, ['transform' => "scale(-1, -1) translate($negativeTileWidthPlusHalfSideLength, $negativeTileHeightPlusHalfSideLength) rotate(-30, 0, 0)"])); 1035 break; 1036 } 1037 } 1038 } 1039 1040 // build* functions 1041 protected function buildChevronShape($width, $height) 1042 { 1043 $e = $height * 0.66; 1044 $halfWidth = $width / 2; 1045 $heightMinusE = $height - $e; 1046 return [ 1047 new Polyline("0,0,$halfWidth,$heightMinusE,$halfWidth,$height,0,$e,0,0"), 1048 new Polyline("$halfWidth,$heightMinusE,$width,0,$width,$e,$halfWidth,$height,$halfWidth,$heightMinusE") 1049 ]; 1050 } 1051 1052 protected function buildOctogonShape($squareSize) 1053 { 1054 $s = $squareSize; 1055 $c = $s * 0.33; 1056 $sMinusC = $s - $c; 1057 return "$c,0,$sMinusC,0,$s,$c,$s,$sMinusC,$sMinusC,$s,$c,$s,0,$sMinusC,0,$c,$c,0"; 1058 } 1059 1060 protected function buildHexagonShape($sideLength) 1061 { 1062 $c = $sideLength; 1063 $a = $c/2; 1064 $b = sin(60 * M_PI / 180) * $c; 1065 $twoB = $b * 2; 1066 $twoC = $c * 2; 1067 $aPlusC = $a + $c; 1068 return "0,$b,$a,0,$aPlusC,0,$twoC,$b,$aPlusC,$twoB,$a,$twoB,0,$b"; 1069 } 1070 1071 protected function buildPlusShape($squareSize) 1072 { 1073 return [ 1074 new Rectangle($squareSize, 0, $squareSize, $squareSize*3), 1075 new Rectangle(0, $squareSize, $squareSize*3, $squareSize), 1076 ]; 1077 } 1078 1079 protected function buildTriangleShape($sideLength, $height) 1080 { 1081 $halfWidth = $sideLength / 2; 1082 return "$halfWidth, 0, $sideLength, $height, 0, $height, $halfWidth, 0"; 1083 } 1084 1085 protected function buildRotatedTriangleShape($sideLength, $width) 1086 { 1087 $halfHeight = $sideLength / 2; 1088 return "0, 0, $width, $halfHeight, 0, $sideLength, 0, 0"; 1089 } 1090 1091 protected function buildRightTriangleShape($sideLength) 1092 { 1093 return "0, 0, $sideLength, $sideLength, 0, $sideLength, 0, 0"; 1094 } 1095 1096 protected function buildDiamondShape($width, $height) 1097 { 1098 $halfWidth = $width / 2; 1099 $halfHeight = $height / 2; 1100 return "$halfWidth, 0, $width, $halfHeight, $halfWidth, $height, 0, $halfHeight"; 1101 } 1102 1103 // draw* functions 1104 protected function drawInnerMosaicTile($x, $y, $triangleSize, $vals) 1105 { 1106 $triangle = $this->buildRightTriangleShape($triangleSize); 1107 $opacity = $this->opacity($vals[0]); 1108 $fill = $this->fillColor($vals[0]); 1109 $styles = [ 1110 'stroke' => self::STROKE_COLOR, 1111 'stroke-opacity' => self::STROKE_OPACITY, 1112 'fill-opacity' => $opacity, 1113 'fill' => $fill, 1114 ]; 1115 $xPlusTriangleSize = $x + $triangleSize; 1116 $yPlusTwoTriangleSize = $y + $triangleSize * 2; 1117 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTriangleSize, $y) scale(-1, 1)"])) 1118 ->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTriangleSize, $yPlusTwoTriangleSize) scale(1, -1)"])); 1119 1120 $opacity = $this->opacity($vals[1]); 1121 $fill = $this->fillColor($vals[1]); 1122 $styles = [ 1123 'stroke' => self::STROKE_COLOR, 1124 'stroke-opacity' => self::STROKE_OPACITY, 1125 'fill-opacity' => $opacity, 1126 'fill' => $fill, 1127 ]; 1128 $xPlusTriangleSize = $x + $triangleSize; 1129 $yPlusTwoTriangleSize = $y + $triangleSize * 2; 1130 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTriangleSize, $yPlusTwoTriangleSize) scale(-1, -1)"])) 1131 ->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTriangleSize, $y) scale(1, 1)"])); 1132 1133 return $this; 1134 } 1135 1136 protected function drawOuterMosaicTile($x, $y, $triangleSize, $val) 1137 { 1138 $triangle = $this->buildRightTriangleShape($triangleSize); 1139 $opacity = $this->opacity($val); 1140 $fill = $this->fillColor($val); 1141 $styles = [ 1142 'stroke' => self::STROKE_COLOR, 1143 'stroke-opacity' => self::STROKE_OPACITY, 1144 'fill-opacity' => $opacity, 1145 'fill' => $fill, 1146 ]; 1147 1148 $yPlusTriangleSize = $y + $triangleSize; 1149 $xPlusTwoTriangleSize = $x + $triangleSize * 2; 1150 $this->svg->addPolyline($triangle, array_merge($styles, ['transform' => "translate($x, $yPlusTriangleSize) scale(1, -1)"])) 1151 ->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTwoTriangleSize, $yPlusTriangleSize) scale(-1, -1)"])) 1152 ->addPolyline($triangle, array_merge($styles, ['transform' => "translate($x, $yPlusTriangleSize) scale(1, 1)"])) 1153 ->addPolyline($triangle, array_merge($styles, ['transform' => "translate($xPlusTwoTriangleSize, $yPlusTriangleSize) scale(-1, 1)"])); 1154 } 1155 1156 // Utility Functions 1157 1158 protected function fillColor($val) 1159 { 1160 return ($val % 2 == 0) ? self::FILL_COLOR_LIGHT : self::FILL_COLOR_DARK; 1161 } 1162 1163 protected function opacity($val) 1164 { 1165 return $this->map($val, 0, 15, self::OPACITY_MIN, self::OPACITY_MAX); 1166 } 1167 1168 protected function hexVal($index, $len) 1169 { 1170 return hexdec(substr($this->hash, $index, $len)); 1171 } 1172 1173 // PHP implementation of Processing's map function 1174 // http://processing.org/reference/map_.html 1175 protected function map($value, $vMin, $vMax, $dMin, $dMax) 1176 { 1177 $vValue = floatval($value); 1178 $vRange = $vMax - $vMin; 1179 $dRange = $dMax - $dMin; 1180 return ($vValue - $vMin) * $dRange / $vRange + $dMin; 1181 } 1182 1183 // Color Functions 1184 protected function hexToHSL($color) 1185 { 1186 $color = trim($color, '#'); 1187 $R = hexdec($color[0].$color[1]); 1188 $G = hexdec($color[2].$color[3]); 1189 $B = hexdec($color[4].$color[5]); 1190 1191 $HSL = array(); 1192 1193 $var_R = ($R / 255); 1194 $var_G = ($G / 255); 1195 $var_B = ($B / 255); 1196 1197 $var_Min = min($var_R, $var_G, $var_B); 1198 $var_Max = max($var_R, $var_G, $var_B); 1199 $del_Max = $var_Max - $var_Min; 1200 1201 $L = ($var_Max + $var_Min)/2; 1202 1203 if ($del_Max == 0) 1204 { 1205 $H = 0; 1206 $S = 0; 1207 } 1208 else 1209 { 1210 if ( $L < 0.5 ) $S = $del_Max / ( $var_Max + $var_Min ); 1211 else $S = $del_Max / ( 2 - $var_Max - $var_Min ); 1212 1213 $del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max; 1214 $del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max; 1215 $del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max; 1216 1217 if ($var_R == $var_Max) $H = $del_B - $del_G; 1218 else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B; 1219 else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R; 1220 1221 if ($H<0) $H++; 1222 if ($H>1) $H--; 1223 } 1224 1225 $HSL['h'] = ($H*360); 1226 $HSL['s'] = $S; 1227 $HSL['l'] = $L; 1228 1229 return $HSL; 1230 } 1231 1232 protected function hexToRGB($hex) { 1233 $hex = str_replace("#", "", $hex); 1234 if(strlen($hex) == 3) { 1235 $r = hexdec(substr($hex,0,1).substr($hex,0,1)); 1236 $g = hexdec(substr($hex,1,1).substr($hex,1,1)); 1237 $b = hexdec(substr($hex,2,1).substr($hex,2,1)); 1238 } else { 1239 $r = hexdec(substr($hex,0,2)); 1240 $g = hexdec(substr($hex,2,2)); 1241 $b = hexdec(substr($hex,4,2)); 1242 } 1243 return ['r' => $r, 'g' => $g, 'b' => $b]; 1244 } 1245 1246 protected function rgbToHSL($r, $g, $b) { 1247 $r /= 255; 1248 $g /= 255; 1249 $b /= 255; 1250 $max = max($r, $g, $b); 1251 $min = min($r, $g, $b); 1252 $l = ($max + $min) / 2; 1253 if ($max == $min) { 1254 $h = $s = 0; 1255 } else { 1256 $d = $max - $min; 1257 $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); 1258 switch ($max) { 1259 case $r: 1260 $h = ($g - $b) / $d + ($g < $b ? 6 : 0); 1261 break; 1262 case $g: 1263 $h = ($b - $r) / $d + 2; 1264 break; 1265 case $b: 1266 $h = ($r - $g) / $d + 4; 1267 break; 1268 } 1269 $h /= 6; 1270 } 1271 $h = floor($h * 360); 1272 $s = floor($s * 100); 1273 $l = floor($l * 100); 1274 return ['h' => $h, 's' => $s, 'l' => $l]; 1275 } 1276 1277 protected function hslToRGB ($h, $s, $l) { 1278 $h += 360; 1279 $c = ( 1 - abs( 2 * $l - 1 ) ) * $s; 1280 $x = $c * ( 1 - abs( fmod( ( $h / 60 ), 2 ) - 1 ) ); 1281 $m = $l - ( $c / 2 ); 1282 1283 if ( $h < 60 ) { 1284 $r = $c; 1285 $g = $x; 1286 $b = 0; 1287 } else if ( $h < 120 ) { 1288 $r = $x; 1289 $g = $c; 1290 $b = 0; 1291 } else if ( $h < 180 ) { 1292 $r = 0; 1293 $g = $c; 1294 $b = $x; 1295 } else if ( $h < 240 ) { 1296 $r = 0; 1297 $g = $x; 1298 $b = $c; 1299 } else if ( $h < 300 ) { 1300 $r = $x; 1301 $g = 0; 1302 $b = $c; 1303 } else { 1304 $r = $c; 1305 $g = 0; 1306 $b = $x; 1307 } 1308 1309 $r = ( $r + $m ) * 255; 1310 $g = ( $g + $m ) * 255; 1311 $b = ( $b + $m ) * 255; 1312 1313 return array( 'r' => floor( $r ), 'g' => floor( $g ), 'b' => floor( $b ) ); 1314 1315 } 1316 1317 //NOT USED 1318 protected function rgbToHex($r, $g, $b) { 1319 $hex = "#"; 1320 $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT); 1321 $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT); 1322 $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT); 1323 return $hex; 1324 } 1325 1326 1327 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body