mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2024-11-13 05:53:53 +01:00
464 lines
12 KiB
Plaintext
464 lines
12 KiB
Plaintext
function __Bone(parent = noone, distance = 0, direction = 0, angle = 0, length = 0, attributes = {}, node = noone) constructor {
|
|
id = UUID_generate();
|
|
self.name = "New bone";
|
|
self.distance = distance;
|
|
self.direction = direction;
|
|
self.angle = angle;
|
|
self.length = length;
|
|
self.node = node;
|
|
|
|
pose_angle = 0;
|
|
pose_scale = 1;
|
|
pose_posit = [ 0, 0 ];
|
|
pose_local_angle = 0;
|
|
pose_local_scale = 1;
|
|
pose_local_posit = [ 0, 0 ];
|
|
|
|
self.is_main = false;
|
|
self.parent_anchor = true;
|
|
self.childs = [];
|
|
|
|
tb_name = new textBox(TEXTBOX_INPUT.text,
|
|
function(_name) {
|
|
name = _name;
|
|
if(node) node.triggerRender();
|
|
});
|
|
tb_name.font = f_p2;
|
|
tb_name.hide = true;
|
|
|
|
self.attributes = attributes;
|
|
updated = false;
|
|
|
|
IKlength = 0;
|
|
IKTarget = noone;
|
|
|
|
freeze_data = {};
|
|
|
|
self.parent = parent;
|
|
if(parent != noone) {
|
|
distance = parent.length;
|
|
direction = parent.angle;
|
|
}
|
|
|
|
static addChild = function(bone) {
|
|
array_push(childs, bone);
|
|
bone.parent = self;
|
|
return self;
|
|
}
|
|
|
|
static childCount = function() {
|
|
var amo = array_length(childs);
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
amo += childs[i].childCount();
|
|
return amo;
|
|
}
|
|
|
|
static freeze = function() {
|
|
freeze_data = {
|
|
angle: angle,
|
|
length: length,
|
|
distance: distance,
|
|
direction: direction
|
|
}
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].freeze();
|
|
}
|
|
|
|
static findBone = function(_id) {
|
|
if(id == _id)
|
|
return self;
|
|
|
|
for( var i = 0; i < array_length(childs); i++ ) {
|
|
var b = childs[i].findBone(_id);
|
|
if(b != noone)
|
|
return b;
|
|
}
|
|
|
|
return noone;
|
|
}
|
|
|
|
static getPoint = function(progress) {
|
|
var len = length * progress;
|
|
|
|
if(parent == noone)
|
|
return new Point(lengthdir_x(distance, direction), lengthdir_y(distance, direction))
|
|
.add(lengthdir_x(len, angle), lengthdir_y(len, angle));
|
|
|
|
if(parent_anchor) {
|
|
var p = parent.getPoint(1)
|
|
.add(lengthdir_x(len, angle), lengthdir_y(len, angle))
|
|
return p;
|
|
}
|
|
|
|
var p = parent.getPoint(0)
|
|
.add(lengthdir_x(distance, direction), lengthdir_y(distance, direction))
|
|
.add(lengthdir_x(len, angle), lengthdir_y(len, angle))
|
|
return p;
|
|
}
|
|
|
|
static draw = function(edit = false, _x = 0, _y = 0, _s = 1, _mx = 0, _my = 0, hovering = noone, selecting = noone) {
|
|
var hover = _drawBone(edit, _x, _y, _s, _mx, _my, hovering, selecting);
|
|
drawControl();
|
|
return hover;
|
|
}
|
|
|
|
control_x0 = 0; control_y0 = 0; control_i0 = 0;
|
|
control_x1 = 0; control_y1 = 0; control_i1 = 0;
|
|
|
|
static _drawBone = function(edit = false, _x = 0, _y = 0, _s = 1, _mx = 0, _my = 0, hovering = noone, selecting = noone) {
|
|
var hover = noone;
|
|
|
|
var p0 = getPoint(0);
|
|
var p1 = getPoint(1);
|
|
|
|
p0.x = _x + p0.x * _s;
|
|
p0.y = _y + p0.y * _s;
|
|
p1.x = _x + p1.x * _s;
|
|
p1.y = _y + p1.y * _s;
|
|
|
|
control_x0 = p0.x; control_y0 = p0.y;
|
|
control_x1 = p1.x; control_y1 = p1.y;
|
|
|
|
if(parent != noone) {
|
|
|
|
if(hovering != noone && hovering[0] == self && hovering[1] == 2) {
|
|
draw_set_color(c_white);
|
|
draw_set_alpha(1);
|
|
} else if(selecting == self) {
|
|
draw_set_color(COLORS._main_value_positive);
|
|
draw_set_alpha(0.75);
|
|
} else {
|
|
draw_set_color(COLORS._main_accent);
|
|
draw_set_alpha(0.75);
|
|
}
|
|
|
|
if(IKlength == 0) {
|
|
if(!parent_anchor && parent.parent != noone) {
|
|
var _p = parent.getPoint(0);
|
|
_p.x = _x + _p.x * _s;
|
|
_p.y = _y + _p.y * _s;
|
|
draw_line_dashed(_p.x, _p.y, p0.x, p0.y, 1);
|
|
}
|
|
|
|
var _ppx = lerp(p0.x, p1.x, 0.2);
|
|
var _ppy = lerp(p0.y, p1.y, 0.2);
|
|
draw_line_width2(p0.x, p0.y, _ppx, _ppy, 2, 12);
|
|
draw_line_width2(_ppx, _ppy, p1.x, p1.y, 12, 2);
|
|
|
|
if((edit & 0b100) && distance_to_line(_mx, _my, p0.x, p0.y, p1.x, p1.y) <= 12) //drag bone
|
|
hover = [ self, 2 ];
|
|
} else {
|
|
draw_set_color(c_white);
|
|
if(!parent_anchor && parent.parent != noone) {
|
|
var _p = parent.getPoint(1);
|
|
_p.x = _x + _p.x * _s;
|
|
_p.y = _y + _p.y * _s;
|
|
draw_line_dashed(_p.x, _p.y, p0.x, p0.y, 1);
|
|
}
|
|
|
|
draw_sprite_ui(THEME.preview_bone_IK, 0, p0.x, p0.y,,,, c_white, draw_get_alpha());
|
|
|
|
if((edit & 0b100) && point_in_circle(_mx, _my, p0.x, p0.y, 24))
|
|
hover = [ self, 2 ];
|
|
}
|
|
draw_set_alpha(1.00);
|
|
|
|
if(attributes.display_name && IKlength == 0) {
|
|
if(abs(p0.y - p1.y) < abs(p0.x - p1.x)) {
|
|
draw_set_text(f_p2, fa_center, fa_bottom, COLORS._main_accent);
|
|
draw_text_add((p0.x + p1.x) / 2, (p0.y + p1.y) / 2 - 4, name);
|
|
} else {
|
|
draw_set_text(f_p2, fa_left, fa_center, COLORS._main_accent);
|
|
draw_text_add((p0.x + p1.x) / 2 + 4, (p0.y + p1.y) / 2, name);
|
|
}
|
|
}
|
|
|
|
if(IKlength == 0) {
|
|
if(!parent_anchor) {
|
|
control_i0 = (hovering != noone && hovering[0] == self && hovering[1] == 0)? 0 : 2;
|
|
|
|
if((edit & 0b001) && point_in_circle(_mx, _my, p0.x, p0.y, ui(16))) //drag head
|
|
hover = [ self, 0 ];
|
|
}
|
|
|
|
control_i1 = (hovering != noone && hovering[0] == self && hovering[1] == 1)? 0 : 2;
|
|
|
|
if((edit & 0b010) && point_in_circle(_mx, _my, p1.x, p1.y, ui(16))) //drag tail
|
|
hover = [ self, 1 ];
|
|
}
|
|
}
|
|
|
|
//draw_set_color(c_red);
|
|
//for( var i = 0; i < array_length(FABRIK_result); i++ ) {
|
|
// var pt = FABRIK_result[i];
|
|
// draw_circle(_x + pt.x * _s, _y + pt.y * _s, 16, false);
|
|
//}
|
|
|
|
for( var i = 0; i < array_length(childs); i++ ) {
|
|
var h = childs[i]._drawBone(edit, _x, _y, _s, _mx, _my, hovering, selecting);
|
|
if(hover == noone && h != noone)
|
|
hover = h;
|
|
}
|
|
|
|
return hover;
|
|
}
|
|
|
|
static drawControl = function() {
|
|
if(parent != noone && IKlength == 0) {
|
|
if(!parent_anchor)
|
|
draw_sprite_colored(THEME.anchor_selector, control_i0, control_x0, control_y0);
|
|
draw_sprite_colored(THEME.anchor_selector, control_i1, control_x1, control_y1);
|
|
}
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].drawControl();
|
|
}
|
|
|
|
static resetPose = function() {
|
|
pose_angle = 0;
|
|
pose_scale = 1;
|
|
pose_posit = [ 0, 0 ];
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].resetPose();
|
|
}
|
|
|
|
static setPose = function(_position = [ 0, 0 ], _angle = 0, _scale = 1) {
|
|
setPoseTransform(_position, _angle, _scale);
|
|
setIKconstrain();
|
|
}
|
|
|
|
static setPoseTransform = function(_position = [ 0, 0 ], _angle = 0, _scale = 1) {
|
|
if(is_main) {
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].setPoseTransform(_position, _angle, _scale);
|
|
return;
|
|
}
|
|
|
|
pose_local_angle = pose_angle;
|
|
pose_local_scale = pose_scale;
|
|
pose_local_posit = pose_posit;
|
|
|
|
pose_posit[0] += _position[0];
|
|
pose_posit[1] += _position[1];
|
|
pose_angle += _angle;
|
|
pose_scale *= _scale;
|
|
|
|
var _x = lengthdir_x(distance, direction) + pose_posit[0];
|
|
var _y = lengthdir_y(distance, direction) + pose_posit[1];
|
|
|
|
direction = point_direction(0, 0, _x, _y) + _angle;
|
|
distance = point_distance(0, 0, _x, _y) * _scale;
|
|
|
|
angle += pose_angle;
|
|
length *= pose_scale;
|
|
|
|
for( var i = 0; i < array_length(childs); i++ ) {
|
|
if(childs[i].parent_anchor)
|
|
childs[i].setPoseTransform(_position, pose_angle, pose_scale);
|
|
else
|
|
childs[i].setPoseTransform(_position, pose_angle, pose_scale);
|
|
}
|
|
}
|
|
|
|
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.getPoint(1);
|
|
bones[i - 1] = bn;
|
|
points[i] = {
|
|
x: _p.x,
|
|
y: _p.y
|
|
};
|
|
bn = bn.parent;
|
|
}
|
|
|
|
_p = bn.getPoint(1);
|
|
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.getPoint(0, 0);
|
|
p.x += lengthdir_x(distance, direction);
|
|
p.y += lengthdir_y(distance, direction);
|
|
|
|
FABRIK(bones, points, lengths, p.x, p.y);
|
|
}
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].setIKconstrain();
|
|
}
|
|
|
|
FABRIK_result = [];
|
|
static FABRIK = function(bones, points, lengths, dx, dy) {
|
|
var threshold = 0.1;
|
|
var _bo = array_create(array_length(points));
|
|
for( var i = 0; i < array_length(points); 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(points, lengths, dx, dy);
|
|
FABRIK_forward(points, lengths, sx, sy);
|
|
|
|
var delta = 0;
|
|
var _bn = array_create(array_length(points));
|
|
for( var i = 0; i < array_length(points); 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 >= 32) break;
|
|
} until(delta <= threshold);
|
|
|
|
for( var i = 0; i < array_length(points) - 1; i++ ) {
|
|
var bone = bones[i];
|
|
var p0 = points[i];
|
|
var p1 = points[i + 1];
|
|
|
|
var dir = point_direction(p0.x, p0.y, p1.x, p1.y);
|
|
bone.angle = dir;
|
|
|
|
FABRIK_result[i] = p0;
|
|
}
|
|
|
|
FABRIK_result[i] = p1;
|
|
}
|
|
|
|
static FABRIK_backward = function(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(p0.x, p0.y, tx, ty);
|
|
|
|
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(points, lengths, sx, sy) {
|
|
var tx = sx;
|
|
var ty = sy;
|
|
|
|
for( var i = 0; i < array_length(points) - 1; 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 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.IKTarget = IKTarget == noone? "" : IKTarget.id;
|
|
|
|
bone.childs = [];
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
bone.childs[i] = childs[i].serialize();
|
|
|
|
return bone;
|
|
}
|
|
|
|
static deserialize = function(bone, attributes, 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.attributes = attributes;
|
|
self.node = node;
|
|
|
|
IKlength = bone.IKlength;
|
|
IKTarget = bone.IKTarget;
|
|
|
|
childs = [];
|
|
for( var i = 0; i < array_length(bone.childs); i++ ) {
|
|
var _b = new __Bone().deserialize(bone.childs[i], attributes, node);
|
|
addChild(_b);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static connect = function() {
|
|
if(parent == noone || IKTarget == "")
|
|
IKTarget = noone;
|
|
else if(is_string(IKTarget))
|
|
IKTarget = parent.findBone(IKTarget);
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
childs[i].connect();
|
|
}
|
|
|
|
static clone = function(attributes) {
|
|
var _b = new __Bone(parent, distance, direction, angle, length, attributes);
|
|
_b.id = id;
|
|
_b.name = name;
|
|
_b.is_main = is_main;
|
|
_b.parent_anchor = parent_anchor;
|
|
_b.IKlength = IKlength;
|
|
_b.IKTarget = IKTarget == noone? "" : IKTarget.id;
|
|
|
|
for( var i = 0; i < array_length(childs); i++ )
|
|
_b.addChild(childs[i].clone(attributes));
|
|
|
|
return _b;
|
|
}
|
|
|
|
static toString = function() {
|
|
return $"Bone {name} [{id}]";
|
|
}
|
|
} |