Mannequin.js е несложна библиптека за движение на фигури. Формата и движенията им става през JavaScript. Ето кратък пример, който показва мъжка фигура пример на живо.
<!DOCTYPE html>
<html>
<head>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.167.0/examples/jsm/",
"mannequin": "https://cdn.jsdelivr.net/npm/mannequin-js@latest/src/mannequin.js"
}
}
</script>
</head>
<body>
<script type="module">
import { createStage, Male } from "mannequin";
createStage( );
new Male();
</script>
</body>
</html>
Фигурите в библиотеката се създават като инстанции на класовете Male(height)
,
Female(height)
или Child(height)
, където незадължителният параметър height
е размер на височината е метри. По подразбиране Male
има височина 1.80,
Female
има височина 1.65 и Child
има височина 1.15 (пример на живо):
var man = new Male();
man.position.x = 0.6;
man.turn = -120;
:
var woman = new Female();
woman.position.x = -0.65;
woman.turn = -60;
:
var kid = new Child();
kid.position.z = -0.18;
:
Тези три класа има общ родител – Mannequin(feminine,height)
,
в който булевият параметър feminine определя дали формата е женствена
или мъжествена (пример на живо):
Разликата между използването на различните класове за фигури е в това, че
Mannequin
придава подразбираща се неутрална поза на фигурата, докато
Male
и Female
придават мъжествена и женствена поза.
Всички видове фигури имат една и съща структура. Например, дясната ръка
в кръстена r_arm
. За някои части на тялото mannequin.js използва името
на ставата – напр. лявата предмишница е кръстена на лакъта l_elbow
.
Левите и десните части на тялото са винаги спрямо фигурата, а не спрямо
потребителя (пример на живо):
Всяка част от тялото има ротационни свойства, които определят нейната позиция. Стойностите им са ъгли на завъртане в градуси, така че 180 е завъртане на половин оборот, а 360 е пълен оборот. Отрицателни ъгли са разрешени и представляват завъртане в противоположни посоки.
Mannequin.js има два начина за настройка на въртене – абсолютно и относително. Когато свойството за ротация е зададено с конкретна стойност, това създава абсолютно завъртане. Следващият код ще зададе ъгъла на огъване напред на торса на 45°:
man.torso.bend = 45;
Абсолютните ротации се считат от някои хора за неинтуитивни. Някои стави, като китките, имат ротации по три ъгъла. Поради естеството на ротациите в 3D пространство, трите ротации са взаимосвързани – промяната на една от тях често засяга другите две. Следващият код демонстрира как променянето на свойството turn променя свойството bend.
man.torso.bend = 45; // bend=45
man.torso.turn = 45; // turn=45, но вече bend≈35.3
Относителните ротации се задават по отношение на текущата стойност на ротационно свойството. Модификациите са много по-безопасни, тъй като не разчитат на фиксирани стойности. Следващият код ще наведе торса на 45° от текущата му позиция и след това го завърти на 45°:
man.torso.bend += 45;
man.torso.turn += 45;
Централните части на тялото са тези, които са единични –
глава head, врат neck, торс torso, таз pelvis и
цялото тяло като body. За да се завърти цялото тяло се
използват свойствата bend
, turn
и tilt
на елемента body
на фигурата или самата фигура (пример на живо):
figure.body.bend = angle;
figure.body.turn = angle;
figure.body.tilt = angle;
figure.bend = angle;
figure.turn = angle;
figure.tilt = angle;
Главата head поддържа свойствата nod
, turn
and tilt
(пример на живо):
figure.head.nod = angle;
figure.head.turn = angle;
figure.head.tilt = angle;
Торсът torso има свойства bend
, turn
и tilt
(пример на живо):
figure.torso.bend = angle;
figure.torso.turn = angle;
figure.torso.tilt = angle;
Въпреки че вратът neck е отделна част от тялото, тя не се контролира индивидуално. Вместо това половината от въртенето на главата се разпределя върху врата. По същия начин тазът pelvis не се контролира индивидуално. Вместо това цялото тяло се контролира чрез навеждане, завъртане и накланяне.
Горните крайници са симетрични части на тялото: ръка arm, лакът elbow, китка wrist, пръсти fingers и индивидуални пръсти върхове на пръсти finger_0 до finger_4 с техните средни фаланги (finger_0.mid до finger_4.mid) и върхове (finger_0.tip до finger_4.tip).
И двете ръце arms, l_arm
и r_arm
, поддържат свойства raise
,
straddle
и turn
(пример на живо).
Следващият код показва свойствата на дясната ръка, но същите са
налични и за лявата ръка:
figure.r_arm.raise = angle;
figure.r_arm.straddle = angle;
figure.r_arm.turn = angle;
По принцип ротациите на симетричните части на тялото се стремят да
запазят симетрията. Например, положителни относителни стойности на
straddle
завъртат лявата ръка наляво, а дясната – надясно.
Завъртането на лакътя elbow е само с bend
(пример на живо).
Отрицателни стойности на angle водят до неестествена поза на лакътя.
figure.r_elbow.bend = angle;
Китките wrists имат същите свойства като торса: bend
, turn
и tilt
(пример на живо),
но подобно на ръцете, ротациите са симетрични:
figure.r_wrist.bend = angle;
figure.r_wrist.turn = angle;
figure.r_wrist.tilt = angle;
Последните части на горните крайници са пръстите fingers. Те са дефинирани като множества (l_fingers
и r_fingers
) от отделни пръсти (l_finger_0
до l_finger_4
и r_finger_0
до r_finger_0
).
Множествата могат само да се свиват с bend. Свиването на пръстите автоматично свива и техните средни фаланги и върхове, така че с l_fingers
и r_fingers
могат да се свият целите пръсти (пример на живо):
figure.r_fingers.bend = angle;
Отделните пръсти са номерирани от палец (0) до кутре (4). Пръстите поддържат свойствата bend
, straddle
и turn
. Средната фаланга на пръст е mid
, а крайната фаланга, върхът на пръста, е tip
. Свойствата mid
и tip
на пръст поддтржат само bend
(пример на живо и пример на живо).
figure.r_finger_1.straddle = alpha;
figure.r_finger_1.bend = beta1;
figure.r_finger_1.mid.bend = beta2;
figure.r_finger_1.tip.bend = beta3;
Долните крайници са симетрични части на тялото: крак leg, коляно knee и глезен ankle.
И двата крака legs поддържат свойствата raise
, straddle
и turn
(пример на живо). Разкрачването straddle
и завъртането turn
са симетрични.
figure.r_leg.raise = angle;
figure.r_leg.straddle = angle;
figure.r_leg.turn = angle;
Движението на коляното knee е само bend
(пример на живо).
Отрицателни стойности на angle водят до неестествена поза на коляното.
figure.r_knee.bend = angle;
Глезените ankles имат същите свойства като китките: bend
, turn
и tilt
(пример на живо):
figure.r_ankle.bend = angle;
figure.r_ankle.turn = angle;
figure.r_ankle.tilt = angle;
Позата на фигурата се определя чрез задаване на стойности на ротационните свойства на частите на тялото. Редът на завъртанията е важен, т.е. промяната на реда на завъртане води до различен резултат. Следващият пример показва навеждане на 45°, завъртане на 90° и накланяне встрани на 60° на три фигури. Тъй като редът на завъртане е различен за всяка фигура, крайните им пози също са различни (пример на живо):
man.torso.bend += 45;
man.torso.turn += 90;
man.torso.tilt += 60;
child.torso.tilt += 60;
child.torso.bend += 45;
child.torso.turn += 90;
woman.torso.turn += 90;
woman.torso.bend += 45;
woman.torso.tilt += 60;
Статичната поза определя позицията на част от тялото, която не се променя. Когато се създава фигура, частите на тялото ѝ заемат вградената поза по подразбиране. Ако не се изолзва редактор на поза, всички ротации трябва да бъдат дефинирани програмно (пример на живо):
Понякога е по-добре да се дефинира фигура стъпка по стъпка. Позата “Тай Чи Чуан”, показана по-горе, може да започне със задаване на позицията на цялото тяло:
// обща поза на тялото
man.body.tilt = -5;
man.body.bend = 15.2;
:
// торс и глава
man.torso.turn -= 30;
man.head.turn -= 70;:
След това може да се зададе ориентацията на краката:
// десен крак
man.r_leg.turn = 50;
man.r_knee.bend = 90;
man.r_ankle.bend = 15;
:
// ляв крак
man.l_leg.raise = -20;
man.l_knee.bend = 30;
man.l_ankle.bend = 42;
:
Накрая и ръцете се нагласяват:
// лява ръка
man.l_arm.straddle = 70;
man.l_elbow.bend = 155;
man.l_wrist.bend = -20;
:
// дясна ръка
man.r_arm.straddle += 70;
man.r_elbow.bend += 40;
man.r_wrist.turn -= 60;
:
Динамичната поза – т.е. поза, която се променя с времето – се задава
със същите свойства, които се използват за статична поза. Mannequin.js управлява
динамичните пози чрез потребителска функция за анимация. Тази потребителска функция
се извиква в цикъла на анимацията веднъж за всеки кадър. Всички промени в позата
трябва да бъдат дефинирани във функцията (пример на живо).
Параметърът t е времето, измерено в секунди от стартирането на библиотеката.
Името на потребителската функция се подава като параметър на createStage()
.
createStage( animate );
function animate(t)
{
var time1 = Math.sin( 2*t ),
time2 = Math.sin( 2*t-60 );
ball.position.x = 0.06*time1;
child.position.y = 0.31 + 0.05*Math.cos(time1 * Math.PI/2);
child.turn = -90-20*time1+20*time2;
child.tilt = 10*time1;
:
}
За да се направи цикълът на анимацията по-бърз, всички
фиксирани ротации трябва да бъдат дефинирани извън
animate
.
Позата може да бъде извлечена от фигура чрез свойството posture
. То съдържа
обект с елементи version
за версията на формата на данните за позата и data
– вложен масив с ъглите на завъртане на ставите. Свойството posture
може
да се използва и за задаване на поза на фигура.
{ "version": 7,
"data": [ [0,0,0], [90,-85,74.8], [16.1,-29.5,26.3], [3.5,-34.8,6.1], ... ]
}
Има алтернативно свойство postureString
, с което се извлича или задава поза
като текстов низ. Преобразуването на позата към и от текстов низ се прави с
JSON.stringify
и JSON.parse
.
Позите могат да бъдат сливани чрез ойлерова интерполация (т.е. линейна интерполация
на ойлерови ъгли). Функцията blend(posture0,posture1,k)
слива
първоначалната поза posture0 и крайната поза posture1 с коефициент
k∈[0,1]. Когато k=0 резултатът е поза posture0, когато k=1
резултатът е поза posture1, когато k е между 0 и 1 резултатът е междинна
поза между posture0 и posture1.
Следващият пример слива позата на една фигура и я копира в друга фигура (пример на живо 1 и пример на живо 2):
// две фигури
var man = new Male();
var woman = new Female();
// две пози
var A = {"version": 7, "data": [[ 0, -7.2, 0 ],...]};
var B = {"version": 7, "data": [[ 0, 2.8, 0 ],...]};
// задаване на междинна поза
man.posture = blend(A,B,0.5);
// копиране на позата в друга фигура
woman.posture = man.posture;
Освен за движение на части на тялото, текущата версия на mannequin.js предоставя основна функционалност за допълнителни промени по фигурата.
По подразбиране всички фигури използват предварително дефиниран набор от
цветове за частите на тялото. Цветовете за конкретна фигура се задават с метода
recolor
със седем параметъра Three.js цвята
или HTML/CSS имена на цветове.
Тези цветове са за глава, обувки, таз, стави, крайници, торс и нокти:
man.recolor(
'antiquewhite', // глава
'gray', // обувки
'antiquewhite', // таз
'burlywood', // стави
'antiquewhite', // крайници
'bisque', // тяло
'burlywood' // нокти
);
Цветът на ставите и крайниците се отнася до всички стави и всички крайници.
Цветовете на индивидуалните части от тялото могат да се променят чрез метода
recolor
на частите (пример на живо):
var man = new Male();
// общи цветове
recolor( 'lightgreen', 'black', 'black', 'white',
'darkolivegreen', 'darkslategray', 'yellow' );
:
// индивидуални цветове
man.l_elbow.recolor( 'yellow', 'black' );
man.l_wrist.recolor( 'orange' );
man.l_fingers.recolor( 'coral' );
man.r_knee.recolor( 'antiquewhite', 'black' );
man.l_nails.recolor( 'black' );
Първият параметър на recolor
е цветът на основния елемент на частта от тялото.
Вторият параметър е цветът на сферичния елемент (ако има такъв).
Достъпът до върховете на пръстите се осъществява чрез l_fingers.tips
и r_fingers.tips
.
Всяка част от тялото може да бъде скрита. Това не премахва нея и нейния графичен образ от фигурата, а просто не я рисува. Методът за скриване е:
figure.joint.hide();
figure.joint.hide( true );
където joint е името на частта от тялото, която да се скрие. Скритите части на тялото могат да се въртят и това се отразява на частите на тялото, прикрепени към тях. Следващият пример скрива двете ръце и двата крака, но те все още същестуват и се използват от лактите и коленете (пример на живо):
man.l_leg.hide();
man.r_leg.hide();
man.l_arm.hide();
man.r_arm.hide();
Ако hide
се използва с параметър true
, тогава скриването
се прилага както а съответната част на тялото, така и на
всички подчинени части.
Показването на скрита част на тяло става с:
figure.joint.show();
figure.joint.show( true );
Частите на тялото са наследници на класа THREE.Object3D
и поддържат
неговите свойства и методи. Въпреки това, поради конструкцията
на скелета и свързването на ставите, мащабирането на част от
тялото трябва да е еднакво по всички оси, в противен случай
позате трябва да бъде ръчно коригирана (пример на живо):
var man = new Male();
man.head.scale.set(3,3,3);
man.l_arm.scale.set(1/2,1/2,1/2);
man.r_arm.scale.set(1/2,1/2,1/2);
man.l_wrist.scale.set(3,5,3);
man.r_wrist.scale.set(3,5,3);
Всеки THREE.Object3D
или негов наследник може да
бъде прикрепен към част от тялото. Прикрепеният
обект е включен в тялото и прави всяко движение,
което тялото извършва:
figure.joint.attach(object);
Обектите могат да бъдат прикрепени към скрити части на тялото, но те не се скриват автоматично. Този подход се използва за замяна на част от тялото с изцяло собствен потребителски обект (пример на живо):
var man = new Male();
// добавяне на гривни
var bracelet = new THREE.Mesh(
new THREE.CylinderGeometry(3,3,1,16),
new THREE.MeshPhongMaterial({color:'crimson',shininess:200})
);
bracelet.castShadow = true;
bracelet.position.y = 6;
man.l_elbow.attach(bracelet);
bracelet = bracelet.clone();
man.r_elbow.attach(bracelet);
// замяна на крака с други обекти
man.r_leg.hide();
var material = new THREE.MeshPhongMaterial({color:'crimson',shininess:200});
var obj = new THREE.Mesh(new THREE.CylinderGeometry(3,2,3,32), material);
obj.castShadow = true;
obj.position.y = 2;
man.r_leg.attach(obj);
Не всяко взаимодействие на фигури с други обекти може
да се осъществи чрез прикачване. Mannequin.js предоставя
метод point(x,y,z)
за всяка част от тялото. Този метод
прилага права кинематика и изчислява глобалните координати на точката (x,y,z),
дефинирана в локалната координатна система на частта
от тялото.
Следващият пример създава въже, преминаващо през 5 точки от частите на тялото на фигура (пример на живо):
setLoopVertex( 0, man.r_fingers.tips.point(0,1,0) );
setLoopVertex( 1, man.head.point(3,1.2,0) );
setLoopVertex( 2, man.l_fingers.tips.point(0,1,0) );
setLoopVertex( 3, man.l_ankle.point(6,2,0) );
setLoopVertex( 4, man.r_ankle.point(6,2,0) );
Глобалните позиции могат да се използват за поставяне на фигури към земята. Въпреки това, mannequin.js не съдържа никаква функционалност за докосване, така че потребителят трябва да избере точки на контакт и да използва техните глобални позиции.
Следващият пример използва четири контактни точки на лявата
обувка (т.е. man.l_ankle
). Контактните точки са показани
като червени точки. Минималното вертикално положение на
контактни точки се използва за регулиране на вертикалното
положение на фигурата (пример на живо):
// изчисляване на минималното вертикално отклонение на контактните точки
var bottom = Math.min(
man.l_ankle.point(6,2,0).y,
man.l_ankle.point(-2,2.5,0).y,
man.l_ankle.point(2,2.5,2).y,
man.l_ankle.point(2,2.5,-2).y,
man.r_ankle.point(6,2,0).y,
man.r_ankle.point(-2,2.5,0).y,
man.r_ankle.point(2,2.5,2).y,
man.r_ankle.point(2,2.5,-2).y
);
man.position.y += (GROUND_LEVEL-bottom);
Стойността на GROUND_LEVEL
е дефинирана от mannequin.js при използването на
createScene()
. Съдържа вертикалното отместване на нивото на земята.
Фигура може да използва функцията stepOnGround()
, за да се придвижи вертикалнно,
така че най-ниската ѝ точка да докосва земята.
man.stepOnTheGround();
Библиотеката mannequin.js се предоставя като комплект от JavaScript модули.
Предназначена е да се използва от CDN. Най-вероятно библиотеката може да бъде
инсталирана с npm
, но това не е пробвано все още.
Библиотеката използва Three.js и очаква следните importmaps да бъдат дефинирани:
three
: указател към Three.js файла three.module.js
three/addons/
: указател към пътя на добавките на Three.jsmannequin
: указател към главния файл на библиотеката mannequin.js
Следващите подсекции демонстраират някои от конфигурационните сценарии на използване на mannequin.js.
Съкращението CDN означава Content Delivery Network (Мрежа за доставка на съдържание).
Относно mannequin.js CDN служи като хостинг на библиотечните файлове. Към момента на изготвяне на този докумен се препоръчва ползването на jsDelivr. Други CDN също са възможни.
Основните предимства на използването на CDN са:
Основните недостатъци на използването на CDN са:
Донякъде минимална програма, която използва mannequin.js от този CDN, е показана в този пример на живо. Ако файлът е свален локално, той може да се стартира директно без допълнителна инсталация. Указателите в importmaps в примера сочат към конкретна версия на Three.js и към най-новата версия на mannequin.js.
<!DOCTYPE html>
<html>
<head>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.167.0/examples/jsm/",
"mannequin": "https://cdn.jsdelivr.net/npm/mannequin-js@latest/src/mannequin.js"
}
}
</script>
</head>
<body>
<script type="module">
import { createStage, Male } from "mannequin";
createStage( );
new Male();
</script>
</body>
</html>
Много от примерите в този документ използват скрипта importmap.js
за генериране на importmaps и инжектирането им в страницата. Това се прави единствено
за поддържане на по-кратък код и за лесно превключване към други версии както на
Three.js, така и на mannequin.js.
По същество пускането е същото като от CDN, като вместо CDN се ползва локална папка. Единствената промяната са пътищата в importmaps, които сочат към локалните файлове и папки.
Основните предимства на използването на локални файлове са:
Основните недостатъци на използването на локални файлове са:
Възможно е едновременното използване на CDN и на локални файлове. Например, използва се онлайн Three.js от CDN и локалнен mannequin.js. Това се определя от пътищата в importmaps.
Библиотеката mannequin.js се предоставя като NPM пакет. Ако nodes.js е инсталиран на потребителското устройство, трябва да е възможно да се инсталира mannequin.js и да се използва директно.
Основните предимства на използването на nodes.js:
Основните недостатъци на използването на nodes.js:
Забележка: Този подход не е тестван. Ако установите, че не работи, но знаете какво трябва да се коригира, за да проработи, моля, свържете се с нас.
Библиотеката mannequin.js дефинира следните фукции и класове:
createStage(animationLoop)
– функция, създава сцена със земя и светлиниgetStage()
– функция, текущата сцена*getVersion()
– функция, текущата версия на mannequin.js като число; напр. 5.2getPostureVersion()
– функция, текущата версия на формата на данните, описващи позаgetGroundLevel()
– функция, вертикалното положение на земята в метриblend(p,q,k)
– функция, смесена поза на пози p и q с коефициент kMannequin(feminine,height)
– клас, най-обща фигураMale(height)
– клас, мъжка фигураFemale(height)
– клас, женска фигураChild(height)
– клас, детска фигураВсички фигури (инстанции на Mannequin
, Male
, Female
и Child
) имат еднакъв
набор от свойства и методи.
body
, pelvis
, torso
, neck
, head
– свойства, основни части на тялоl_leg
, l_knee
, l_ankle
– свойства, части на ляв кракr_leg
, r_knee
, r_ankle
– свойства, части на десен кракl_arm
, l_elbow
, l_wrist
, l_finger_0
…l_finger_4
, l_fingers
, l_nails
– свойства, части на лява ръкаr_arm
, r_elbow
, r_wrist
, r_finger_0
…r_finger_4
, r_fingers
, r_nails
– свойства, части на дясна ръкаfeminine
– свойство, булев флаг дали фигура е женскаbend
, tilt
, turn
– свойства, завъртяност на тялоposture
, postureString
– свойства, поза на фигураstepOnGround()
– метод, премества вертикално фигура, за да докосне земятаrecolor(...)
– метод, променя цветовете на частите на тялоВсички части на тяло имат подобни свойства и методи. Някои от свойствата за завъртане не са налични за всички части поради биологически причини.
posture
– свойство, поза на част на тяло (масив от ъглите на завъртане)hide()
, show()
– методи, скриване и показване на част от тялоattach(image)
, detach(image)
– методи, добавяне и премахване на потребителски 3D обект ктм част на тялоpoint(x,y,z)
– метод, изчисляване на глобални координати на локална позиция (x,y,z) спрямо част на тялоrecolor(...)
– метод, смяна на цветовете на част на тялоlabel(...)
– метод, добавяне на 3D текст към част на тялоbend
, tilt
, turn
– свойства, завъртяност на глезени, тяло, торс и киткиbend
– свойство, завъртяност на лакти и коленаbend
, straddle
, turn
– свойства, завъртяност на пръстиraise
, straddle
, turn
– свойства, завъртяност на ръце и кракаnod
, tilt
, turn
– свойства, завъртяност на главаПръстите имат допълителни свойства и методи за достъп до тяхната структура:
mid
– свойство, средна фаланга на пръстmid.bend
– метод, завъртяност на средната фалангаtip
– свойство, крайна фаланга на пръстtip.nail
– свойство, нокът на пръстtip.bend
– метод, завъртяност на крайната фалангаtip.nail.bend
– метод, завъртяност на нокът … шегувам се, няма такъв методГруповите свойства l_fingers
и r_fingers
съдържат:
finger_0
…finger_4
– свойства, отделните пръсти, съответстващи на l_finger_0
…l_finger_4
и r_finger_0
…r_finger_4
bend
– метод, свиване на всички пръсти, включително средните и крайните фалангиhide()
, show()
– методи, скриване и показване на всички пръсти