Skip to content

Commit 20fc451

Browse files
author
nejc
committed
Add Vector implementation with abstract base class
1 parent ab71390 commit 20fc451

File tree

4 files changed

+355
-0
lines changed

4 files changed

+355
-0
lines changed

src/Abstract/AbstractVector.php

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
namespace Nejcc\PhpDatatypes\Abstract;
4+
5+
use Nejcc\PhpDatatypes\Interfaces\DataTypeInterface;
6+
use Nejcc\PhpDatatypes\Exceptions\InvalidArgumentException;
7+
8+
abstract class AbstractVector implements DataTypeInterface
9+
{
10+
protected array $components;
11+
12+
public function __construct(array $components)
13+
{
14+
$this->validateComponents($components);
15+
$this->components = $components;
16+
}
17+
18+
abstract protected function validateComponents(array $components): void;
19+
20+
public function getComponents(): array
21+
{
22+
return $this->components;
23+
}
24+
25+
public function magnitude(): float
26+
{
27+
return sqrt(array_sum(array_map(fn($component) => $component ** 2, $this->components)));
28+
}
29+
30+
public function normalize(): self
31+
{
32+
$magnitude = $this->magnitude();
33+
if ($magnitude === 0.0) {
34+
throw new InvalidArgumentException("Cannot normalize a zero vector");
35+
}
36+
37+
$normalized = array_map(fn($component) => $component / $magnitude, $this->components);
38+
return new static($normalized);
39+
}
40+
41+
public function dot(self $other): float
42+
{
43+
if (get_class($this) !== get_class($other)) {
44+
throw new InvalidArgumentException("Cannot calculate dot product of vectors with different dimensions");
45+
}
46+
47+
return array_sum(array_map(
48+
fn($a, $b) => $a * $b,
49+
$this->components,
50+
$other->components
51+
));
52+
}
53+
54+
public function add(self $other): self
55+
{
56+
if (get_class($this) !== get_class($other)) {
57+
throw new InvalidArgumentException("Cannot add vectors with different dimensions");
58+
}
59+
60+
$result = array_map(
61+
fn($a, $b) => $a + $b,
62+
$this->components,
63+
$other->components
64+
);
65+
66+
return new static($result);
67+
}
68+
69+
public function subtract(self $other): self
70+
{
71+
if (get_class($this) !== get_class($other)) {
72+
throw new InvalidArgumentException("Cannot subtract vectors with different dimensions");
73+
}
74+
75+
$result = array_map(
76+
fn($a, $b) => $a - $b,
77+
$this->components,
78+
$other->components
79+
);
80+
81+
return new static($result);
82+
}
83+
84+
public function scale(float $scalar): self
85+
{
86+
$result = array_map(
87+
fn($component) => $component * $scalar,
88+
$this->components
89+
);
90+
91+
return new static($result);
92+
}
93+
94+
public function __toString(): string
95+
{
96+
return '(' . implode(', ', $this->components) . ')';
97+
}
98+
99+
protected function validateNumericComponents(array $components): void
100+
{
101+
foreach ($components as $component) {
102+
if (!is_numeric($component)) {
103+
throw new InvalidArgumentException("All components must be numeric");
104+
}
105+
}
106+
}
107+
108+
protected function validateComponentCount(array $components, int $expectedCount): void
109+
{
110+
if (count($components) !== $expectedCount) {
111+
throw new InvalidArgumentException(sprintf(
112+
"Vector must have exactly %d components",
113+
$expectedCount
114+
));
115+
}
116+
}
117+
118+
public function getComponent(int $index): float
119+
{
120+
if (!isset($this->components[$index])) {
121+
throw new InvalidArgumentException("Invalid component index");
122+
}
123+
return $this->components[$index];
124+
}
125+
126+
public function equals(DataTypeInterface $other): bool
127+
{
128+
if (!$other instanceof self) {
129+
return false;
130+
}
131+
132+
return $this->components === $other->components;
133+
}
134+
135+
public function distance(self $other): float
136+
{
137+
if (get_class($this) !== get_class($other)) {
138+
throw new InvalidArgumentException("Cannot calculate distance between vectors with different dimensions");
139+
}
140+
141+
$squaredDiff = array_map(
142+
fn($a, $b) => ($a - $b) ** 2,
143+
$this->components,
144+
$other->components
145+
);
146+
147+
return sqrt(array_sum($squaredDiff));
148+
}
149+
}

src/Composite/Vector/Vec2.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace Nejcc\PhpDatatypes\Composite\Vector;
4+
5+
use Nejcc\PhpDatatypes\Abstract\AbstractVector;
6+
use Nejcc\PhpDatatypes\Exceptions\InvalidArgumentException;
7+
8+
class Vec2 extends AbstractVector
9+
{
10+
protected function validateComponents(array $components): void
11+
{
12+
$this->validateComponentCount($components, 2);
13+
$this->validateNumericComponents($components);
14+
}
15+
16+
public function getX(): float
17+
{
18+
return $this->getComponent(0);
19+
}
20+
21+
public function getY(): float
22+
{
23+
return $this->getComponent(1);
24+
}
25+
26+
public function cross(Vec2 $other): float
27+
{
28+
return ($this->getX() * $other->getY()) - ($this->getY() * $other->getX());
29+
}
30+
31+
public static function zero(): self
32+
{
33+
return new self([0.0, 0.0]);
34+
}
35+
36+
public static function unitX(): self
37+
{
38+
return new self([1.0, 0.0]);
39+
}
40+
41+
public static function unitY(): self
42+
{
43+
return new self([0.0, 1.0]);
44+
}
45+
46+
public function getValue(): array
47+
{
48+
return $this->components;
49+
}
50+
51+
public function setValue(mixed $value): void
52+
{
53+
if (!is_array($value)) {
54+
throw new InvalidArgumentException('Value must be an array of components.');
55+
}
56+
$this->validateComponents($value);
57+
$this->components = $value;
58+
}
59+
}

src/Composite/Vector/Vec3.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Nejcc\PhpDatatypes\Composite\Vector;
4+
5+
use Nejcc\PhpDatatypes\Abstract\AbstractVector;
6+
use Nejcc\PhpDatatypes\Exceptions\InvalidArgumentException;
7+
8+
class Vec3 extends AbstractVector
9+
{
10+
protected function validateComponents(array $components): void
11+
{
12+
$this->validateComponentCount($components, 3);
13+
$this->validateNumericComponents($components);
14+
}
15+
16+
public function getX(): float
17+
{
18+
return $this->getComponent(0);
19+
}
20+
21+
public function getY(): float
22+
{
23+
return $this->getComponent(1);
24+
}
25+
26+
public function getZ(): float
27+
{
28+
return $this->getComponent(2);
29+
}
30+
31+
public function cross(Vec3 $other): self
32+
{
33+
return new self([
34+
$this->getY() * $other->getZ() - $this->getZ() * $other->getY(),
35+
$this->getZ() * $other->getX() - $this->getX() * $other->getZ(),
36+
$this->getX() * $other->getY() - $this->getY() * $other->getX()
37+
]);
38+
}
39+
40+
public static function zero(): self
41+
{
42+
return new self([0.0, 0.0, 0.0]);
43+
}
44+
45+
public static function unitX(): self
46+
{
47+
return new self([1.0, 0.0, 0.0]);
48+
}
49+
50+
public static function unitY(): self
51+
{
52+
return new self([0.0, 1.0, 0.0]);
53+
}
54+
55+
public static function unitZ(): self
56+
{
57+
return new self([0.0, 0.0, 1.0]);
58+
}
59+
60+
public function getValue(): array
61+
{
62+
return $this->components;
63+
}
64+
65+
public function setValue(mixed $value): void
66+
{
67+
if (!is_array($value)) {
68+
throw new InvalidArgumentException('Value must be an array of components.');
69+
}
70+
$this->validateComponents($value);
71+
$this->components = $value;
72+
}
73+
}

src/Composite/Vector/Vec4.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Nejcc\PhpDatatypes\Composite\Vector;
4+
5+
use Nejcc\PhpDatatypes\Abstract\AbstractVector;
6+
use Nejcc\PhpDatatypes\Exceptions\InvalidArgumentException;
7+
8+
class Vec4 extends AbstractVector
9+
{
10+
protected function validateComponents(array $components): void
11+
{
12+
$this->validateComponentCount($components, 4);
13+
$this->validateNumericComponents($components);
14+
}
15+
16+
public function getX(): float
17+
{
18+
return $this->getComponent(0);
19+
}
20+
21+
public function getY(): float
22+
{
23+
return $this->getComponent(1);
24+
}
25+
26+
public function getZ(): float
27+
{
28+
return $this->getComponent(2);
29+
}
30+
31+
public function getW(): float
32+
{
33+
return $this->getComponent(3);
34+
}
35+
36+
public static function zero(): self
37+
{
38+
return new self([0.0, 0.0, 0.0, 0.0]);
39+
}
40+
41+
public static function unitX(): self
42+
{
43+
return new self([1.0, 0.0, 0.0, 0.0]);
44+
}
45+
46+
public static function unitY(): self
47+
{
48+
return new self([0.0, 1.0, 0.0, 0.0]);
49+
}
50+
51+
public static function unitZ(): self
52+
{
53+
return new self([0.0, 0.0, 1.0, 0.0]);
54+
}
55+
56+
public static function unitW(): self
57+
{
58+
return new self([0.0, 0.0, 0.0, 1.0]);
59+
}
60+
61+
public function getValue(): array
62+
{
63+
return $this->components;
64+
}
65+
66+
public function setValue(mixed $value): void
67+
{
68+
if (!is_array($value)) {
69+
throw new InvalidArgumentException('Value must be an array of components.');
70+
}
71+
$this->validateComponents($value);
72+
$this->components = $value;
73+
}
74+
}

0 commit comments

Comments
 (0)