1
|
|
|
<?php |
2
|
|
|
/** @formatter:off |
3
|
|
|
* ****************************************************************** |
4
|
|
|
* Created by Marko Kungla on Jun 20, 2016 - 9:11:10 PM |
5
|
|
|
* Contact [email protected] |
6
|
|
|
* @copyright 2016 Marko Kungla - https://github.com/mkungla |
7
|
|
|
* @license The MIT License (MIT) |
8
|
|
|
* |
9
|
|
|
* @category AframeVR |
10
|
|
|
* @package aframe-php |
11
|
|
|
* |
12
|
|
|
* Lang PHP (php version >= 7) |
13
|
|
|
* Encoding UTF-8 |
14
|
|
|
* File Entity.php |
15
|
|
|
* Code format PSR-2 and 12 |
16
|
|
|
* @link https://github.com/mkungla/aframe-php |
17
|
|
|
* @issues https://github.com/mkungla/aframe-php/issues |
18
|
|
|
* ******************************************************************** |
19
|
|
|
* Contributors: |
20
|
|
|
* @author Marko Kungla <[email protected]> |
21
|
|
|
* ******************************************************************** |
22
|
|
|
* Comments: |
23
|
|
|
* @formatter:on */ |
24
|
|
|
namespace AframeVR\Core; |
25
|
|
|
|
26
|
|
|
use \AframeVR\Core\Exceptions\BadComponentCallException; |
27
|
|
|
use \AframeVR\Interfaces\{ |
28
|
|
|
ComponentInterface, |
29
|
|
|
EntityInterface, |
30
|
|
|
AnimationInterface |
31
|
|
|
}; |
32
|
|
|
use \AframeVR\Core\Helpers\{ |
33
|
|
|
EntityChildrenFactory, |
34
|
|
|
MeshAttributes, |
35
|
|
|
Helper |
36
|
|
|
}; |
37
|
|
|
use \AframeVR\Core\Animation; |
38
|
|
|
use \DOMElement; |
39
|
|
|
use \Closure; |
40
|
|
|
|
41
|
|
|
class Entity implements EntityInterface |
42
|
|
|
{ |
43
|
|
|
use MeshAttributes; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Array of used components |
47
|
|
|
* |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $components = array(); |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Array of used animations |
54
|
|
|
* |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
protected $animations = array(); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Dom element attributes |
61
|
|
|
* |
62
|
|
|
* @var unknown |
63
|
|
|
*/ |
64
|
|
|
protected $attrs = array(); |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Children Factory |
68
|
|
|
* |
69
|
|
|
* @var \AframeVR\Core\Helpers\EntityChildrenFactory |
70
|
|
|
*/ |
71
|
|
|
protected $childrenFactory; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Indent used when rendering formated outout |
75
|
|
|
* |
76
|
|
|
* @var unknown |
77
|
|
|
*/ |
78
|
|
|
private $indentation = 0; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Constructor |
82
|
|
|
* |
83
|
|
|
* @param string $id |
84
|
|
|
*/ |
85
|
81 |
|
public function __construct(string $id = 'untitled') |
86
|
|
|
{ |
87
|
81 |
|
$this->attr('id', $id); |
88
|
|
|
|
89
|
|
|
/* Extending entity reset | initial setup */ |
90
|
81 |
|
$this->reset(); |
91
|
81 |
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Reset primitive default attributtes |
95
|
|
|
* |
96
|
|
|
* {@inheritdoc} |
97
|
|
|
* |
98
|
|
|
* @return void |
99
|
|
|
*/ |
100
|
76 |
|
public function reset() |
101
|
|
|
{ |
102
|
|
|
/* Components which All entities inherently have */ |
103
|
76 |
|
$this->component('Position'); |
104
|
76 |
|
$this->component('Rotation'); |
105
|
76 |
|
$this->component('Scale'); |
106
|
76 |
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Set DOM attributes |
110
|
|
|
* |
111
|
|
|
* @param string $attr |
112
|
|
|
* @param string $val |
113
|
|
|
* @return void |
114
|
|
|
*/ |
115
|
81 |
|
public function attr(string $attr, string $val) |
116
|
|
|
{ |
117
|
81 |
|
$this->attrs[$attr] = $val; |
118
|
81 |
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Child entity / primitive |
122
|
|
|
* |
123
|
|
|
* @return \AframeVR\Core\Helpers\EntityChildrenFactory |
124
|
|
|
*/ |
125
|
6 |
|
public function child(): EntityChildrenFactory |
126
|
|
|
{ |
127
|
6 |
|
return $this->childrenFactory ?? $this->childrenFactory = new EntityChildrenFactory(); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Position component |
132
|
|
|
* |
133
|
|
|
* All entities inherently have the position component. |
134
|
|
|
* |
135
|
|
|
* @param int|float $x_axis |
136
|
|
|
* @param int|float $y_axis |
137
|
|
|
* @param int|float $z_axis |
138
|
|
|
* @return \AframeVR\Interfaces\EntityInterface |
139
|
|
|
*/ |
140
|
4 |
|
public function position(float $x_axis = 0, float $y_axis = 0, float $z_axis = 0): EntityInterface |
141
|
|
|
{ |
142
|
4 |
|
$this->component('Position')->positionX($x_axis); |
143
|
4 |
|
$this->component('Position')->positionY($y_axis); |
144
|
4 |
|
$this->component('Position')->positionZ($z_axis); |
145
|
4 |
|
return $this; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Rotation component |
150
|
|
|
* |
151
|
|
|
* All entities inherently have the rotation component. |
152
|
|
|
* |
153
|
|
|
* @param int|float $roll |
154
|
|
|
* @param int|float $pitch |
155
|
|
|
* @param int|float $yaw |
156
|
|
|
* @return \AframeVR\Interfaces\EntityInterface |
157
|
|
|
*/ |
158
|
4 |
|
public function rotation(float $roll = 0, float $pitch = 0, float $yaw = 0): EntityInterface |
159
|
|
|
{ |
160
|
4 |
|
$this->component('Rotation')->roll($roll); |
161
|
4 |
|
$this->component('Rotation')->pitch($pitch); |
162
|
4 |
|
$this->component('Rotation')->yaw($yaw); |
163
|
4 |
|
return $this; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Scale component |
168
|
|
|
* |
169
|
|
|
* All entities inherently have the scale component. |
170
|
|
|
* |
171
|
|
|
* @param int|float $scale_x |
172
|
|
|
* @param int|float $scale_y |
173
|
|
|
* @param int|float $scale_z |
174
|
|
|
* @return \AframeVR\Interfaces\EntityInterface |
175
|
|
|
*/ |
176
|
8 |
|
public function scale(float $scale_x = 1, float $scale_y = 1, float $scale_z = 1): EntityInterface |
177
|
|
|
{ |
178
|
8 |
|
$this->component('Scale')->scaleX($scale_x); |
179
|
8 |
|
$this->component('Scale')->scaleY($scale_y); |
180
|
8 |
|
$this->component('Scale')->scaleZ($scale_z); |
181
|
8 |
|
return $this; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Animations |
186
|
|
|
* |
187
|
|
|
* @param mixed $name |
188
|
|
|
* @return \AframeVR\Interfaces\AnimationInterface |
189
|
|
|
*/ |
190
|
2 |
|
public function animation($name = 'untitled'): AnimationInterface |
191
|
|
|
{ |
192
|
2 |
|
return $this->animations[$name] ?? $this->animations[$name] = new Animation(); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Set mixin attribute |
197
|
|
|
* |
198
|
|
|
* @param string $id |
199
|
|
|
* @return \AframeVR\Core\Entity |
200
|
|
|
*/ |
201
|
1 |
|
public function mixin(string $id) |
202
|
|
|
{ |
203
|
1 |
|
$this->attr('mixin', $id); |
204
|
1 |
|
return $this; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Load component for this entity |
209
|
|
|
* |
210
|
|
|
* @param string $component_name |
211
|
|
|
* @throws \AframeVR\Core\Exceptions\BadComponentCallException |
212
|
|
|
* @return object|null |
213
|
|
|
*/ |
214
|
81 |
|
public function component(string $component_name) |
215
|
|
|
{ |
216
|
81 |
|
if (! array_key_exists($component_name, $this->components)) { |
217
|
81 |
|
$component = sprintf('\AframeVR\Core\Components\%s\%sComponent', ucfirst($component_name), |
218
|
|
|
ucfirst($component_name)); |
219
|
81 |
|
if (class_exists($component)) { |
220
|
81 |
|
$this->components[$component_name] = new $component(); |
221
|
|
|
} else { |
222
|
1 |
|
throw new BadComponentCallException($component_name); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
81 |
|
return $this->components[$component_name] ?? null; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Handle entity components |
231
|
|
|
* |
232
|
|
|
* Since we might need to customize these to have |
233
|
|
|
* custom components loaded as $this->methosd aswell therefore |
234
|
|
|
* we have these placeholder magic methods here |
235
|
|
|
* |
236
|
|
|
* @param string $component_name |
237
|
|
|
* @param array $args |
238
|
|
|
*/ |
239
|
30 |
|
public function __call(string $component_name, array $args) |
240
|
|
|
{ |
241
|
30 |
|
if (! method_exists($this, $component_name)) { |
242
|
30 |
|
$this->{$component_name} = Closure::bind( |
243
|
30 |
|
function () use ($component_name) { |
244
|
30 |
|
return $this->component($component_name); |
245
|
30 |
|
}, $this, get_class()); |
246
|
|
|
} |
247
|
|
|
|
248
|
30 |
|
return call_user_func($this->{$component_name}, $args); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Create and add DOM element of the entity |
253
|
|
|
* |
254
|
|
|
* @param \DOMDocument $aframe_dom |
255
|
|
|
* @return \DOMElement |
256
|
|
|
*/ |
257
|
13 |
|
public function domElement(\DOMDocument &$aframe_dom): DOMElement |
258
|
|
|
{ |
259
|
13 |
|
$a_entity = $aframe_dom->createElement('a-entity'); |
260
|
|
|
|
261
|
13 |
|
$this->appendAttributes($a_entity); |
262
|
|
|
|
263
|
13 |
|
foreach ($this->components as $component) { |
264
|
|
|
/* |
265
|
|
|
* Check does component has any attributes to add to DOM element. |
266
|
|
|
* default attributes most of cases are ommited so we might not have any attributes to add |
267
|
|
|
*/ |
268
|
13 |
|
if ($component->hasDOMAttributes()) |
269
|
13 |
|
$a_entity->setAttributeNode($component->getDOMAttr()); |
270
|
|
|
} |
271
|
|
|
|
272
|
13 |
|
$this->appendChildren($aframe_dom, $a_entity); |
273
|
13 |
|
$this->appendAnimations($aframe_dom, $a_entity); |
274
|
13 |
|
return $a_entity; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Append DOM attributes no set by components |
279
|
|
|
* |
280
|
|
|
* @param \DOMElement $a_entity |
281
|
|
|
*/ |
282
|
13 |
|
private function appendAttributes(\DOMElement &$a_entity) |
283
|
|
|
{ |
284
|
13 |
|
foreach ($this->attrs as $attr => $val) { |
285
|
13 |
|
$this->setAttribute($a_entity, $attr, $val); |
286
|
|
|
} |
287
|
13 |
|
} |
288
|
|
|
|
289
|
13 |
|
private function setAttribute(&$a_entity, $attr, $val) |
290
|
|
|
{ |
291
|
13 |
|
if ($attr === 'id' && ($val === 'untitled' || is_numeric($val))) |
292
|
4 |
|
return; |
293
|
|
|
|
294
|
11 |
|
$a_entity->setAttribute($attr, $val); |
295
|
11 |
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Append childern to entities DOM element |
299
|
|
|
* |
300
|
|
|
* @param \DOMDocument $aframe_dom |
301
|
|
|
* @param \DOMElement $a_entity |
302
|
|
|
*/ |
303
|
13 |
|
private function appendChildren(\DOMDocument &$aframe_dom, \DOMElement &$a_entity) |
304
|
|
|
{ |
305
|
13 |
|
if ($this->childrenFactory instanceof EntityChildrenFactory) { |
306
|
2 |
|
foreach ($this->childrenFactory->getChildern() as $child) { |
307
|
2 |
|
$this->addFormatComment($aframe_dom, $a_entity, "\n\t"); |
308
|
2 |
|
$a_entity->appendChild($child->domElement($aframe_dom)); |
309
|
2 |
|
$this->addFormatComment($aframe_dom, $a_entity, ''); |
310
|
|
|
} |
311
|
|
|
} |
312
|
13 |
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Append childern to entities DOM element |
316
|
|
|
* |
317
|
|
|
* @param \DOMDocument $aframe_dom |
318
|
|
|
* @param \DOMElement $a_entity |
319
|
|
|
*/ |
320
|
13 |
|
private function appendAnimations(\DOMDocument &$aframe_dom, \DOMElement &$a_entity) |
321
|
|
|
{ |
322
|
13 |
|
foreach ($this->animations as $animations) { |
323
|
1 |
|
$this->addFormatComment($aframe_dom, $a_entity, "\n\t"); |
324
|
1 |
|
$a_entity->appendChild($animations->domElement($aframe_dom)); |
325
|
1 |
|
$this->addFormatComment($aframe_dom, $a_entity, ''); |
326
|
|
|
} |
327
|
13 |
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Add format comment |
331
|
|
|
* |
332
|
|
|
* @param \DOMDocument $aframe_dom |
333
|
|
|
* @param \DOMElement $a_entity |
334
|
|
|
* @param string $content |
335
|
|
|
*/ |
336
|
3 |
|
private function addFormatComment(\DOMDocument &$aframe_dom, \DOMElement &$a_entity, string $content) |
337
|
|
|
{ |
338
|
3 |
|
if ($aframe_dom->formatOutput) { |
339
|
1 |
|
$com = $aframe_dom->createComment($content); |
340
|
1 |
|
$a_entity->appendChild($com); |
341
|
|
|
} |
342
|
3 |
|
} |
343
|
|
|
} |
344
|
|
|
|