RoundnessModule.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <?php
  2. declare(strict_types = 1);
  3. namespace BaconQrCode\Renderer\Module;
  4. use BaconQrCode\Encoder\ByteMatrix;
  5. use BaconQrCode\Exception\InvalidArgumentException;
  6. use BaconQrCode\Renderer\Module\EdgeIterator\EdgeIterator;
  7. use BaconQrCode\Renderer\Path\Path;
  8. /**
  9. * Rounds the corners of module groups.
  10. */
  11. final class RoundnessModule implements ModuleInterface
  12. {
  13. public const STRONG = 1;
  14. public const MEDIUM = .5;
  15. public const SOFT = .25;
  16. /**
  17. * @var float
  18. */
  19. private $intensity;
  20. public function __construct(float $intensity)
  21. {
  22. if ($intensity <= 0 || $intensity > 1) {
  23. throw new InvalidArgumentException('Intensity must between 0 (exclusive) and 1 (inclusive)');
  24. }
  25. $this->intensity = $intensity / 2;
  26. }
  27. public function createPath(ByteMatrix $matrix) : Path
  28. {
  29. $path = new Path();
  30. foreach (new EdgeIterator($matrix) as $edge) {
  31. $points = $edge->getSimplifiedPoints();
  32. $length = count($points);
  33. $currentPoint = $points[0];
  34. $nextPoint = $points[1];
  35. $horizontal = ($currentPoint[1] === $nextPoint[1]);
  36. if ($horizontal) {
  37. $right = $nextPoint[0] > $currentPoint[0];
  38. $path = $path->move(
  39. $currentPoint[0] + ($right ? $this->intensity : -$this->intensity),
  40. $currentPoint[1]
  41. );
  42. } else {
  43. $up = $nextPoint[0] < $currentPoint[0];
  44. $path = $path->move(
  45. $currentPoint[0],
  46. $currentPoint[1] + ($up ? -$this->intensity : $this->intensity)
  47. );
  48. }
  49. for ($i = 1; $i <= $length; ++$i) {
  50. if ($i === $length) {
  51. $previousPoint = $points[$length - 1];
  52. $currentPoint = $points[0];
  53. $nextPoint = $points[1];
  54. } else {
  55. $previousPoint = $points[(0 === $i ? $length : $i) - 1];
  56. $currentPoint = $points[$i];
  57. $nextPoint = $points[($length - 1 === $i ? -1 : $i) + 1];
  58. }
  59. $horizontal = ($previousPoint[1] === $currentPoint[1]);
  60. if ($horizontal) {
  61. $right = $previousPoint[0] < $currentPoint[0];
  62. $up = $nextPoint[1] < $currentPoint[1];
  63. $sweep = ($up xor $right);
  64. if ($this->intensity < 0.5
  65. || ($right && $previousPoint[0] !== $currentPoint[0] - 1)
  66. || (! $right && $previousPoint[0] - 1 !== $currentPoint[0])
  67. ) {
  68. $path = $path->line(
  69. $currentPoint[0] + ($right ? -$this->intensity : $this->intensity),
  70. $currentPoint[1]
  71. );
  72. }
  73. $path = $path->ellipticArc(
  74. $this->intensity,
  75. $this->intensity,
  76. 0,
  77. false,
  78. $sweep,
  79. $currentPoint[0],
  80. $currentPoint[1] + ($up ? -$this->intensity : $this->intensity)
  81. );
  82. } else {
  83. $up = $previousPoint[1] > $currentPoint[1];
  84. $right = $nextPoint[0] > $currentPoint[0];
  85. $sweep = ! ($up xor $right);
  86. if ($this->intensity < 0.5
  87. || ($up && $previousPoint[1] !== $currentPoint[1] + 1)
  88. || (! $up && $previousPoint[0] + 1 !== $currentPoint[0])
  89. ) {
  90. $path = $path->line(
  91. $currentPoint[0],
  92. $currentPoint[1] + ($up ? $this->intensity : -$this->intensity)
  93. );
  94. }
  95. $path = $path->ellipticArc(
  96. $this->intensity,
  97. $this->intensity,
  98. 0,
  99. false,
  100. $sweep,
  101. $currentPoint[0] + ($right ? $this->intensity : -$this->intensity),
  102. $currentPoint[1]
  103. );
  104. }
  105. }
  106. $path = $path->close();
  107. }
  108. return $path;
  109. }
  110. }