From 410d98002a60d4268834c8ffd1f2e6f86261fa0c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 9 Jun 2024 10:49:00 +0200 Subject: [PATCH] Add Drawable improvements & preparation for future features * Add DrawableFactoryInterface * Add universal Drawable factory class * Add x/y setter methods to PointInterface * Replace Point::class type hints by PointInterface::class --- .../Imagick/Modifiers/TextModifier.php | 5 +- src/Geometry/Bezier.php | 36 ++++----- src/Geometry/Factories/BezierFactory.php | 18 ++++- src/Geometry/Factories/CircleFactory.php | 79 ++++++++++++++++++- src/Geometry/Factories/Drawable.php | 69 ++++++++++++++++ src/Geometry/Factories/EllipseFactory.php | 25 ++++-- src/Geometry/Factories/LineFactory.php | 18 ++++- src/Geometry/Factories/PolygonFactory.php | 18 ++++- src/Geometry/Factories/RectangleFactory.php | 25 ++++-- src/Geometry/Line.php | 36 ++++++--- src/Geometry/Point.php | 41 +++++----- src/Geometry/Polygon.php | 48 +++++------ src/Interfaces/DrawableFactoryInterface.php | 39 +++++++++ src/Interfaces/PointInterface.php | 48 +++++++++++ src/Modifiers/FillModifier.php | 4 +- src/Modifiers/TextModifier.php | 5 +- src/Typography/Line.php | 4 +- .../Unit/Geometry/Factories/DrawableTest.php | 47 +++++++++++ 18 files changed, 461 insertions(+), 104 deletions(-) create mode 100644 src/Geometry/Factories/Drawable.php create mode 100644 src/Interfaces/DrawableFactoryInterface.php create mode 100644 tests/Unit/Geometry/Factories/DrawableTest.php diff --git a/src/Drivers/Imagick/Modifiers/TextModifier.php b/src/Drivers/Imagick/Modifiers/TextModifier.php index 51f30bf7..42cf7a07 100644 --- a/src/Drivers/Imagick/Modifiers/TextModifier.php +++ b/src/Drivers/Imagick/Modifiers/TextModifier.php @@ -15,6 +15,7 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SpecializedInterface; use Intervention\Image\Modifiers\TextModifier as GenericTextModifier; use Intervention\Image\Typography\Line; @@ -112,14 +113,14 @@ class TextModifier extends GenericTextModifier implements SpecializedInterface * @param FrameInterface $frame * @param Line $textline * @param null|ImagickDraw $draw - * @param Point $offset + * @param PointInterface $offset * @return void */ private function maybeDrawTextline( FrameInterface $frame, Line $textline, ?ImagickDraw $draw = null, - Point $offset = new Point(), + PointInterface $offset = new Point(), ): void { if ($draw !== null) { $frame->native()->annotateImage( diff --git a/src/Geometry/Bezier.php b/src/Geometry/Bezier.php index 47082f4d..66ba9b9f 100644 --- a/src/Geometry/Bezier.php +++ b/src/Geometry/Bezier.php @@ -15,8 +15,8 @@ use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; /** - * @implements IteratorAggregate - * @implements ArrayAccess + * @implements IteratorAggregate + * @implements ArrayAccess */ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInterface { @@ -26,7 +26,7 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Create new bezier instance * - * @param array $points + * @param array $points * @param PointInterface $pivot * @return void */ @@ -49,7 +49,7 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Implement iteration through all points of bezier * - * @return Traversable + * @return Traversable */ public function getIterator(): Traversable { @@ -69,10 +69,10 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Change pivot point to given point * - * @param Point $pivot + * @param PointInterface $pivot * @return Bezier */ - public function setPivot(Point $pivot): self + public function setPivot(PointInterface $pivot): self { $this->pivot = $pivot; @@ -82,9 +82,9 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Return first control point of bezier * - * @return ?Point + * @return ?PointInterface */ - public function first(): ?Point + public function first(): ?PointInterface { if ($point = reset($this->points)) { return $point; @@ -96,9 +96,9 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Return second control point of bezier * - * @return ?Point + * @return ?PointInterface */ - public function second(): ?Point + public function second(): ?PointInterface { if (array_key_exists(1, $this->points)) { return $this->points[1]; @@ -110,9 +110,9 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Return third control point of bezier * - * @return ?Point + * @return ?PointInterface */ - public function third(): ?Point + public function third(): ?PointInterface { if (array_key_exists(2, $this->points)) { return $this->points[2]; @@ -124,9 +124,9 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Return last control point of bezier * - * @return ?Point + * @return ?PointInterface */ - public function last(): ?Point + public function last(): ?PointInterface { if ($point = end($this->points)) { return $point; @@ -160,7 +160,7 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter * Return point at given offset * * @param mixed $offset - * @return Point + * @return PointInterface */ public function offsetGet($offset): mixed { @@ -171,7 +171,7 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter * Set point at given offset * * @param mixed $offset - * @param Point $value + * @param PointInterface $value * @return void */ public function offsetSet($offset, $value): void @@ -193,10 +193,10 @@ class Bezier implements IteratorAggregate, Countable, ArrayAccess, DrawableInter /** * Add given point to bezier * - * @param Point $point + * @param PointInterface $point * @return Bezier */ - public function addPoint(Point $point): self + public function addPoint(PointInterface $point): self { $this->points[] = $point; diff --git a/src/Geometry/Factories/BezierFactory.php b/src/Geometry/Factories/BezierFactory.php index 0a5a14b8..8747bd50 100644 --- a/src/Geometry/Factories/BezierFactory.php +++ b/src/Geometry/Factories/BezierFactory.php @@ -6,18 +6,20 @@ namespace Intervention\Image\Geometry\Factories; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Bezier; +use Intervention\Image\Interfaces\DrawableFactoryInterface; +use Intervention\Image\Interfaces\DrawableInterface; -class BezierFactory +class BezierFactory implements DrawableFactoryInterface { protected Bezier $bezier; /** * Create new factory instance * - * @param callable|Bezier $init + * @param null|callable|Bezier $init * @return void */ - public function __construct(callable|Bezier $init) + public function __construct(null|callable|Bezier $init = null) { $this->bezier = is_a($init, Bezier::class) ? $init : new Bezier([]); @@ -26,6 +28,16 @@ class BezierFactory } } + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->bezier; + } + /** * Add a point to the bezier to be produced * diff --git a/src/Geometry/Factories/CircleFactory.php b/src/Geometry/Factories/CircleFactory.php index ed3565c9..83aa0336 100644 --- a/src/Geometry/Factories/CircleFactory.php +++ b/src/Geometry/Factories/CircleFactory.php @@ -4,8 +4,44 @@ declare(strict_types=1); namespace Intervention\Image\Geometry\Factories; -class CircleFactory extends EllipseFactory +use Intervention\Image\Geometry\Circle; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\DrawableFactoryInterface; +use Intervention\Image\Interfaces\DrawableInterface; +use Intervention\Image\Interfaces\PointInterface; + +class CircleFactory implements DrawableFactoryInterface { + protected Circle $circle; + + /** + * Create new factory instance + * + * @param PointInterface $pivot + * @param null|callable|Circle $init + * @return void + */ + public function __construct( + protected PointInterface $pivot = new Point(), + null|callable|Circle $init = null, + ) { + $this->circle = is_a($init, Circle::class) ? $init : new Circle(0, $pivot); + + if (is_callable($init)) { + $init($this); + } + } + + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->circle; + } + /** * Set the radius of the circle to be produced * @@ -14,7 +50,7 @@ class CircleFactory extends EllipseFactory */ public function radius(int $radius): self { - $this->ellipse->setSize($radius * 2, $radius * 2); + $this->circle->setSize($radius * 2, $radius * 2); return $this; } @@ -27,8 +63,45 @@ class CircleFactory extends EllipseFactory */ public function diameter(int $diameter): self { - $this->ellipse->setSize($diameter, $diameter); + $this->circle->setSize($diameter, $diameter); return $this; } + + /** + * Set the background color of the circle to be produced + * + * @param mixed $color + * @return CircleFactory + */ + public function background(mixed $color): self + { + $this->circle->setBackgroundColor($color); + + return $this; + } + + /** + * Set the border color & border size of the ellipse to be produced + * + * @param mixed $color + * @param int $size + * @return CircleFactory + */ + public function border(mixed $color, int $size = 1): self + { + $this->circle->setBorder($color, $size); + + return $this; + } + + /** + * Produce the circle + * + * @return Circle + */ + public function __invoke(): Circle + { + return $this->circle; + } } diff --git a/src/Geometry/Factories/Drawable.php b/src/Geometry/Factories/Drawable.php new file mode 100644 index 00000000..638bb547 --- /dev/null +++ b/src/Geometry/Factories/Drawable.php @@ -0,0 +1,69 @@ +ellipse = is_a($init, Ellipse::class) ? $init : new Ellipse(0, 0, $pivot); if (is_callable($init)) { @@ -27,6 +32,16 @@ class EllipseFactory } } + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->ellipse; + } + /** * Set the size of the ellipse to be produced * diff --git a/src/Geometry/Factories/LineFactory.php b/src/Geometry/Factories/LineFactory.php index 531f4cf0..7876584b 100644 --- a/src/Geometry/Factories/LineFactory.php +++ b/src/Geometry/Factories/LineFactory.php @@ -6,18 +6,20 @@ namespace Intervention\Image\Geometry\Factories; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Line; +use Intervention\Image\Interfaces\DrawableFactoryInterface; +use Intervention\Image\Interfaces\DrawableInterface; -class LineFactory +class LineFactory implements DrawableFactoryInterface { protected Line $line; /** * Create the factory instance * - * @param callable|Line $init + * @param null|callable|Line $init * @return void */ - public function __construct(callable|Line $init) + public function __construct(null|callable|Line $init = null) { $this->line = is_a($init, Line::class) ? $init : new Line(new Point(), new Point()); @@ -26,6 +28,16 @@ class LineFactory } } + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->line; + } + /** * Set the color of the line to be produced * diff --git a/src/Geometry/Factories/PolygonFactory.php b/src/Geometry/Factories/PolygonFactory.php index f1acc7d9..ef18b800 100644 --- a/src/Geometry/Factories/PolygonFactory.php +++ b/src/Geometry/Factories/PolygonFactory.php @@ -6,18 +6,20 @@ namespace Intervention\Image\Geometry\Factories; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Interfaces\DrawableFactoryInterface; +use Intervention\Image\Interfaces\DrawableInterface; -class PolygonFactory +class PolygonFactory implements DrawableFactoryInterface { protected Polygon $polygon; /** * Create new factory instance * - * @param callable|Polygon $init + * @param null|callable|Polygon $init * @return void */ - public function __construct(callable|Polygon $init) + public function __construct(null|callable|Polygon $init = null) { $this->polygon = is_a($init, Polygon::class) ? $init : new Polygon([]); @@ -26,6 +28,16 @@ class PolygonFactory } } + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->polygon; + } + /** * Add a point to the polygon to be produced * diff --git a/src/Geometry/Factories/RectangleFactory.php b/src/Geometry/Factories/RectangleFactory.php index be402ffc..b563dd0f 100644 --- a/src/Geometry/Factories/RectangleFactory.php +++ b/src/Geometry/Factories/RectangleFactory.php @@ -6,20 +6,25 @@ namespace Intervention\Image\Geometry\Factories; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; +use Intervention\Image\Interfaces\DrawableFactoryInterface; +use Intervention\Image\Interfaces\DrawableInterface; +use Intervention\Image\Interfaces\PointInterface; -class RectangleFactory +class RectangleFactory implements DrawableFactoryInterface { protected Rectangle $rectangle; /** * Create new instance * - * @param Point $pivot - * @param callable|Rectangle $init + * @param PointInterface $pivot + * @param null|callable|Rectangle $init * @return void */ - public function __construct(protected Point $pivot, callable|Rectangle $init) - { + public function __construct( + protected PointInterface $pivot = new Point(), + null|callable|Rectangle $init = null, + ) { $this->rectangle = is_a($init, Rectangle::class) ? $init : new Rectangle(0, 0, $pivot); if (is_callable($init)) { @@ -27,6 +32,16 @@ class RectangleFactory } } + /** + * {@inheritdoc} + * + * @see DrawableFactoryInterface::create() + */ + public function create(): DrawableInterface + { + return $this->rectangle; + } + /** * Set the size of the rectangle to be produced * diff --git a/src/Geometry/Line.php b/src/Geometry/Line.php index eb879f7a..ef0beab3 100644 --- a/src/Geometry/Line.php +++ b/src/Geometry/Line.php @@ -17,14 +17,14 @@ class Line implements DrawableInterface /** * Create new line instance * - * @param Point $start - * @param Point $end + * @param PointInterface $start + * @param PointInterface $end * @param int $width * @return void */ public function __construct( - protected Point $start, - protected Point $end, + protected PointInterface $start, + protected PointInterface $end, protected int $width = 1 ) { } @@ -39,6 +39,18 @@ class Line implements DrawableInterface return $this->start; } + /** + * {@inheritdoc} + * + * @see DrawableInterface::setPosition() + */ + public function setPosition(PointInterface $position): DrawableInterface + { + $this->start = $position; + + return $this; + } + /** * Return line width * @@ -65,9 +77,9 @@ class Line implements DrawableInterface /** * Get starting point of line * - * @return Point + * @return PointInterface */ - public function start(): Point + public function start(): PointInterface { return $this->start; } @@ -75,9 +87,9 @@ class Line implements DrawableInterface /** * get end point of line * - * @return Point + * @return PointInterface */ - public function end(): Point + public function end(): PointInterface { return $this->end; } @@ -85,10 +97,10 @@ class Line implements DrawableInterface /** * Set starting point of line * - * @param Point $start + * @param PointInterface $start * @return Line */ - public function setStart(Point $start): self + public function setStart(PointInterface $start): self { $this->start = $start; @@ -128,10 +140,10 @@ class Line implements DrawableInterface /** * Set end point of line * - * @param Point $end + * @param PointInterface $end * @return Line */ - public function setEnd(Point $end): self + public function setEnd(PointInterface $end): self { $this->end = $end; diff --git a/src/Geometry/Point.php b/src/Geometry/Point.php index 7f524838..4522fb54 100644 --- a/src/Geometry/Point.php +++ b/src/Geometry/Point.php @@ -22,9 +22,9 @@ class Point implements PointInterface } /** - * Sets X coordinate + * {@inheritdoc} * - * @param int $x + * @see PointInterface::setX() */ public function setX(int $x): self { @@ -34,9 +34,9 @@ class Point implements PointInterface } /** - * Get X coordinate + * {@inheritdoc} * - * @return int + * @see PointInterface::x() */ public function x(): int { @@ -44,9 +44,9 @@ class Point implements PointInterface } /** - * Sets Y coordinate + * {@inheritdoc} * - * @param int $y + * @see PointInterface::setY() */ public function setY(int $y): self { @@ -56,9 +56,9 @@ class Point implements PointInterface } /** - * Get Y coordinate + * {@inheritdoc} * - * @return int + * @see PointInterface::y() */ public function y(): int { @@ -66,9 +66,9 @@ class Point implements PointInterface } /** - * Move X coordinate + * {@inheritdoc} * - * @param int $value + * @see PointInterface::moveX() */ public function moveX(int $value): self { @@ -78,9 +78,9 @@ class Point implements PointInterface } /** - * Move Y coordinate + * {@inheritdoc} * - * @param int $value + * @see PointInterface::moveY() */ public function moveY(int $value): self { @@ -89,17 +89,20 @@ class Point implements PointInterface return $this; } + /** + * {@inheritdoc} + * + * @see PointInterface::move() + */ public function move(int $x, int $y): self { return $this->moveX($x)->moveY($y); } /** - * Sets both X and Y coordinate + * {@inheritdoc} * - * @param int $x - * @param int $y - * @return Point + * @see PointInterface::setPosition() */ public function setPosition(int $x, int $y): self { @@ -110,11 +113,9 @@ class Point implements PointInterface } /** - * Rotate point ccw around pivot + * {@inheritdoc} * - * @param float $angle - * @param PointInterface $pivot - * @return Point + * @see PointInterface::rotate() */ public function rotate(float $angle, PointInterface $pivot): self { diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 3f5e6d3e..b1fe1301 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -15,8 +15,8 @@ use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; /** - * @implements IteratorAggregate - * @implements ArrayAccess + * @implements IteratorAggregate + * @implements ArrayAccess */ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInterface { @@ -26,7 +26,7 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Create new polygon instance * - * @param array $points + * @param array $points * @param PointInterface $pivot * @return void */ @@ -49,7 +49,7 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Implement iteration through all points of polygon * - * @return Traversable + * @return Traversable */ public function getIterator(): Traversable { @@ -69,10 +69,10 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Change pivot point to given point * - * @param Point $pivot + * @param PointInterface $pivot * @return Polygon */ - public function setPivot(Point $pivot): self + public function setPivot(PointInterface $pivot): self { $this->pivot = $pivot; @@ -82,9 +82,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return first point of polygon * - * @return ?Point + * @return ?PointInterface */ - public function first(): ?Point + public function first(): ?PointInterface { if ($point = reset($this->points)) { return $point; @@ -96,9 +96,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return last point of polygon * - * @return ?Point + * @return ?PointInterface */ - public function last(): ?Point + public function last(): ?PointInterface { if ($point = end($this->points)) { return $point; @@ -132,7 +132,7 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte * Return point at given offset * * @param mixed $offset - * @return Point + * @return PointInterface */ public function offsetGet($offset): mixed { @@ -143,7 +143,7 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte * Set point at given offset * * @param mixed $offset - * @param Point $value + * @param PointInterface $value * @return void */ public function offsetSet($offset, $value): void @@ -165,10 +165,10 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Add given point to polygon * - * @param Point $point + * @param PointInterface $point * @return Polygon */ - public function addPoint(Point $point): self + public function addPoint(PointInterface $point): self { $this->points[] = $point; @@ -198,9 +198,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return most left point of all points in polygon * - * @return Point + * @return PointInterface */ - public function mostLeftPoint(): Point + public function mostLeftPoint(): PointInterface { $points = []; foreach ($this->points as $point) { @@ -220,9 +220,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return most right point in polygon * - * @return Point + * @return PointInterface */ - public function mostRightPoint(): Point + public function mostRightPoint(): PointInterface { $points = []; foreach ($this->points as $point) { @@ -242,9 +242,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return most top point in polygon * - * @return Point + * @return PointInterface */ - public function mostTopPoint(): Point + public function mostTopPoint(): PointInterface { $points = []; foreach ($this->points as $point) { @@ -264,9 +264,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return most bottom point in polygon * - * @return Point + * @return PointInterface */ - public function mostBottomPoint(): Point + public function mostBottomPoint(): PointInterface { $points = []; foreach ($this->points as $point) { @@ -286,9 +286,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte /** * Return point in absolute center of the polygon * - * @return Point + * @return PointInterface */ - public function centerPoint(): Point + public function centerPoint(): PointInterface { return new Point( $this->mostRightPoint()->x() - (intval(round($this->width() / 2))), diff --git a/src/Interfaces/DrawableFactoryInterface.php b/src/Interfaces/DrawableFactoryInterface.php new file mode 100644 index 00000000..a5284f39 --- /dev/null +++ b/src/Interfaces/DrawableFactoryInterface.php @@ -0,0 +1,39 @@ + + * @return array */ protected function strokeOffsets(FontInterface $font): array { diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 555acd91..3bdeb02b 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -75,10 +75,10 @@ class Line implements IteratorAggregate, Countable /** * Set position of current line * - * @param Point $point + * @param PointInterface $point * @return Line */ - public function setPosition(Point $point): self + public function setPosition(PointInterface $point): self { $this->position = $point; diff --git a/tests/Unit/Geometry/Factories/DrawableTest.php b/tests/Unit/Geometry/Factories/DrawableTest.php new file mode 100644 index 00000000..a0bbec77 --- /dev/null +++ b/tests/Unit/Geometry/Factories/DrawableTest.php @@ -0,0 +1,47 @@ +assertInstanceOf(BezierFactory::class, Drawable::bezier()); + } + + public function testCircle(): void + { + $this->assertInstanceOf(CircleFactory::class, Drawable::circle()); + } + + public function testEllipse(): void + { + $this->assertInstanceOf(EllipseFactory::class, Drawable::ellipse()); + } + + public function testLine(): void + { + $this->assertInstanceOf(LineFactory::class, Drawable::line()); + } + + public function testPolygon(): void + { + $this->assertInstanceOf(PolygonFactory::class, Drawable::polygon()); + } + + public function testRectangle(): void + { + $this->assertInstanceOf(RectangleFactory::class, Drawable::rectangle()); + } +}