mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2024-12-28 16:06:20 +01:00
688 lines
No EOL
18 KiB
Text
688 lines
No EOL
18 KiB
Text
function __Bone(_parent = noone, _distance = 0, _direction = 0, _angle = 0, _length = 0, _node = noone) constructor {
|
|
ID = UUID_generate();
|
|
name = "New bone";
|
|
node = _node;
|
|
parent = _parent;
|
|
|
|
distance = _distance; pose_distance = _distance;
|
|
direction = _direction; pose_direction = _direction;
|
|
angle = _angle; pose_angle = _angle;
|
|
length = _length; pose_length = _length;
|
|
|
|
pose_posit = [ 0, 0 ];
|
|
pose_rotate = 0;
|
|
pose_scale = 1;
|
|
|
|
pose_local_posit = [ 0, 0 ]; pose_apply_posit = [ 0, 0 ];
|
|
pose_local_rotate = 0; pose_apply_rotate = 0;
|
|
pose_local_scale = 1; pose_apply_scale = 1;
|
|
|
|
bone_head_init = new __vec2(); bone_head_pose = new __vec2();
|
|
bone_tail_init = new __vec2(); bone_tail_pose = new __vec2();
|
|
|
|
apply_scale = true;
|
|
apply_rotation = true;
|
|
|
|
childs = [];
|
|
is_main = false;
|
|
parent_anchor = true;
|
|
|
|
tb_name = new textBox(TEXTBOX_INPUT.text, function(_name) /*=>*/ { name = _name; if(node) node.triggerRender(); });
|
|
tb_name.font = f_p2;
|
|
tb_name.hide = true;
|
|
|
|
updated = false;
|
|
|
|
IKlength = 0;
|
|
IKTargetID = "";
|
|
IKTarget = noone;
|
|
|
|
constrains = [];
|
|
|
|
freeze_data = {};
|
|
|
|
control_x0 = 0; control_y0 = 0; control_i0 = 0;
|
|
control_x1 = 0; control_y1 = 0; control_i1 = 0;
|
|
|
|
static addChild = function(bone) { array_push(childs, bone); bone.parent = self; return self; }
|
|
static childCount = function() { return array_reduce(childs, function(amo, ch) /*=>*/ { return amo + ch.childCount(); }, array_length(childs)); }
|
|
|
|
static freeze = function() {
|
|
freeze_data = { angle, length, distance, direction };
|
|
array_foreach(childs, function(c) /*=>*/ {return c.freeze()});
|
|
}
|
|
|
|
////- Find
|
|
|
|
static findBone = function(_id) {
|
|
if(ID == _id) return self;
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ ) {
|
|
var b = childs[i].findBone(_id);
|
|
if(b != noone) return b;
|
|
}
|
|
|
|
return noone;
|
|
}
|
|
|
|
static findBoneByName = function(_name) {
|
|
if(string_trim(name) == string_trim(_name))
|
|
return self;
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ ) {
|
|
var b = childs[i].findBoneByName(_name);
|
|
if(b != noone) return b;
|
|
}
|
|
|
|
return noone;
|
|
}
|
|
|
|
////- Get position
|
|
|
|
static getHead = function(pose = true) { return pose? bone_head_pose.clone() : bone_head_init.clone(); }
|
|
static getTail = function(pose = true) { return pose? bone_tail_pose.clone() : bone_tail_init.clone(); }
|
|
|
|
static getPoint = function(progress, pose = true) {
|
|
var _dir, _dis, _len, _ang;
|
|
|
|
if(pose) {
|
|
_dir = pose_direction;
|
|
_dis = pose_distance;
|
|
|
|
_len = pose_length;
|
|
_ang = pose_angle;
|
|
|
|
} else {
|
|
_dir = direction;
|
|
_dis = distance;
|
|
|
|
_len = length;
|
|
_ang = angle;
|
|
}
|
|
|
|
var len = _len * progress;
|
|
|
|
var _dx = lengthdir_x(_dis, _dir), _dy = lengthdir_y(_dis, _dir);
|
|
var _lx = lengthdir_x( len, _ang), _ly = lengthdir_y( len, _ang);
|
|
|
|
if(parent == noone)
|
|
return new __vec2(_dx, _dy)
|
|
.addElement(_lx, _ly);
|
|
|
|
if(parent_anchor)
|
|
return parent.getTail(pose)
|
|
.addElement(_lx, _ly);
|
|
|
|
return parent.getHead(pose)
|
|
.addElement(_dx, _dy)
|
|
.addElement(_lx, _ly);
|
|
}
|
|
|
|
////- Draw
|
|
|
|
static draw = function(attributes, edit = false, _x = 0, _y = 0, _s = 1, _mx = 0, _my = 0, _hover = noone, _select = noone, _blend = c_white, _alpha = 1) {
|
|
setControl(_x, _y, _s);
|
|
|
|
var hover = noone, h;
|
|
|
|
if(parent != noone) {
|
|
h = drawBone(attributes, edit, _x, _y, _s, _mx, _my, _hover, _select, _blend, _alpha);
|
|
if(h != noone) hover = h;
|
|
}
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ ) {
|
|
h = childs[i].draw(attributes, edit, _x, _y, _s, _mx, _my, _hover, _select, _blend, _alpha);
|
|
if(h == noone) continue;
|
|
|
|
if(hover == noone || IKlength) hover = h;
|
|
if(h[1] != 2) hover = h;
|
|
}
|
|
|
|
return hover;
|
|
}
|
|
|
|
static drawBone = function(attributes, edit = false, _x = 0, _y = 0, _s = 1, _mx = 0, _my = 0, _hover = noone, _select = noone, _blend = c_white, _alpha = 1) {
|
|
var hover = noone;
|
|
|
|
var p0x = _x + bone_head_pose.x * _s;
|
|
var p0y = _y + bone_head_pose.y * _s;
|
|
var p1x = _x + bone_tail_pose.x * _s;
|
|
var p1y = _y + bone_tail_pose.y * _s;
|
|
|
|
if(_select && _select.ID == self.ID) {
|
|
draw_set_color(COLORS._main_value_positive);
|
|
draw_set_alpha(0.75 * _alpha);
|
|
|
|
} else if(_hover != noone && _hover[0].ID == self.ID && _hover[1] == 2) {
|
|
draw_set_color(c_white);
|
|
draw_set_alpha(0.75 * _alpha);
|
|
|
|
} else {
|
|
draw_set_color(COLORS._main_accent);
|
|
draw_set_alpha(0.75 * _alpha);
|
|
}
|
|
|
|
if(IKlength == 0) {
|
|
if(pose_rotate != 0) {
|
|
var nx = p0x + lengthdir_x(16, angle + pose_rotate);
|
|
var ny = p0y + lengthdir_y(16, angle + pose_rotate);
|
|
|
|
draw_line_width(p0x, p0y, nx, ny, 2);
|
|
}
|
|
|
|
if(!parent_anchor && parent.parent != noone) {
|
|
var _p = parent.getTail();
|
|
var _px = _x + _p.x * _s;
|
|
var _py = _y + _p.y * _s;
|
|
draw_line_dashed(_px, _py, p0x, p0y, 2, 8);
|
|
}
|
|
|
|
if(attributes.display_bone == 0) {
|
|
var _ppx = lerp(p0x, p1x, 0.2);
|
|
var _ppy = lerp(p0y, p1y, 0.2);
|
|
var _prr = point_direction(p0x, p0y, p1x, p1y) + 90;
|
|
var _prx = lengthdir_x(6 * pose_scale, _prr);
|
|
var _pry = lengthdir_y(6 * pose_scale, _prr);
|
|
|
|
draw_primitive_begin(pr_trianglelist);
|
|
draw_vertex(p0x, p0y);
|
|
draw_vertex(_ppx, _ppy);
|
|
draw_vertex(_ppx + _prx, _ppy + _pry);
|
|
|
|
draw_vertex(p0x, p0y);
|
|
draw_vertex(_ppx, _ppy);
|
|
draw_vertex(_ppx - _prx, _ppy - _pry);
|
|
|
|
draw_vertex(p1x, p1y);
|
|
draw_vertex(_ppx, _ppy);
|
|
draw_vertex(_ppx + _prx, _ppy + _pry);
|
|
|
|
draw_vertex(p1x, p1y);
|
|
draw_vertex(_ppx, _ppy);
|
|
draw_vertex(_ppx - _prx, _ppy - _pry);
|
|
draw_primitive_end();
|
|
|
|
if((edit & 0b100) && distance_to_line(_mx, _my, p0x, p0y, p1x, p1y) <= 12) //drag bone
|
|
hover = [ self, 2, bone_head_pose ];
|
|
|
|
} else if(attributes.display_bone == 1) {
|
|
draw_line_width(p0x, p0y, p1x, p1y, 3);
|
|
|
|
if((edit & 0b100) && distance_to_line(_mx, _my, p0x, p0y, p1x, p1y) <= 6) //drag bone
|
|
hover = [ self, 2, bone_head_pose ];
|
|
}
|
|
|
|
} else {
|
|
draw_set_color(c_white);
|
|
if(!parent_anchor && parent.parent != noone) {
|
|
var _p = parent.getTail();
|
|
var _px = _x + _p.x * _s;
|
|
var _py = _y + _p.y * _s;
|
|
draw_line_dashed(_px, _py, p0x, p0y, 1);
|
|
}
|
|
|
|
draw_sprite_ui(THEME.preview_bone_IK, 0, p0x, p0y,,,, COLORS._main_accent, draw_get_alpha());
|
|
|
|
if((edit & 0b100) && point_in_circle(_mx, _my, p0x, p0y, 24))
|
|
hover = [ self, 2, bone_head_pose ];
|
|
}
|
|
draw_set_alpha(1);
|
|
|
|
if(attributes.display_name && IKlength == 0) {
|
|
if(abs(p0y - p1y) < abs(p0x - p1x)) {
|
|
draw_set_text(f_p3, fa_center, fa_bottom, COLORS._main_accent);
|
|
draw_text_add((p0x + p1x) / 2, (p0y + p1y) / 2 - 4, name);
|
|
|
|
} else {
|
|
draw_set_text(f_p3, fa_left, fa_center, COLORS._main_accent);
|
|
draw_text_add((p0x + p1x) / 2 + 4, (p0y + p1y) / 2, name);
|
|
}
|
|
}
|
|
|
|
if(IKlength == 0) {
|
|
if(!parent_anchor) {
|
|
control_i0 = (_hover != noone && _hover[0] == self && _hover[1] == 0)? 1 : 0;
|
|
|
|
if((edit & 0b001) && point_in_circle(_mx, _my, p0x, p0y, ui(16))) //drag head
|
|
hover = [ self, 0, bone_head_pose ];
|
|
}
|
|
|
|
control_i1 = (_hover != noone && _hover[0] == self && _hover[1] == 1)? 1 : 0;
|
|
|
|
if((edit & 0b010) && point_in_circle(_mx, _my, p1x, p1y, ui(16))) //drag tail
|
|
hover = [ self, 1, bone_tail_pose ];
|
|
}
|
|
|
|
return hover;
|
|
}
|
|
|
|
static drawThumbnail = function(_s, _bbox, _bone_bbox = undefined) {
|
|
_bone_bbox ??= bbox();
|
|
|
|
if(!is_main && is_array(_bone_bbox)) {
|
|
var boxs = min(_bbox.w / _bone_bbox[4], _bbox.h / _bone_bbox[5]);
|
|
|
|
_bbox.w = boxs * _bone_bbox[4];
|
|
_bbox.h = boxs * _bone_bbox[5];
|
|
|
|
_bbox.x0 = _bbox.xc - _bbox.w / 2;
|
|
_bbox.x1 = _bbox.xc + _bbox.w / 2;
|
|
|
|
_bbox.y0 = _bbox.yc - _bbox.h / 2;
|
|
_bbox.y1 = _bbox.yc + _bbox.h / 2;
|
|
|
|
var p0x = _bbox.x0 + _bbox.w * (bone_head_pose.x - _bone_bbox[0]) / _bone_bbox[4];
|
|
var p0y = _bbox.y0 + _bbox.h * (bone_head_pose.y - _bone_bbox[1]) / _bone_bbox[5];
|
|
var p1x = _bbox.x0 + _bbox.w * (bone_tail_pose.x - _bone_bbox[0]) / _bone_bbox[4];
|
|
var p1y = _bbox.y0 + _bbox.h * (bone_tail_pose.y - _bone_bbox[1]) / _bone_bbox[5];
|
|
|
|
draw_set_circle_precision(8);
|
|
|
|
draw_set_color(COLORS._main_accent);
|
|
draw_line_width(p0x, p0y, p1x, p1y, 1.5 * _s);
|
|
|
|
draw_set_color(COLORS._main_icon_dark);
|
|
draw_circle(p0x, p0y, 4 * _s, false);
|
|
draw_circle(p1x, p1y, 4 * _s, false);
|
|
|
|
draw_set_color(COLORS._main_accent);
|
|
draw_circle(p0x, p0y, 2 * _s, false);
|
|
draw_circle(p1x, p1y, 2 * _s, false);
|
|
}
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].drawThumbnail(_s, _bbox, _bone_bbox);
|
|
}
|
|
|
|
static setControl = function(_x = 0, _y = 0, _s = 1) {
|
|
control_x0 = _x + bone_head_pose.x * _s;
|
|
control_y0 = _y + bone_head_pose.y * _s;
|
|
control_x1 = _x + bone_tail_pose.x * _s;
|
|
control_y1 = _y + bone_tail_pose.y * _s;
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].setControl(_x, _y, _s);
|
|
}
|
|
|
|
static drawControl = function(attributes) {
|
|
if(parent != noone && IKlength == 0) {
|
|
if(!parent_anchor)
|
|
draw_anchor(control_i0 * .5, control_x0, control_y0, 8, attributes.display_bone);
|
|
draw_anchor(control_i1 * .5, control_x1, control_y1, 8, attributes.display_bone);
|
|
}
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].drawControl(attributes);
|
|
}
|
|
|
|
////- Pose
|
|
|
|
static resetPose = function() {
|
|
pose_distance = distance;
|
|
pose_direction = direction;
|
|
pose_angle = angle;
|
|
pose_length = length;
|
|
|
|
pose_posit = [ 0, 0 ];
|
|
pose_rotate = 0;
|
|
pose_scale = 1;
|
|
|
|
pose_local_posit = [ 0, 0 ];
|
|
pose_local_rotate = 0;
|
|
pose_local_scale = 1;
|
|
|
|
pose_apply_posit = [ 0, 0 ];
|
|
pose_apply_rotate = 0;
|
|
pose_apply_scale = 1;
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].resetPose();
|
|
|
|
return self;
|
|
}
|
|
|
|
static __setPosition = function() {
|
|
bone_head_init = getPoint(0, false);
|
|
bone_head_pose = getPoint(0, true);
|
|
bone_tail_init = getPoint(1, false);
|
|
bone_tail_pose = getPoint(1, true);
|
|
}
|
|
|
|
static setPosition = function() {
|
|
__setPosition();
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].setPosition();
|
|
|
|
return self;
|
|
}
|
|
|
|
static setPose = function(_ik = true) {
|
|
__c_bone = self;
|
|
|
|
setPosition();
|
|
setPoseTransform();
|
|
if(_ik) { setPosition(); setIKconstrain(); }
|
|
array_foreach(constrains, function(c) /*=>*/ {return c.constrain(__c_bone)});
|
|
setPosition();
|
|
|
|
return self;
|
|
}
|
|
|
|
static setPoseTransform = function() {
|
|
if(is_main) {
|
|
array_foreach(childs, function(c) /*=>*/ {return c.setPoseTransform()});
|
|
return;
|
|
}
|
|
|
|
pose_apply_posit = [ pose_posit[0], pose_posit[1] ];
|
|
pose_apply_rotate = pose_rotate;
|
|
pose_apply_scale = pose_scale;
|
|
|
|
if(parent) { // do this instead of recursion.
|
|
pose_local_posit = parent.pose_apply_posit;
|
|
pose_local_rotate = parent.pose_apply_rotate;
|
|
pose_local_scale = parent.pose_apply_scale;
|
|
|
|
pose_apply_posit[0] += pose_local_posit[0];
|
|
pose_apply_posit[1] += pose_local_posit[1];
|
|
pose_apply_rotate += pose_local_rotate;
|
|
pose_apply_scale *= pose_local_scale;
|
|
}
|
|
|
|
var ldx = lengthdir_x(distance, direction) + pose_posit[0];
|
|
var ldy = lengthdir_y(distance, direction) + pose_posit[1];
|
|
|
|
pose_direction = point_direction(0, 0, ldx, ldy) + pose_local_rotate;
|
|
pose_distance = point_distance(0, 0, ldx, ldy) * pose_local_scale;
|
|
|
|
pose_angle = angle + pose_apply_rotate;
|
|
pose_length = length * pose_apply_scale;
|
|
|
|
array_foreach(childs, function(c) /*=>*/ {return c.setPoseTransform()});
|
|
}
|
|
|
|
////- IK
|
|
|
|
static setIKconstrain = function() {
|
|
if(IKlength > 0 && IKTarget != noone) {
|
|
var points = array_create(IKlength + 1);
|
|
var lengths = array_create(IKlength);
|
|
var bones = array_create(IKlength);
|
|
var bn = IKTarget;
|
|
|
|
for( var i = IKlength; i > 0; i-- ) {
|
|
var _p = bn.getTail();
|
|
bones[i - 1] = bn;
|
|
points[i] = { x: _p.x, y: _p.y };
|
|
bn = bn.parent;
|
|
}
|
|
|
|
_p = bn.getTail();
|
|
points[0] = { x: _p.x, y: _p.y };
|
|
|
|
for( var i = 0; i < IKlength; i++ ) {
|
|
var p0 = points[i];
|
|
var p1 = points[i + 1];
|
|
|
|
lengths[i] = point_distance(p0.x, p0.y, p1.x, p1.y);
|
|
}
|
|
|
|
var p = parent.getHead();
|
|
var px = p.x + lengthdir_x(pose_distance, pose_direction);
|
|
var py = p.y + lengthdir_y(pose_distance, pose_direction);
|
|
|
|
FABRIK(bones, points, lengths, px, py);
|
|
}
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].setIKconstrain();
|
|
}
|
|
|
|
FABRIK_result = [];
|
|
static FABRIK = function(bones, points, lengths, dx, dy) {
|
|
|
|
var threshold = 0.01;
|
|
var _bo = array_create(array_length(points));
|
|
for( var i = 0, n = array_length(points); i < n; i++ )
|
|
_bo[i] = { x: points[i].x, y: points[i].y };
|
|
|
|
var sx = points[0].x;
|
|
var sy = points[0].y;
|
|
var itr = 0;
|
|
|
|
do {
|
|
FABRIK_backward(bones, points, lengths, dx, dy);
|
|
FABRIK_forward(bones, points, lengths, sx, sy);
|
|
|
|
var delta = 0;
|
|
var _bn = array_create(array_length(points));
|
|
for( var i = 0, n = array_length(points); i < n; i++ ) {
|
|
_bn[i] = { x: points[i].x, y: points[i].y };
|
|
delta += point_distance(_bo[i].x, _bo[i].y, _bn[i].x, _bn[i].y);
|
|
}
|
|
|
|
_bo = _bn;
|
|
if(++itr >= 64) break;
|
|
} until(delta <= threshold);
|
|
|
|
for( var i = 0, n = array_length(points) - 1; i < n; i++ ) {
|
|
var _b = bones[i];
|
|
var p0 = points[i];
|
|
var p1 = points[i + 1];
|
|
|
|
var dir = point_direction(p0.x, p0.y, p1.x, p1.y);
|
|
var dis = point_distance( p0.x, p0.y, p1.x, p1.y);
|
|
|
|
_b.pose_angle = dir;
|
|
|
|
FABRIK_result[i] = p0;
|
|
}
|
|
|
|
FABRIK_result[i] = p1;
|
|
|
|
}
|
|
|
|
static FABRIK_backward = function(bones, points, lengths, dx, dy) {
|
|
var tx = dx;
|
|
var ty = dy;
|
|
|
|
for( var i = array_length(points) - 1; i > 0; i-- ) {
|
|
var p1 = points[i];
|
|
var p0 = points[i - 1];
|
|
var len = lengths[i - 1];
|
|
var dir = point_direction(tx, ty, p0.x, p0.y);
|
|
|
|
p1.x = tx;
|
|
p1.y = ty;
|
|
|
|
p0.x = p1.x + lengthdir_x(len, dir);
|
|
p0.y = p1.y + lengthdir_y(len, dir);
|
|
|
|
tx = p0.x;
|
|
ty = p0.y;
|
|
}
|
|
}
|
|
|
|
static FABRIK_forward = function(bones, points, lengths, sx, sy) {
|
|
var tx = sx;
|
|
var ty = sy;
|
|
|
|
for( var i = 0, n = array_length(points) - 1; i < n; i++ ) {
|
|
var _b = bones[i];
|
|
var p0 = points[i];
|
|
var p1 = points[i + 1];
|
|
var len = lengths[i];
|
|
var dir = point_direction(tx, ty, p1.x, p1.y);
|
|
|
|
p0.x = tx;
|
|
p0.y = ty;
|
|
|
|
p1.x = p0.x + lengthdir_x(len, dir);
|
|
p1.y = p0.y + lengthdir_y(len, dir);
|
|
|
|
tx = p1.x;
|
|
ty = p1.y;
|
|
}
|
|
}
|
|
|
|
static __getBBOX = function() {
|
|
if(is_main) return noone;
|
|
|
|
var p0 = bone_head_pose;
|
|
var p1 = bone_tail_pose;
|
|
|
|
var x0 = min(p0.x, p1.x);
|
|
var y0 = min(p0.y, p1.y);
|
|
var x1 = max(p0.x, p1.x);
|
|
var y1 = max(p0.y, p1.y);
|
|
|
|
return [ x0, y0, x1, y1, 0, 0 ];
|
|
}
|
|
|
|
static bbox = function() {
|
|
var _bbox = __getBBOX();
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ ) {
|
|
var _bbox_ch = childs[i].bbox();
|
|
|
|
if(is_array(_bbox)) {
|
|
_bbox[0] = min(_bbox[0], _bbox_ch[0]);
|
|
_bbox[1] = min(_bbox[1], _bbox_ch[1]);
|
|
_bbox[2] = max(_bbox[2], _bbox_ch[2]);
|
|
_bbox[3] = max(_bbox[3], _bbox_ch[3]);
|
|
|
|
} else _bbox = _bbox_ch;
|
|
}
|
|
|
|
if(is_array(_bbox)) {
|
|
_bbox[4] = _bbox[2] - _bbox[0];
|
|
_bbox[5] = _bbox[3] - _bbox[1];
|
|
}
|
|
|
|
return _bbox;
|
|
}
|
|
|
|
////- Serialize
|
|
|
|
static serialize = function() {
|
|
var bone = {};
|
|
|
|
bone.ID = ID;
|
|
bone.name = name;
|
|
bone.distance = distance;
|
|
bone.direction = direction;
|
|
bone.angle = angle;
|
|
bone.length = length;
|
|
|
|
bone.is_main = is_main;
|
|
bone.parent_anchor = parent_anchor;
|
|
|
|
bone.IKlength = IKlength;
|
|
bone.IKTargetID = IKTargetID;
|
|
|
|
bone.apply_rotation = apply_rotation;
|
|
bone.apply_scale = apply_scale;
|
|
|
|
bone.constrains = array_map(constrains, function(c) /*=>*/ {return c.serialize()});
|
|
|
|
bone.childs = [];
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
bone.childs[i] = childs[i].serialize();
|
|
|
|
return bone;
|
|
}
|
|
|
|
static deserialize = function(bone, node) {
|
|
ID = bone.ID;
|
|
name = bone.name;
|
|
distance = bone.distance;
|
|
direction = bone.direction;
|
|
angle = bone.angle;
|
|
length = bone.length;
|
|
|
|
is_main = bone.is_main;
|
|
parent_anchor = bone.parent_anchor;
|
|
|
|
self.node = node;
|
|
|
|
IKlength = bone.IKlength;
|
|
IKTargetID = struct_try_get(bone, "IKTargetID", "");
|
|
|
|
apply_rotation = bone.apply_rotation;
|
|
apply_scale = bone.apply_scale;
|
|
|
|
if(struct_has(bone, "constrains")) {
|
|
__b = self;
|
|
constrains = array_filter(array_map(bone.constrains, function(c) /*=>*/ {return new __Bone_Constrain(__b).deserialize(c)}), function(c) /*=>*/ {return c != noone});
|
|
}
|
|
|
|
childs = [];
|
|
for( var i = 0, n = array_length(bone.childs); i < n; i++ )
|
|
addChild(new __Bone().deserialize(bone.childs[i], node));
|
|
|
|
return self;
|
|
}
|
|
|
|
static connect = function() {
|
|
IKTarget = noone;
|
|
if(parent != noone && IKTargetID != "")
|
|
IKTarget = parent.findBone(IKTargetID);
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].connect();
|
|
|
|
return self;
|
|
}
|
|
|
|
static clone = function() {
|
|
var _b = new __Bone(parent, distance, direction, angle, length);
|
|
|
|
_b.angle = angle;
|
|
_b.length = length;
|
|
_b.distance = distance;
|
|
_b.direction = direction;
|
|
|
|
_b.ID = ID;
|
|
_b.name = name;
|
|
_b.is_main = is_main;
|
|
_b.parent_anchor = parent_anchor;
|
|
|
|
_b.IKlength = IKlength;
|
|
_b.IKTargetID = IKTargetID;
|
|
|
|
_b.apply_rotation = apply_rotation;
|
|
_b.apply_scale = apply_scale;
|
|
|
|
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
_b.addChild(childs[i].clone());
|
|
|
|
__b = _b;
|
|
_b.constrains = array_map(constrains, function(c) /*=>*/ {return new __Bone_Constrain(__b).deserialize(c.serialize())});
|
|
|
|
return _b;
|
|
}
|
|
|
|
////- Actions
|
|
|
|
static toString = function() { return $"Bone {name} [{ID}] : [{direction}, {distance}] / [{angle}, {length}]"; }
|
|
|
|
static toArray = function(arr = []) {
|
|
if(!is_main) array_push(arr, self);
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childs[i].toArray(arr);
|
|
|
|
return arr;
|
|
}
|
|
|
|
static getHash = function() {
|
|
var childHash = "";
|
|
for( var i = 0, n = array_length(childs); i < n; i++ )
|
|
childHash += childs[i].getHash() + ",";
|
|
|
|
var h = $"{name} [{ID}] : {parent_anchor}, [{IKlength}, {IKTargetID}], [{apply_scale}, {apply_rotation}], [{childHash}]";
|
|
return sha1_string_unicode(h);
|
|
}
|
|
|
|
} |