Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/2d/node_2d.cpp
9896 views
1
/**************************************************************************/
2
/* node_2d.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "node_2d.h"
32
33
#include "scene/main/viewport.h"
34
35
#ifdef TOOLS_ENABLED
36
Dictionary Node2D::_edit_get_state() const {
37
Dictionary state;
38
state["position"] = get_position();
39
state["rotation"] = get_rotation();
40
state["scale"] = get_scale();
41
state["skew"] = get_skew();
42
43
return state;
44
}
45
46
void Node2D::_edit_set_state(const Dictionary &p_state) {
47
position = p_state["position"];
48
rotation = p_state["rotation"];
49
scale = p_state["scale"];
50
skew = p_state["skew"];
51
52
_update_transform();
53
}
54
55
void Node2D::_edit_set_position(const Point2 &p_position) {
56
set_position(p_position);
57
}
58
59
Point2 Node2D::_edit_get_position() const {
60
return position;
61
}
62
63
void Node2D::_edit_set_scale(const Size2 &p_scale) {
64
set_scale(p_scale);
65
}
66
67
Size2 Node2D::_edit_get_scale() const {
68
return scale;
69
}
70
71
void Node2D::_edit_set_rotation(real_t p_rotation) {
72
rotation = p_rotation;
73
_update_transform();
74
}
75
76
real_t Node2D::_edit_get_rotation() const {
77
return rotation;
78
}
79
80
bool Node2D::_edit_use_rotation() const {
81
return true;
82
}
83
84
void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
85
ERR_FAIL_COND(!_edit_use_rect());
86
87
Rect2 r = _edit_get_rect();
88
89
Vector2 zero_offset;
90
Size2 new_scale(1, 1);
91
92
if (r.size.x != 0) {
93
zero_offset.x = -r.position.x / r.size.x;
94
new_scale.x = p_edit_rect.size.x / r.size.x;
95
}
96
97
if (r.size.y != 0) {
98
zero_offset.y = -r.position.y / r.size.y;
99
new_scale.y = p_edit_rect.size.y / r.size.y;
100
}
101
102
Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset;
103
104
Transform2D postxf;
105
postxf.set_rotation_scale_and_skew(rotation, scale, skew);
106
new_pos = postxf.xform(new_pos);
107
108
position += new_pos;
109
scale *= new_scale;
110
111
_update_transform();
112
}
113
#endif
114
115
void Node2D::_set_xform_dirty(bool p_dirty) const {
116
if (is_group_processing()) {
117
if (p_dirty) {
118
xform_dirty.mt.set();
119
} else {
120
xform_dirty.mt.clear();
121
}
122
} else {
123
xform_dirty.st = p_dirty;
124
}
125
}
126
127
void Node2D::_update_xform_values() const {
128
rotation = transform.get_rotation();
129
skew = transform.get_skew();
130
position = transform.columns[2];
131
scale = transform.get_scale();
132
_set_xform_dirty(false);
133
}
134
135
void Node2D::_update_transform() {
136
transform.set_rotation_scale_and_skew(rotation, scale, skew);
137
transform.columns[2] = position;
138
139
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
140
141
_notify_transform();
142
}
143
144
void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) {
145
ERR_THREAD_GUARD;
146
if (p_keep_global_transform) {
147
Transform2D temp = get_global_transform();
148
Node::reparent(p_parent);
149
set_global_transform(temp);
150
} else {
151
Node::reparent(p_parent);
152
}
153
}
154
155
void Node2D::set_position(const Point2 &p_pos) {
156
ERR_THREAD_GUARD;
157
if (_is_xform_dirty()) {
158
_update_xform_values();
159
}
160
position = p_pos;
161
_update_transform();
162
}
163
164
void Node2D::set_rotation(real_t p_radians) {
165
ERR_THREAD_GUARD;
166
if (_is_xform_dirty()) {
167
_update_xform_values();
168
}
169
rotation = p_radians;
170
_update_transform();
171
}
172
173
void Node2D::set_rotation_degrees(real_t p_degrees) {
174
ERR_THREAD_GUARD;
175
set_rotation(Math::deg_to_rad(p_degrees));
176
}
177
178
void Node2D::set_skew(real_t p_radians) {
179
ERR_THREAD_GUARD;
180
if (_is_xform_dirty()) {
181
_update_xform_values();
182
}
183
skew = p_radians;
184
_update_transform();
185
}
186
187
void Node2D::set_scale(const Size2 &p_scale) {
188
ERR_THREAD_GUARD;
189
if (_is_xform_dirty()) {
190
_update_xform_values();
191
}
192
scale = p_scale;
193
// Avoid having 0 scale values, can lead to errors in physics and rendering.
194
if (Math::is_zero_approx(scale.x)) {
195
scale.x = CMP_EPSILON;
196
}
197
if (Math::is_zero_approx(scale.y)) {
198
scale.y = CMP_EPSILON;
199
}
200
_update_transform();
201
}
202
203
Point2 Node2D::get_position() const {
204
ERR_READ_THREAD_GUARD_V(Point2());
205
if (_is_xform_dirty()) {
206
_update_xform_values();
207
}
208
209
return position;
210
}
211
212
real_t Node2D::get_rotation() const {
213
ERR_READ_THREAD_GUARD_V(0);
214
if (_is_xform_dirty()) {
215
_update_xform_values();
216
}
217
218
return rotation;
219
}
220
221
real_t Node2D::get_rotation_degrees() const {
222
ERR_READ_THREAD_GUARD_V(0);
223
return Math::rad_to_deg(get_rotation());
224
}
225
226
real_t Node2D::get_skew() const {
227
ERR_READ_THREAD_GUARD_V(0);
228
if (_is_xform_dirty()) {
229
_update_xform_values();
230
}
231
232
return skew;
233
}
234
235
Size2 Node2D::get_scale() const {
236
ERR_READ_THREAD_GUARD_V(Size2());
237
if (_is_xform_dirty()) {
238
_update_xform_values();
239
}
240
241
return scale;
242
}
243
244
Transform2D Node2D::get_transform() const {
245
ERR_READ_THREAD_GUARD_V(Transform2D());
246
return transform;
247
}
248
249
void Node2D::rotate(real_t p_radians) {
250
ERR_THREAD_GUARD;
251
set_rotation(get_rotation() + p_radians);
252
}
253
254
void Node2D::translate(const Vector2 &p_amount) {
255
ERR_THREAD_GUARD;
256
set_position(get_position() + p_amount);
257
}
258
259
void Node2D::global_translate(const Vector2 &p_amount) {
260
ERR_THREAD_GUARD;
261
set_global_position(get_global_position() + p_amount);
262
}
263
264
void Node2D::apply_scale(const Size2 &p_amount) {
265
ERR_THREAD_GUARD;
266
set_scale(get_scale() * p_amount);
267
}
268
269
void Node2D::move_x(real_t p_delta, bool p_scaled) {
270
ERR_THREAD_GUARD;
271
Transform2D t = get_transform();
272
Vector2 m = t[0];
273
if (!p_scaled) {
274
m.normalize();
275
}
276
set_position(t[2] + m * p_delta);
277
}
278
279
void Node2D::move_y(real_t p_delta, bool p_scaled) {
280
ERR_THREAD_GUARD;
281
Transform2D t = get_transform();
282
Vector2 m = t[1];
283
if (!p_scaled) {
284
m.normalize();
285
}
286
set_position(t[2] + m * p_delta);
287
}
288
289
Point2 Node2D::get_global_position() const {
290
ERR_READ_THREAD_GUARD_V(Point2());
291
return get_global_transform().get_origin();
292
}
293
294
void Node2D::set_global_position(const Point2 &p_pos) {
295
ERR_THREAD_GUARD;
296
CanvasItem *parent = get_parent_item();
297
if (parent) {
298
Transform2D inv = parent->get_global_transform().affine_inverse();
299
set_position(inv.xform(p_pos));
300
} else {
301
set_position(p_pos);
302
}
303
}
304
305
real_t Node2D::get_global_rotation() const {
306
ERR_READ_THREAD_GUARD_V(0);
307
return get_global_transform().get_rotation();
308
}
309
310
real_t Node2D::get_global_rotation_degrees() const {
311
ERR_READ_THREAD_GUARD_V(0);
312
return Math::rad_to_deg(get_global_rotation());
313
}
314
315
real_t Node2D::get_global_skew() const {
316
ERR_READ_THREAD_GUARD_V(0);
317
return get_global_transform().get_skew();
318
}
319
320
void Node2D::set_global_rotation(const real_t p_radians) {
321
ERR_THREAD_GUARD;
322
CanvasItem *parent = get_parent_item();
323
if (parent) {
324
Transform2D parent_global_transform = parent->get_global_transform();
325
Transform2D new_transform = parent_global_transform * get_transform();
326
new_transform.set_rotation(p_radians);
327
new_transform = parent_global_transform.affine_inverse() * new_transform;
328
set_rotation(new_transform.get_rotation());
329
} else {
330
set_rotation(p_radians);
331
}
332
}
333
334
void Node2D::set_global_rotation_degrees(const real_t p_degrees) {
335
ERR_THREAD_GUARD;
336
set_global_rotation(Math::deg_to_rad(p_degrees));
337
}
338
339
void Node2D::set_global_skew(const real_t p_radians) {
340
ERR_THREAD_GUARD;
341
CanvasItem *parent = get_parent_item();
342
if (parent) {
343
Transform2D parent_global_transform = parent->get_global_transform();
344
Transform2D new_transform = parent_global_transform * get_transform();
345
new_transform.set_skew(p_radians);
346
new_transform = parent_global_transform.affine_inverse() * new_transform;
347
set_skew(new_transform.get_skew());
348
} else {
349
set_skew(p_radians);
350
}
351
}
352
353
Size2 Node2D::get_global_scale() const {
354
ERR_READ_THREAD_GUARD_V(Size2());
355
return get_global_transform().get_scale();
356
}
357
358
void Node2D::set_global_scale(const Size2 &p_scale) {
359
ERR_THREAD_GUARD;
360
CanvasItem *parent = get_parent_item();
361
if (parent) {
362
Transform2D parent_global_transform = parent->get_global_transform();
363
Transform2D new_transform = parent_global_transform * get_transform();
364
new_transform.set_scale(p_scale);
365
new_transform = parent_global_transform.affine_inverse() * new_transform;
366
set_scale(new_transform.get_scale());
367
} else {
368
set_scale(p_scale);
369
}
370
}
371
372
void Node2D::set_transform(const Transform2D &p_transform) {
373
ERR_THREAD_GUARD;
374
transform = p_transform;
375
_set_xform_dirty(true);
376
377
if (!_is_using_identity_transform()) {
378
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
379
}
380
381
_notify_transform();
382
}
383
384
void Node2D::set_global_transform(const Transform2D &p_transform) {
385
ERR_THREAD_GUARD;
386
CanvasItem *parent = get_parent_item();
387
if (parent) {
388
set_transform(parent->get_global_transform().affine_inverse() * p_transform);
389
} else {
390
set_transform(p_transform);
391
}
392
}
393
394
Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const {
395
ERR_READ_THREAD_GUARD_V(Transform2D());
396
if (p_parent == this) {
397
return Transform2D();
398
}
399
400
Node2D *parent_2d = Object::cast_to<Node2D>(get_parent());
401
402
ERR_FAIL_NULL_V(parent_2d, Transform2D());
403
if (p_parent == parent_2d) {
404
return get_transform();
405
} else {
406
return parent_2d->get_relative_transform_to_parent(p_parent) * get_transform();
407
}
408
}
409
410
void Node2D::look_at(const Vector2 &p_pos) {
411
ERR_THREAD_GUARD;
412
rotate(get_angle_to(p_pos));
413
}
414
415
real_t Node2D::get_angle_to(const Vector2 &p_pos) const {
416
ERR_READ_THREAD_GUARD_V(0);
417
return (to_local(p_pos) * get_scale()).angle();
418
}
419
420
Point2 Node2D::to_local(Point2 p_global) const {
421
ERR_READ_THREAD_GUARD_V(Point2());
422
return get_global_transform().affine_inverse().xform(p_global);
423
}
424
425
Point2 Node2D::to_global(Point2 p_local) const {
426
ERR_READ_THREAD_GUARD_V(Point2());
427
return get_global_transform().xform(p_local);
428
}
429
430
void Node2D::_notification(int p_notification) {
431
switch (p_notification) {
432
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
433
RID ae = get_accessibility_element();
434
ERR_FAIL_COND(ae.is_null());
435
436
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_CONTAINER);
437
} break;
438
439
case NOTIFICATION_ENTER_TREE: {
440
ERR_MAIN_THREAD_GUARD;
441
442
if (get_viewport()) {
443
get_parent()->connect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty), CONNECT_REFERENCE_COUNTED);
444
}
445
} break;
446
case NOTIFICATION_EXIT_TREE: {
447
ERR_MAIN_THREAD_GUARD;
448
449
if (get_viewport()) {
450
get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty));
451
}
452
} break;
453
}
454
}
455
456
void Node2D::_bind_methods() {
457
ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position);
458
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation);
459
ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Node2D::set_rotation_degrees);
460
ClassDB::bind_method(D_METHOD("set_skew", "radians"), &Node2D::set_skew);
461
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node2D::set_scale);
462
463
ClassDB::bind_method(D_METHOD("get_position"), &Node2D::get_position);
464
ClassDB::bind_method(D_METHOD("get_rotation"), &Node2D::get_rotation);
465
ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node2D::get_rotation_degrees);
466
ClassDB::bind_method(D_METHOD("get_skew"), &Node2D::get_skew);
467
ClassDB::bind_method(D_METHOD("get_scale"), &Node2D::get_scale);
468
469
ClassDB::bind_method(D_METHOD("rotate", "radians"), &Node2D::rotate);
470
ClassDB::bind_method(D_METHOD("move_local_x", "delta", "scaled"), &Node2D::move_x, DEFVAL(false));
471
ClassDB::bind_method(D_METHOD("move_local_y", "delta", "scaled"), &Node2D::move_y, DEFVAL(false));
472
ClassDB::bind_method(D_METHOD("translate", "offset"), &Node2D::translate);
473
ClassDB::bind_method(D_METHOD("global_translate", "offset"), &Node2D::global_translate);
474
ClassDB::bind_method(D_METHOD("apply_scale", "ratio"), &Node2D::apply_scale);
475
476
ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node2D::set_global_position);
477
ClassDB::bind_method(D_METHOD("get_global_position"), &Node2D::get_global_position);
478
ClassDB::bind_method(D_METHOD("set_global_rotation", "radians"), &Node2D::set_global_rotation);
479
ClassDB::bind_method(D_METHOD("set_global_rotation_degrees", "degrees"), &Node2D::set_global_rotation_degrees);
480
ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node2D::get_global_rotation);
481
ClassDB::bind_method(D_METHOD("get_global_rotation_degrees"), &Node2D::get_global_rotation_degrees);
482
ClassDB::bind_method(D_METHOD("set_global_skew", "radians"), &Node2D::set_global_skew);
483
ClassDB::bind_method(D_METHOD("get_global_skew"), &Node2D::get_global_skew);
484
ClassDB::bind_method(D_METHOD("set_global_scale", "scale"), &Node2D::set_global_scale);
485
ClassDB::bind_method(D_METHOD("get_global_scale"), &Node2D::get_global_scale);
486
487
ClassDB::bind_method(D_METHOD("set_transform", "xform"), &Node2D::set_transform);
488
ClassDB::bind_method(D_METHOD("set_global_transform", "xform"), &Node2D::set_global_transform);
489
490
ClassDB::bind_method(D_METHOD("look_at", "point"), &Node2D::look_at);
491
ClassDB::bind_method(D_METHOD("get_angle_to", "point"), &Node2D::get_angle_to);
492
493
ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node2D::to_local);
494
ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node2D::to_global);
495
496
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
497
498
ADD_GROUP("Transform", "");
499
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,or_less,or_greater,hide_slider,suffix:px"), "set_position", "get_position");
500
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation");
501
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees");
502
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale");
503
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians_as_degrees"), "set_skew", "get_skew");
504
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_transform", "get_transform");
505
506
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position");
507
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation", PROPERTY_HINT_NONE, "radians_as_degrees", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation");
508
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation_degrees", "get_global_rotation_degrees");
509
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_scale", "get_global_scale");
510
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_skew", PROPERTY_HINT_NONE, "radians_as_degrees", PROPERTY_USAGE_NONE), "set_global_skew", "get_global_skew");
511
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
512
}
513
514