mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d227b97843 | ||
|
|
7f11534fc1 | ||
|
|
af2cf10c16 | ||
|
|
055fd78d4d | ||
|
|
26b472893d | ||
|
|
26fbbebb89 | ||
|
|
2eba83411c | ||
|
|
80ffa1a9b4 | ||
|
|
1b06799315 | ||
|
|
046b0b8be3 | ||
|
|
f4b16f2cdb | ||
|
|
b266afedea | ||
|
|
646eb94f72 | ||
|
|
1bf79fc72d | ||
|
|
cddbb93c99 | ||
|
|
5463eee217 | ||
|
|
c93709f80f | ||
|
|
f6b915a88e | ||
|
|
9231356638 | ||
|
|
7bb32a382a | ||
|
|
a17f651f64 | ||
|
|
c9f0f6e27c | ||
|
|
3defd47c29 | ||
|
|
3068fada17 | ||
|
|
e71ed5cf6c | ||
|
|
0ccd7e260c | ||
|
|
36c4889d75 | ||
|
|
8da9e63c45 | ||
|
|
5e81e3ac4c | ||
|
|
dabb78e545 | ||
|
|
582618ee72 | ||
|
|
047667432c | ||
|
|
f4261d5bc2 |
@@ -12,12 +12,17 @@ websocket-connection_lost = انقطع الاتصال بالسيرفر. يتم
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = نسخة جديدة متوفرة: { $version }
|
||||
version_update-description = سيؤدي النقر على "{ version_update-update }" إلى تنزيل مثبت SlimeVR نيابة عنك.
|
||||
version_update-update = تحديث
|
||||
version_update-close = أغلق
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = لست متأكد من أجهزة التعقب؟ قم بتحريك الجهاز لتحديد العنصر المناسب.
|
||||
tips-do_not_move_heels = يرجى عدم تحريك كاحليك أثناء التسجيل!
|
||||
tips-file_select = اسحب الملفات وأفلتها لاستخدامها أو <u> تصفح </ u>
|
||||
tips-tap_setup = يمكنك النقر ببطء مرتين على جهاز التعقب لاختياره بدلاً من تحديده من القائمة.
|
||||
|
||||
## Body parts
|
||||
|
||||
@@ -31,6 +36,7 @@ body_part-RIGHT_HAND = اليد اليمنى
|
||||
body_part-RIGHT_UPPER_LEG = الفخذ الأيمن
|
||||
body_part-RIGHT_LOWER_LEG = الكاحل الأيمن
|
||||
body_part-RIGHT_FOOT = القدم اليمنى
|
||||
body_part-UPPER_CHEST = أعلى الصدر
|
||||
body_part-CHEST = الصدر
|
||||
body_part-WAIST = الخصر
|
||||
body_part-HIP = الورك
|
||||
@@ -48,8 +54,9 @@ skeleton_bone-NONE = غير محدد
|
||||
skeleton_bone-HEAD = إمالة الرأس
|
||||
skeleton_bone-NECK = طول العنق
|
||||
skeleton_bone-torso_group = طول الجذع
|
||||
skeleton_bone-CHEST = طول الصدر
|
||||
skeleton_bone-UPPER_CHEST = طول أعلى الصدر
|
||||
skeleton_bone-CHEST_OFFSET = درجة تشريد الصدر
|
||||
skeleton_bone-CHEST = طول الصدر
|
||||
skeleton_bone-WAIST = طول الخصر
|
||||
skeleton_bone-HIP = طول الورك
|
||||
skeleton_bone-HIP_OFFSET = درجة تشريد الورك
|
||||
@@ -102,6 +109,8 @@ bvh-recording = تسجيل...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = إيقاف التعقب مؤقتا
|
||||
tracking-paused = إلغاء الإيقاف التعقب
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
@@ -168,6 +177,9 @@ tracker-infos-custom_name = اسم مخصص
|
||||
tracker-infos-url = عنوان URL لجهاز التعقب
|
||||
tracker-infos-version = إصدار البرنامج الثابت
|
||||
tracker-infos-hardware_rev = مراجعة الأجهزة
|
||||
tracker-infos-hardware_identifier = معرف الجهاز
|
||||
tracker-infos-imu = مستشعر IMU
|
||||
tracker-infos-board_type = اللوحة الرئيسية
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -220,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } الفخذ
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } الكاحل الأيمن؟
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } القدم اليمنى؟
|
||||
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } وحدة التحكم اليمنى؟
|
||||
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } أعلى الصدر؟
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part } الصدر؟
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part } الخصر؟
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part } الورك؟
|
||||
@@ -521,7 +534,26 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = العودة إلى معايرة التركيب
|
||||
onboarding-reset_tutorial = إعادة البرنامج التعليمي
|
||||
onboarding-reset_tutorial-description = هذه الميزة لم تنته بعد، فقط اضغط على متابعة
|
||||
onboarding-reset_tutorial-explanation = أثناء استخدام أجهزة التعقب، قد تخرج عن المحاذاة بسبب انحراف IMU ، أو لأنك ربما تكون قد نقلتها جسديا. لديك عدة طرق لإصلاح هذا.
|
||||
onboarding-reset_tutorial-skip = تخطى الخطوة
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
اضغط على جهاز التعقب المحدد { $taps } مرات لتشغيل إعادة ضبط الانعراج.
|
||||
|
||||
سيؤدي ذلك إلى جعل أجهزة التعقب تواجه نفس اتجاه HMD الخاص بك.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-1 =
|
||||
اضغط على جهاز التعقب المحدد { $taps } مرات لتشغيل إعادة تعيين كاملة.
|
||||
|
||||
يجب أن تكون واقفًا (i-pose). هناك تأخير لمدة 3 ثوان (قابل للتكوين) قبل إعادة التعيين بالكامل.
|
||||
هذا يعيد تعيين موضع ودوران جميع جهاز التعقب. يجب أن يحل معظم المشاكل.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-2 =
|
||||
اضغط على المتتبع المحدد { $taps } مرات لتشغيل إعادة تعيين متصاعد.
|
||||
|
||||
يساعد إعادة التعيين المتصاعد في تحديد كيفية وضع أجهزة التعقب عليك بالفعل. لذلك إذا قمت بنقلهم عن طريق الخطأ وغيرت كيفية توجيههم بمقدار كبير ، فسيساعد ذلك.
|
||||
|
||||
يجب أن تكون في وضع تزلج كما هو موضح في معالج "التثبيت التلقائي" ولديك تأخير لمدة 3 ثوانٍ (قابل للتكوين) قبل أن يتم تشغيله.
|
||||
|
||||
## Setup start
|
||||
|
||||
@@ -576,9 +608,24 @@ onboarding-connect_tracker-next = لقد قمت بتوصيل جميع أجهزة
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial = برنامج تعليم معايرة IMU
|
||||
onboarding-calibration_tutorial-subtitle = سوف يساعد هذا في تقليل الانجراف التعقب!
|
||||
onboarding-calibration_tutorial-description = كل مرة تقوم بتشغيل أجهزة التعقب، يجب أن تستريح للحظة على سطح مستوٍ للمعايرة. لنفعل الشيء نفسه بالنقر فوق الزر "{ onboarding-calibration_tutorial-calibrate }" ، <b>لا تحركها!</b>
|
||||
onboarding-calibration_tutorial-calibrate = وضعت أجهزة التعقب على الطاولة
|
||||
onboarding-calibration_tutorial-status-waiting = بانتظارك
|
||||
onboarding-calibration_tutorial-status-calibrating = جاري المعايرة
|
||||
onboarding-calibration_tutorial-status-success = رائع!
|
||||
onboarding-calibration_tutorial-status-error = تم نقل جهاز التعقب
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
onboarding-assignment_tutorial = كيفية تحضير جهاز تعقب Slime قبل وضعه
|
||||
onboarding-assignment_tutorial-first_step = 1. ضع ملصق جزء الجسم (إذا كان لديك واحد) على جهاز التعقب وفقا لاختيارك
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = ملصق
|
||||
onboarding-assignment_tutorial-second_step = 2. قم بتوصيل الشريط بجهاز التعقب الخاص بك ، مع الحفاظ على جانب الخطاف والحلقة من وجه الشريط في الاتجاه التالي:
|
||||
onboarding-assignment_tutorial-second_step-continuation = يجب أن يكون جانب الخطاف والحلقة للامتداد في هذا الاتجاه:
|
||||
onboarding-assignment_tutorial-done = وضعت الملصقات والأشرطة!
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
@@ -589,10 +636,12 @@ onboarding-assign_trackers-description = دعنا نختار موقع أجهزة
|
||||
# $assigned (Number) - Trackers that have been assigned a body part
|
||||
# $trackers (Number) - Trackers connected to the server
|
||||
onboarding-assign_trackers-assigned =
|
||||
{ $assigned ->
|
||||
{ $trackers ->
|
||||
[zero] { $assigned } من { $trackers } أجهزة تعقب عينت
|
||||
[one] جهاز واحد من { $trackers } أجهزة تعقب عينت
|
||||
[two] جهازان من { $trackers } أجهزة تعقب عينت
|
||||
[few] { $assigned } من { $trackers } أجهزة تعقب عينت
|
||||
[many] { $assigned } من { $trackers } أجهزة تعقب عينت
|
||||
*[other] { $assigned } من { $trackers } أجهزة تعقب عينت
|
||||
}
|
||||
onboarding-assign_trackers-advanced = إظهار مواقع التعيين المتقدمة
|
||||
@@ -669,13 +718,15 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
## Tracker mounting method choose
|
||||
|
||||
onboarding-choose_mounting = ما طريقة معايرة التركيب المستخدمة؟
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = اتجاه التركيب يصحح وضع أجهزة التعقب على جسمك.
|
||||
onboarding-choose_mounting-auto_mounting = التركيب التلقائي
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = الموصى به
|
||||
onboarding-choose_mounting-auto_mounting-label = تجريبي
|
||||
onboarding-choose_mounting-auto_mounting-description = سيكتشف هذا تلقائيًا اتجاهات التركيب لجميع أجهزة التعقب من وضعين
|
||||
onboarding-choose_mounting-manual_mounting = التركيب اليدوي
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = إذا كنت تعلم ماذا تفعل
|
||||
onboarding-choose_mounting-manual_mounting-label = المستحسن
|
||||
onboarding-choose_mounting-manual_mounting-description = سيسمح لك باختيار اتجاه التثبيت يدويًا لكل جهاز تعقب
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -710,6 +761,10 @@ onboarding-automatic_mounting-put_trackers_on-next = ارتديت جميع أج
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = ما هي طريقة معايرة النسب التي يجب استخدامها؟
|
||||
# Multiline string
|
||||
onboarding-choose_proportions-description =
|
||||
تستخدم نسب الجسم لمعرفة قياسات جسمك. هم مطلوبون لحساب مواقع أجهزة التعقب.
|
||||
عندما لا تتطابق نسب جسمك مع تلك المحفوظة ، ستكون دقة التتبع لديك أسوأ وستلاحظ أشياء مثل التزلج أو الانزلاق ، أو أن جسمك لا يتطابق مع الصورة الرمزية بشكل جيد.
|
||||
onboarding-choose_proportions-auto_proportions = النسب التلقائية
|
||||
# Italized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = الموصى به
|
||||
@@ -718,6 +773,8 @@ onboarding-choose_proportions-manual_proportions = النسب اليدوية
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = للمسات الصغيرة
|
||||
onboarding-choose_proportions-manual_proportions-description = سيسمح لك بتعديل النسب يدويًا عن طريق تعديلها مباشرة
|
||||
onboarding-choose_proportions-export = تصدير النسب
|
||||
onboarding-choose_proportions-file_type = ملف نسب الجسم
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -787,3 +844,10 @@ home-no_trackers = لم يتم الكشف أو تعيين عن أي جهاز ت
|
||||
|
||||
## Status system
|
||||
|
||||
status_system-StatusTrackerReset = يوصى بإجراء إعادة تعيين كاملة نظرًا لعدم تعديل واحد أو أكثر من أجهزة التعقب.
|
||||
status_system-StatusSteamVRDisconnected =
|
||||
{ $type ->
|
||||
[steamvr_feeder] حاليًا غير متصل بتطبيق SlimeVR Feeder.
|
||||
*[other] حاليًا غير متصل بـ SteamVR عبر برنامج تشغيل SlimeVR.
|
||||
}
|
||||
status_system-StatusTrackerError = يحتوي جهاز التعقب { $trackerName } على خطأ.
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,6 +10,9 @@
|
||||
websocket-connecting = Připojování k serveru
|
||||
websocket-connection_lost = Ztráta spojení se serverem. Pokus o obnovení připojení...
|
||||
|
||||
## Update notification
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Nejste si jisti, který tracker je který? Zatřeste tracker a zvýrazní se odpovídající položka.
|
||||
@@ -30,7 +30,6 @@ body_part-RIGHT_HAND = Pravá ruka
|
||||
body_part-RIGHT_UPPER_LEG = Pravé stehno
|
||||
body_part-RIGHT_LOWER_LEG = Pravý kotník
|
||||
body_part-RIGHT_FOOT = Pravá noha
|
||||
body_part-RIGHT_CONTROLLER = Pravý ovladač
|
||||
body_part-CHEST = Hrudník
|
||||
body_part-WAIST = Pás
|
||||
body_part-HIP = Kyčel
|
||||
@@ -41,7 +40,6 @@ body_part-LEFT_HAND = Levá ruka
|
||||
body_part-LEFT_UPPER_LEG = Levé stehno
|
||||
body_part-LEFT_LOWER_LEG = Levý kotník
|
||||
body_part-LEFT_FOOT = Levá noha
|
||||
body_part-LEFT_CONTROLLER = Levý ovladač
|
||||
|
||||
## Proportions
|
||||
|
||||
@@ -63,8 +61,6 @@ skeleton_bone-SHOULDERS_DISTANCE = Vzdálenost ramen
|
||||
skeleton_bone-SHOULDERS_WIDTH = Šířka ramen
|
||||
skeleton_bone-UPPER_ARM = Délka nadloktí
|
||||
skeleton_bone-LOWER_ARM = Délka podloktí
|
||||
skeleton_bone-CONTROLLER_Y = Vzdálenost ovladače Y
|
||||
skeleton_bone-CONTROLLER_Z = Vzdálenost ovladače Z
|
||||
skeleton_bone-ELBOW_OFFSET = Odsazení loktů
|
||||
|
||||
## Tracker reset buttons
|
||||
@@ -72,7 +68,6 @@ skeleton_bone-ELBOW_OFFSET = Odsazení loktů
|
||||
reset-reset_all = Obnovení všech proporcí
|
||||
reset-full = Resetovat
|
||||
reset-mounting = Obnovit montáž
|
||||
reset-quick = Rychlý reset
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
@@ -93,11 +88,16 @@ navbar-mounting = Montážní kalibrace
|
||||
navbar-onboarding = Průvodce nastavením
|
||||
navbar-settings = Nastavení
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = Nahrávat BVH
|
||||
bvh-recording = Nahrávání...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = Pozastavit sledování
|
||||
tracking-paused = Pokračovat se sledováním
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Překrytí
|
||||
@@ -124,6 +124,7 @@ widget-developer_mode-more_info = Více informací
|
||||
widget-imu_visualizer = Rotace
|
||||
widget-imu_visualizer-rotation_raw = Nezpracované
|
||||
widget-imu_visualizer-rotation_preview = Náhled
|
||||
widget-imu_visualizer-rotation_hide = Skrýt
|
||||
|
||||
## Tracker status
|
||||
|
||||
@@ -160,6 +161,9 @@ tracker-infos-manufacturer = Výrobce
|
||||
tracker-infos-display_name = Zobrazovaný název
|
||||
tracker-infos-custom_name = Vlastní název
|
||||
tracker-infos-url = URL Trackeru
|
||||
tracker-infos-version = Verze firmwaru
|
||||
tracker-infos-hardware_rev = Revize hardwaru
|
||||
tracker-infos-board_type = Základní deska
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -297,8 +301,6 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = Použí
|
||||
## FK/Tracking settings
|
||||
|
||||
settings-general-fk_settings = Nastavení trackování
|
||||
settings-general-fk_settings-leg_tweak = Vyladění nohou
|
||||
settings-general-fk_settings-leg_tweak-description = Podlahovej-clip může snížit nebo dokonce eliminovat klipování s podlahou, ale může způsobit problémy, když klečíte na kolenou. Korekce-bruslení opravuje bruslení na ledě, avšak může snížit přesnost některých pohybových vzorců.
|
||||
# Floor clip:
|
||||
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
|
||||
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
|
||||
@@ -334,12 +336,6 @@ settings-general-gesture_control-taps =
|
||||
[few] { $amount } klepnutí
|
||||
*[other] { $amount } klepnutí
|
||||
}
|
||||
settings-general-gesture_control-quickResetEnabled = Povolit klepnutí pro rychlý resetování
|
||||
settings-general-gesture_control-quickResetDelay = Zpoždění rychlého resetu
|
||||
settings-general-gesture_control-quickResetTaps = Klepnutí pro rychlý reset
|
||||
settings-general-gesture_control-resetEnabled = Povolit klepnutí pro resetování
|
||||
settings-general-gesture_control-resetDelay = Zpoždění resetování
|
||||
settings-general-gesture_control-resetTaps = Klepnutí pro resetování
|
||||
settings-general-gesture_control-mountingResetEnabled = Povolit klepnutí pro resetování montáže
|
||||
settings-general-gesture_control-mountingResetDelay = Zpoždění resetování montáže
|
||||
settings-general-gesture_control-mountingResetTaps = Klepnutí pro resetování montáže
|
||||
@@ -427,11 +423,14 @@ settings-osc-vrchat-network-address-placeholder = VRChat ip adresa
|
||||
settings-osc-vrchat-network-trackers = Trackery
|
||||
settings-osc-vrchat-network-trackers-description = Vypnuti a zapnutí odesílání konkrétních trackerů přes OSC.
|
||||
settings-osc-vrchat-network-trackers-chest = Hrudník
|
||||
settings-osc-vrchat-network-trackers-waist = Pás
|
||||
settings-osc-vrchat-network-trackers-hip = Kyčel
|
||||
settings-osc-vrchat-network-trackers-knees = Kolena
|
||||
settings-osc-vrchat-network-trackers-feet = Chodidla
|
||||
settings-osc-vrchat-network-trackers-elbows = Lokty
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
onboarding-skip = Přeskočit nastavení
|
||||
@@ -459,15 +458,10 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Zpět na kalibraci montáže
|
||||
onboarding-reset_tutorial = Obnovit tutoriál
|
||||
onboarding-reset_tutorial-description = Tato funkce není dokončena, stačí stisknout tlačítko pokračovat
|
||||
|
||||
## Setup start
|
||||
|
||||
onboarding-home = Vítejte k SlimeVR
|
||||
# This cares about multilines and it's centered!!
|
||||
onboarding-home-description =
|
||||
Přinášíme full-body tracking
|
||||
pro každého
|
||||
onboarding-home-start = Pusťme se do toho!
|
||||
|
||||
## Enter VR part of setup
|
||||
@@ -492,10 +486,7 @@ onboarding-connect_tracker-description-p1 = Všechny zatím nepřipojené jednod
|
||||
onboarding-connect_tracker-issue-serial = Mám potíže s připojením!
|
||||
onboarding-connect_tracker-usb = USB Tracker
|
||||
onboarding-connect_tracker-connection_status-connecting = Odesílání přihlašovacích údajů Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-connected = Připojeno k Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-error = Nelze se připojit k Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-start_connecting = Hledání trackerů
|
||||
onboarding-connect_tracker-connection_status-handshake = Připojeno k Serveru
|
||||
onboarding-connect_tracker-connection_status-connection_error = Nelze se připojit k síti Wi-Fi
|
||||
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
|
||||
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
|
||||
# English in this case only has 2 plural rules, which are "one" and "other",
|
||||
@@ -510,6 +501,13 @@ onboarding-connect_tracker-connected_trackers =
|
||||
}
|
||||
onboarding-connect_tracker-next = Připojil jsem všechny své trackery
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial-status-success = Super!
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Zpět na přihlašovací údaje Wi-Fi
|
||||
@@ -527,6 +525,12 @@ onboarding-assign_trackers-assigned =
|
||||
onboarding-assign_trackers-advanced = Zobrazit pokročilá místa přiřazení
|
||||
onboarding-assign_trackers-next = Přiřadil jsem všechny trackery
|
||||
|
||||
## Tracker assignment warnings
|
||||
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
onboarding-manual_mounting-back = Zpět na Enter VR
|
||||
@@ -556,6 +560,9 @@ onboarding-automatic_mounting-put_trackers_on-title = Nasaďte si trackery
|
||||
onboarding-automatic_mounting-put_trackers_on-description = Pro kalibraci rotace montáže použijeme právě přiřazené trackery. Nasaďte všechny trackery, na obrázku vpravo vidíte, které jsou které.
|
||||
onboarding-automatic_mounting-put_trackers_on-next = Mám nasazené všechny trackery
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
onboarding-manual_proportions-back = Zpět na Reset tutoriál
|
||||
@@ -573,22 +580,14 @@ onboarding-automatic_proportions-prev_step = Předchozí krok
|
||||
onboarding-automatic_proportions-put_trackers_on-title = Nasaďte si trackery
|
||||
onboarding-automatic_proportions-put_trackers_on-description = Pro kalibraci proporcí použijeme trackery, které jste právě přiřadili. Nasaďte si trackery, na obrázku vpravo vidíte, která jsou která.
|
||||
onboarding-automatic_proportions-put_trackers_on-next = Mám nasazené všechny trackery
|
||||
onboarding-automatic_proportions-preparation-title = Příprava
|
||||
onboarding-automatic_proportions-preparation-description = Umístěte židli přímo za sebe do herního prostoru. Buďte připraveni se během nastavení autobonu posadit.
|
||||
onboarding-automatic_proportions-preparation-next = Jsem před židlí
|
||||
onboarding-automatic_proportions-start_recording-title = Připravte se hýbat
|
||||
onboarding-automatic_proportions-start_recording-description = Nyní budeme nahrávat některé konkrétní pózy a pohyby. Tyto se zobrazí na další obrazovce. Po stisknutí tlačítka buďte připraveni začít!
|
||||
onboarding-automatic_proportions-start_recording-next = Spustit nahrávání
|
||||
onboarding-automatic_proportions-recording-title = ZÁZN
|
||||
onboarding-automatic_proportions-recording-description-p0 = Probíhá nahrávání...
|
||||
onboarding-automatic_proportions-recording-description-p1 = Proveďte níže uvedené pohyby:
|
||||
onboarding-automatic_proportions-recording-steps-0 = Párkrát ohněte kolena.
|
||||
onboarding-automatic_proportions-recording-steps-1 = Posaďte se na židli a pak se postavte.
|
||||
onboarding-automatic_proportions-recording-steps-2 = Natočte horní část těla doleva a pak se ohněte doprava.
|
||||
onboarding-automatic_proportions-recording-steps-3 = Natočte horní část těla doprava a pak se ohněte doleva.
|
||||
onboarding-automatic_proportions-recording-steps-4 = Hýbejte se, dokud časovač neskončí.
|
||||
onboarding-automatic_proportions-recording-processing = Zpracování výsledku
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 15)
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
|
||||
onboarding-automatic_proportions-recording-timer =
|
||||
{ $time ->
|
||||
[one] Zbývá 1 sekunda
|
||||
@@ -607,3 +606,6 @@ onboarding-automatic_proportions-done-description = Kalibrace proporcí vašeho
|
||||
## Home
|
||||
|
||||
home-no_trackers = Nebyly zjištěny ani přiřazeny žádné trackery
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ skeleton_bone-HIP_OFFSET = Hüftversatz
|
||||
skeleton_bone-HIPS_WIDTH = Hüftbreite
|
||||
skeleton_bone-leg_group = Beinlänge
|
||||
skeleton_bone-UPPER_LEG = Linker Oberschenkellänge
|
||||
skeleton_bone-LOWER_LEG = Linker Unterschenkellänge
|
||||
skeleton_bone-LOWER_LEG = Unterschenkellänge
|
||||
skeleton_bone-FOOT_LENGTH = Fußlänge
|
||||
skeleton_bone-FOOT_SHIFT = Fußverschiebung
|
||||
skeleton_bone-SKELETON_OFFSET = Skelettversatz
|
||||
@@ -328,10 +328,10 @@ settings-general-fk_settings-leg_tweak-skating_correction = Rutschkorrektur
|
||||
settings-general-fk_settings-leg_tweak-toe_snap = Zehenausrichtung
|
||||
settings-general-fk_settings-leg_tweak-foot_plant = Fußkorrektur
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-amount = Rutschkorrekturstärke
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-description = Die Rutschkorrektur korrigiert das Wegrutschen des Fußes, kann aber die Genauigkeit bestimmter Bewegungsmuster verringern. Wenn du dies aktivierst, stellen sicher, dass du im Spiel dein Tracking vollständig zurücksetzt und neu kalibrierst.
|
||||
settings-general-fk_settings-leg_tweak-floor_clip-description = Bodenclip kann das Clipping durch den Boden reduzieren oder sogar eliminieren. Wenn du dies aktivierst, stelle sicher, dass du im Spiel dein Tracking vollständig zurücksetzt und neu kalibrierst.
|
||||
settings-general-fk_settings-leg_tweak-toe_snap-description = Zehen-Ausrichtung versucht, die Rotation deiner Füße zu erraten, wenn keine Fuß-Tracker verwendet werden.
|
||||
settings-general-fk_settings-leg_tweak-foot_plant-description = Fußkorrektur richtet deine Füße parallel zum Boden aus, wenn sie den Boden berühren.
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-description = Die Rutschkorrektur korrigiert das Wegrutschen des Fußes, kann aber die Genauigkeit bestimmter Bewegungsmuster verringern. Wenn Sie dies aktivieren, stellen Sie sicher, dass Sie im Spiel Ihr Tracking vollständig zurücksetzten und neu kalibrieren.
|
||||
settings-general-fk_settings-leg_tweak-floor_clip-description = Bodenclip kann das Clipping durch den Boden reduzieren oder sogar eliminieren. Wenn Sie dies aktivieren, stellen Sie sicher, dass Sie im Spiel Ihr Tracking vollständig zurücksetzten und neu kalibrieren.
|
||||
settings-general-fk_settings-leg_tweak-toe_snap-description = Zehen-Ausrichtung versucht, die Rotation Ihrer Füße zu erraten, wenn keine Fuß-Tracker verwendet werden.
|
||||
settings-general-fk_settings-leg_tweak-foot_plant-description = Fußkorrektur richtet Ihre Füße parallel zum Boden aus, wenn sie den Boden berühren.
|
||||
settings-general-fk_settings-leg_fk = Beintracking
|
||||
settings-general-fk_settings-arm_fk = Arm-Tracking
|
||||
settings-general-fk_settings-arm_fk-description = Ändern Sie die Art und Weise, wie die Arme berechnet werden.
|
||||
@@ -364,7 +364,7 @@ settings-general-gesture_control-fullResetEnabled = Vollständiger Reset durch A
|
||||
settings-general-gesture_control-fullResetDelay = Verzögerung für einen vollständigen Reset
|
||||
settings-general-gesture_control-fullResetTaps = Antipp-Anzahl für einen vollständigen Reset
|
||||
settings-general-gesture_control-mountingResetEnabled = Antippen für Befestigungs-Reset
|
||||
settings-general-gesture_control-mountingResetDelay = Befestigungs-Reset-Verzügerung
|
||||
settings-general-gesture_control-mountingResetDelay = Befestigungs-Reset-Verzögerung
|
||||
settings-general-gesture_control-mountingResetTaps = Anzahl für Befestigungs-Reset
|
||||
|
||||
## Interface settings
|
||||
@@ -522,7 +522,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Zurück zur Trackerausrichtung
|
||||
onboarding-reset_tutorial = Tutorial neustarten
|
||||
onboarding-reset_tutorial-description = Diese Funktion ist noch nicht fertig, drücken Sie einfach auf Fortsetzen
|
||||
onboarding-reset_tutorial-explanation = Während Sie Ihre Tracker verwenden, können sie aufgrund der IMU-Gierdrift oder weil Sie sie physisch bewegt haben, aus der Ausrichtung geraten. Sie haben mehrere Möglichkeiten, dies zu beheben.
|
||||
onboarding-reset_tutorial-skip = Schritt überspringen
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
@@ -694,11 +694,11 @@ onboarding-choose_mounting = Welche Kalibrierungsmethode ist zu verwenden?
|
||||
onboarding-choose_mounting-description = Die Montageausrichtung korrigiert die Platzierung von Trackern am Körper.
|
||||
onboarding-choose_mounting-auto_mounting = Befestigung automatisch ermitteln
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = Empfohlen
|
||||
onboarding-choose_mounting-auto_mounting-description = Dadurch werden die Befestigungsausrichtungen für alle deine Tracker automatisch aus 2 Posen erkannt
|
||||
onboarding-choose_mounting-auto_mounting-label = Experimentell
|
||||
onboarding-choose_mounting-auto_mounting-description = Dadurch werden die Befestigungsausrichtungen für alle Ihrer Tracker automatisch aus 2 Posen erkannt
|
||||
onboarding-choose_mounting-manual_mounting = Manuelle Befestigungsposition
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = Wenn du weißt, was du tust
|
||||
onboarding-choose_mounting-manual_mounting-label = Empfohlen
|
||||
onboarding-choose_mounting-manual_mounting-description = Auf diese Weise können Sie die Montagerichtung für jeden Tracker manuell auswählen
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -720,7 +720,7 @@ onboarding-automatic_mounting-prev_step = Vorheriger Schritt
|
||||
onboarding-automatic_mounting-done-title = Tracker Rotation kalibriert.
|
||||
onboarding-automatic_mounting-done-description = Ihre Rotations-Kalibrierung ist abgeschlossen!
|
||||
onboarding-automatic_mounting-done-restart = Zurück zum Start
|
||||
onboarding-automatic_mounting-mounting_reset-title = Drehungs-Reset
|
||||
onboarding-automatic_mounting-mounting_reset-title = Befestigungs-Reset
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. Beugen Sie sich in die "Skifahren"-Pose mit gebeugten Beinen, geneigtem Oberkörper und gebeugten Armen.
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. Drücken Sie die Schaltfläche "Befestigungs-Reset" und warten Sie 3 Sekunden, bevor die Drehungen der Tracker gesetzt werden.
|
||||
onboarding-automatic_mounting-preparation-title = Vorbereitung
|
||||
@@ -739,7 +739,7 @@ onboarding-choose_proportions-auto_proportions-subtitle = Empfohlen
|
||||
onboarding-choose_proportions-auto_proportions-description = Dadurch werden Ihre Proportionen erraten, indem Stichproben Ihrer Bewegungen verrechnet werden
|
||||
onboarding-choose_proportions-manual_proportions = Manuelle Körperproportionen
|
||||
onboarding-choose_proportions-manual_proportions-description = Auf diese Weise können Sie Ihre Proportionen manuell anpassen, indem Sie diese direkt ändern
|
||||
onboarding-choose_proportions-save = Proportionen speichern
|
||||
onboarding-choose_proportions-export = Proportionen exportieren
|
||||
onboarding-choose_proportions-file_type = Körperproportions-Datei
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,6 +10,9 @@
|
||||
websocket-connecting = Σύνδεση με τον διακομιστή
|
||||
websocket-connection_lost = Η σύνδεση μεταξύ του διακομιστή χάθηκε. Προσπαθώντας να επανασυνδεθεί...
|
||||
|
||||
## Update notification
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Δεν είστε σίγουροι ποιος ανιχνευτής είναι ποιος; Κουνήστε έναν ανιχνευτή και θα επισημάνει το αντίστοιχο στοιχείο.
|
||||
@@ -32,7 +32,7 @@ body_part-RIGHT_LOWER_LEG = Δεξιός αστράγαλος
|
||||
body_part-RIGHT_FOOT = Δεξί πόδι
|
||||
body_part-CHEST = Στήθος
|
||||
body_part-WAIST = Μέση
|
||||
body_part-HIP = γοφοί
|
||||
body_part-HIP = Γοφοί
|
||||
body_part-LEFT_SHOULDER = Αριστερός ώμος
|
||||
body_part-LEFT_UPPER_ARM = Αριστερό μπράτσο
|
||||
body_part-LEFT_LOWER_ARM = Αριστερό αγγόνας
|
||||
@@ -68,7 +68,6 @@ skeleton_bone-ELBOW_OFFSET = Μετατόπιση αγκώνα
|
||||
reset-reset_all = Επαναφορά όλων των αναλογιών
|
||||
reset-full = Επαναφορά
|
||||
reset-mounting = Επαναφορά τοποθέτησης
|
||||
reset-quick = Γρήγορη επαναφορά
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
@@ -89,11 +88,14 @@ navbar-mounting = Βαθμονόμηση τοποθέτησης
|
||||
navbar-onboarding = Οδηγός εγκατάστασης
|
||||
navbar-settings = Ρυθμίσεις
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = Εγγραφή BVH
|
||||
bvh-recording = Γίνεται εγγραφή...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Υπέρθεση
|
||||
@@ -191,7 +193,32 @@ body_assignment_menu-unassign_tracker = Μη εκχωρημένος ανιχνε
|
||||
|
||||
## Tracker assignment menu
|
||||
|
||||
# A -translation_key (with a dash in the front) means that it's a label.
|
||||
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
|
||||
#
|
||||
# We are using it here because english doesn't require changing the text in each case but
|
||||
# maybe your language does.
|
||||
-tracker_selection-part = Ποιος ιχνηλάτης πρέπει να αντιστοιχίσετε στο σας
|
||||
tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να είναι μη εκχωρημένος;
|
||||
tracker_selection_menu-HEAD = { -tracker_selection-part } κεφάλι;
|
||||
tracker_selection_menu-NECK = { -tracker_selection-part } λαιμός;
|
||||
tracker_selection_menu-RIGHT_SHOULDER = { -tracker_selection-part } δεξιός ώμος;
|
||||
tracker_selection_menu-RIGHT_UPPER_ARM = { -tracker_selection-part } δεξιό μπράτσο?
|
||||
tracker_selection_menu-RIGHT_LOWER_ARM = { -tracker_selection-part } δεξί αγγόνας?
|
||||
tracker_selection_menu-RIGHT_HAND = { -tracker_selection-part } δεξί χέρι?
|
||||
tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } δεξιός μηρός?
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } δεξιός αστράγαλος?
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } δεξί πόδι?
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part } στήθος?
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part } μέση?
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part } γοφοί?
|
||||
tracker_selection_menu-LEFT_SHOULDER = { -tracker_selection-part } αριστερός ώμος?
|
||||
tracker_selection_menu-LEFT_UPPER_ARM = { -tracker_selection-part } αριστερό μπράτσο?
|
||||
tracker_selection_menu-LEFT_LOWER_ARM = { -tracker_selection-part } αριστερό αγγόνας?
|
||||
tracker_selection_menu-LEFT_HAND = { -tracker_selection-part } αριστερό χέρι?
|
||||
tracker_selection_menu-LEFT_UPPER_LEG = { -tracker_selection-part } αριστερός μηρός?
|
||||
tracker_selection_menu-LEFT_LOWER_LEG = { -tracker_selection-part } αριστερός αστράγαλος?
|
||||
tracker_selection_menu-LEFT_FOOT = { -tracker_selection-part } αριστερό πόδι?
|
||||
|
||||
## Mounting menu
|
||||
|
||||
@@ -201,6 +228,11 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
|
||||
|
||||
## SteamVR settings
|
||||
|
||||
settings-general-steamvr = SteamVR
|
||||
settings-general-steamvr-trackers-waist = Μέση
|
||||
settings-general-steamvr-trackers-chest = Στήθος
|
||||
settings-general-steamvr-trackers-feet = Πόδια
|
||||
settings-general-steamvr-trackers-hands = Χέρια
|
||||
|
||||
## Tracker mechanics
|
||||
|
||||
@@ -222,6 +254,11 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
|
||||
|
||||
## OSC VRChat settings
|
||||
|
||||
settings-osc-vrchat-network-trackers-chest = Στήθος
|
||||
settings-osc-vrchat-network-trackers-feet = Πόδια
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
@@ -244,18 +281,30 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
|
||||
## Tracker connection setup
|
||||
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
|
||||
## Tracker assignment warnings
|
||||
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
|
||||
## Tracker automatic mounting setup
|
||||
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
|
||||
@@ -264,3 +313,6 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
|
||||
|
||||
## Home
|
||||
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ body_part-RIGHT_HAND = Right hand
|
||||
body_part-RIGHT_UPPER_LEG = Right thigh
|
||||
body_part-RIGHT_LOWER_LEG = Right ankle
|
||||
body_part-RIGHT_FOOT = Right foot
|
||||
body_part-UPPER_CHEST = Upper chest
|
||||
body_part-CHEST = Chest
|
||||
body_part-WAIST = Waist
|
||||
body_part-HIP = Hip
|
||||
@@ -47,8 +48,9 @@ skeleton_bone-NONE = None
|
||||
skeleton_bone-HEAD = Head Shift
|
||||
skeleton_bone-NECK = Neck Length
|
||||
skeleton_bone-torso_group = Torso length
|
||||
skeleton_bone-CHEST = Chest Length
|
||||
skeleton_bone-UPPER_CHEST = Upper Chest Length
|
||||
skeleton_bone-CHEST_OFFSET = Chest Offset
|
||||
skeleton_bone-CHEST = Chest Length
|
||||
skeleton_bone-WAIST = Waist Length
|
||||
skeleton_bone-HIP = Hip Length
|
||||
skeleton_bone-HIP_OFFSET = Hip Offset
|
||||
@@ -207,6 +209,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } right thigh
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } right ankle?
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } right foot?
|
||||
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } right controller?
|
||||
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } upper chest?
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part } chest?
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part } waist?
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part } hip?
|
||||
@@ -491,7 +494,7 @@ onboarding-wifi_creds-password =
|
||||
## Mounting setup
|
||||
onboarding-reset_tutorial-back = Go Back to Mounting calibration
|
||||
onboarding-reset_tutorial = Reset tutorial
|
||||
onboarding-reset_tutorial-description = While you use your trackers they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
|
||||
onboarding-reset_tutorial-explanation = While you use your trackers they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
|
||||
onboarding-reset_tutorial-skip = Skip step
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 = Tap { $taps } times the highlighted tracker for triggering yaw reset.
|
||||
@@ -649,11 +652,11 @@ onboarding-choose_mounting = What mounting calibration method to use?
|
||||
onboarding-choose_mounting-description = Mounting orientation corrects for the placement of trackers on your body.
|
||||
onboarding-choose_mounting-auto_mounting = Automatic mounting
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = Experimental
|
||||
onboarding-choose_mounting-auto_mounting-label = Experimental
|
||||
onboarding-choose_mounting-auto_mounting-description = This will automatically detect the mounting directions for all of your trackers from 2 poses
|
||||
onboarding-choose_mounting-manual_mounting = Manual mounting
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = Recommended
|
||||
onboarding-choose_mounting-manual_mounting-label = Recommended
|
||||
onboarding-choose_mounting-manual_mounting-description = This will let you choose the mounting direction manually for each tracker
|
||||
|
||||
|
||||
@@ -697,7 +700,7 @@ onboarding-choose_proportions-manual_proportions = Manual proportions
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = For small touches
|
||||
onboarding-choose_proportions-manual_proportions-description = This will let you adjust your proportions manually by modifying them directly
|
||||
onboarding-choose_proportions-save = Export proportions
|
||||
onboarding-choose_proportions-export = Export proportions
|
||||
onboarding-choose_proportions-file_type = Body proportions file
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -532,20 +532,20 @@ onboarding-reset_tutorial-skip = Saltar paso
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
Toca { $taps } veces el tracker resaltado para activar el reinicio horizontal.
|
||||
|
||||
|
||||
Esto va a hacer que tus sensores miren para la misma dirección que tu HMD.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-1 =
|
||||
Toca { $taps } veces el tracker resaltado para activar el reinicio completo.
|
||||
|
||||
|
||||
Se requiere que estas de forma parada (pose en i). Esto tiene un delay de 3 segundos (configurable) antes de que actualmente suceda.
|
||||
Esto reinicia completamente la posición y rotación de todos tus sensores, debería de arreglar la mayoría de tus problemas.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-2 =
|
||||
Toca { $taps } veces el tracker resaltado para activar el reinicio de montura.
|
||||
|
||||
|
||||
El reinicio de montura ayuda en como tus sensores están puestos en tu cuerpo, ya que si los movistes o cambiaste para donde están orientados bastante, esto debería de ayudar.
|
||||
|
||||
|
||||
Requiere que estas en una pose como que estás esquiando, como se muestra en el tutorial de montura automática y tenes un retraso de 3 segundos (configurable) antes de que actualmente suceda.
|
||||
|
||||
## Setup start
|
||||
@@ -758,7 +758,7 @@ onboarding-choose_proportions-manual_proportions = Proporciones manuales
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Para toques pequeños
|
||||
onboarding-choose_proportions-manual_proportions-description = Esto te permitirá ajustar tus proporciones manualmente de forma directa
|
||||
onboarding-choose_proportions-save = Exportar proporciones
|
||||
onboarding-choose_proportions-export = Exportar proporciones
|
||||
onboarding-choose_proportions-file_type = Archivo de proporciones del cuerpo
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -527,7 +527,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Retourner à l'alignement des capteurs
|
||||
onboarding-reset_tutorial = Didacticiel de réinitialisation
|
||||
onboarding-reset_tutorial-description = Cette fonctionnalité n'est pas encore terminée, appuyez simplement sur continuer
|
||||
onboarding-reset_tutorial-explanation = Pendant que vous utilisez vos capteurs, ils peuvent se désaligner à cause de la dérive horizontale du IMU, ou parce que vous les avez déplacés physiquement. Vous avez plusieurs façons de résoudre ce problème.
|
||||
onboarding-reset_tutorial-skip = Sauter l'étape
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
@@ -707,11 +707,11 @@ onboarding-choose_mounting = Quelle méthode de calibration de l’alignement ut
|
||||
onboarding-choose_mounting-description = La calibration de l'alignement ajuste pour l'orientation des capteurs sur votre corps.
|
||||
onboarding-choose_mounting-auto_mounting = Alignement automatique
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = Expérimentale
|
||||
onboarding-choose_mounting-auto_mounting-label = Expérimentale
|
||||
onboarding-choose_mounting-auto_mounting-description = Ceci permettra de détecter automatiquement la direction de tous vos capteurs à partir de 2 poses
|
||||
onboarding-choose_mounting-manual_mounting = Alignement manuel
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = Recommendée
|
||||
onboarding-choose_mounting-manual_mounting-label = Recommendée
|
||||
onboarding-choose_mounting-manual_mounting-description = Ceci vous permettra de choisir la direction de chaque capteur manuellement
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -758,7 +758,7 @@ onboarding-choose_proportions-manual_proportions = Proportions manuelles
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Pour les retouches
|
||||
onboarding-choose_proportions-manual_proportions-description = Ceci vous permettra d'ajuster vos proportions manuellement en les modifiant directement
|
||||
onboarding-choose_proportions-save = Exporter les proportions
|
||||
onboarding-choose_proportions-export = Exporter les proportions
|
||||
onboarding-choose_proportions-file_type = Fichier de proportions
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,6 +10,9 @@
|
||||
websocket-connecting = מתחבר לשרת
|
||||
websocket-connection_lost = החיבור לשרת אבד. מנסה להתחבר מחדש
|
||||
|
||||
## Update notification
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = לא בטוח איזה חיישן אתה מחזיק? נער את החיישן והתוכנה תסמן לך אותו.
|
||||
@@ -30,7 +30,6 @@ body_part-RIGHT_HAND = יד ימין
|
||||
body_part-RIGHT_UPPER_LEG = ירך ימין
|
||||
body_part-RIGHT_LOWER_LEG = קרסול ימין
|
||||
body_part-RIGHT_FOOT = רגל ימין
|
||||
body_part-RIGHT_CONTROLLER = בקר ימני
|
||||
body_part-CHEST = חזה
|
||||
body_part-WAIST = מותניים
|
||||
body_part-HIP = ירך
|
||||
@@ -41,7 +40,6 @@ body_part-LEFT_HAND = יד שמאל
|
||||
body_part-LEFT_UPPER_LEG = ירך שמאל
|
||||
body_part-LEFT_LOWER_LEG = קרסול שמאל
|
||||
body_part-LEFT_FOOT = רגל שמאל
|
||||
body_part-LEFT_CONTROLLER = בקר שמאלי
|
||||
|
||||
## Proportions
|
||||
|
||||
@@ -58,15 +56,12 @@ skeleton_bone-SHOULDERS_DISTANCE = מרחק כתפיים
|
||||
skeleton_bone-SHOULDERS_WIDTH = רוחב כתפיים
|
||||
skeleton_bone-UPPER_ARM = אורך זרוע עליונה
|
||||
skeleton_bone-LOWER_ARM = אורך זרוע תחתונה
|
||||
skeleton_bone-CONTROLLER_Y = מרחק בקר ציר Y
|
||||
skeleton_bone-CONTROLLER_Z = מרחק בקר ציר Z
|
||||
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = איפוס כל הפרופורציות
|
||||
reset-full = איפוס
|
||||
reset-mounting = איפוס הרכבה
|
||||
reset-quick = איפוס מהיר
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
@@ -87,11 +82,14 @@ navbar-mounting = כיול ההרכבה
|
||||
navbar-onboarding = אשף ההגדרה
|
||||
navbar-settings = הגדרות
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = הקלטת BVH
|
||||
bvh-recording = מקליט...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = ממשק Overlay
|
||||
@@ -256,6 +254,13 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = שימ
|
||||
|
||||
## OSC VRChat settings
|
||||
|
||||
settings-osc-vrchat-network-trackers-chest = חזה
|
||||
settings-osc-vrchat-network-trackers-knees = ברכיים
|
||||
settings-osc-vrchat-network-trackers-feet = רגל
|
||||
settings-osc-vrchat-network-trackers-elbows = מרפקים
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
@@ -278,24 +283,35 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = שימ
|
||||
## Tracker connection setup
|
||||
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
|
||||
## Tracker assignment warnings
|
||||
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
|
||||
## Tracker automatic mounting setup
|
||||
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
onboarding-automatic_proportions-recording-steps-4 = זוז במקום עד שהספירה תסתיים
|
||||
onboarding-automatic_proportions-recording-processing = מעבד את התוצאה
|
||||
onboarding-automatic_proportions-verify_results-title = אמת את התוצאות
|
||||
onboarding-automatic_proportions-verify_results-description = אנא בדוק את התוצאות, האם התוצאות נראות נכון?
|
||||
@@ -309,3 +325,6 @@ onboarding-automatic_proportions-done-description = תהליך כיול פרופ
|
||||
## Home
|
||||
|
||||
home-no_trackers = לא זוהו או הוקצו חיישנים
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ websocket-connection_lost = サーバーへの接続が失われました。再
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = 新しいバージョンが利用可能です:{ $version }
|
||||
version_update-description = { version_update-update }をクリックすると、SlimeVRインストーラーがダウンロードされます。
|
||||
version_update-update = アップデート
|
||||
version_update-close = 閉じる
|
||||
|
||||
@@ -19,6 +21,8 @@ version_update-close = 閉じる
|
||||
|
||||
tips-find_tracker = どのトラッカーがどれだかわからない?トラッカーを振ると、該当する項目がハイライトされます。
|
||||
tips-do_not_move_heels = レコーディング中にかかとが動かないように注意しましょう!
|
||||
tips-file_select = 使用するファイルをドラッグ&ドロップするか、 <u>参照</u>します。
|
||||
tips-tap_setup = 追跡装置をゆっくり2回軽くタップして選択することができます、メニューから選ぶ必要はありません
|
||||
|
||||
## Body parts
|
||||
|
||||
@@ -48,12 +52,14 @@ body_part-LEFT_FOOT = 左足先
|
||||
skeleton_bone-NONE = 無し
|
||||
skeleton_bone-HEAD = ヘッドシフト
|
||||
skeleton_bone-NECK = 首長さ
|
||||
skeleton_bone-CHEST = 胸囲
|
||||
skeleton_bone-torso_group = 胴体の長さ
|
||||
skeleton_bone-CHEST_OFFSET = 胸オフセット
|
||||
skeleton_bone-CHEST = 胸囲
|
||||
skeleton_bone-WAIST = ウエスト長さ
|
||||
skeleton_bone-HIP = ヒップ長さ
|
||||
skeleton_bone-HIP_OFFSET = ヒップオフセット
|
||||
skeleton_bone-HIPS_WIDTH = ヒップ幅
|
||||
skeleton_bone-leg_group = 股下の長さ
|
||||
skeleton_bone-UPPER_LEG = 膝長さ
|
||||
skeleton_bone-LOWER_LEG = 足長さ
|
||||
skeleton_bone-FOOT_LENGTH = 足先長さ
|
||||
@@ -61,8 +67,11 @@ skeleton_bone-FOOT_SHIFT = 足先シフト
|
||||
skeleton_bone-SKELETON_OFFSET = スケルトンオフセット
|
||||
skeleton_bone-SHOULDERS_DISTANCE = 肩の距離
|
||||
skeleton_bone-SHOULDERS_WIDTH = 肩幅
|
||||
skeleton_bone-arm_group = 腕の長さ
|
||||
skeleton_bone-UPPER_ARM = 上腕長さ
|
||||
skeleton_bone-LOWER_ARM = 前腕長さ
|
||||
skeleton_bone-HAND_Y = 手の距離 Y
|
||||
skeleton_bone-HAND_Z = 手の距離Z
|
||||
skeleton_bone-ELBOW_OFFSET = 肘オフセット
|
||||
|
||||
## Tracker reset buttons
|
||||
@@ -98,6 +107,8 @@ bvh-recording = レコーディング中...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = トラッキング停止
|
||||
tracking-paused = トラッキング再開
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
@@ -107,6 +118,7 @@ widget-overlay-is_mirrored_label = オーバーレイをミラーとして表示
|
||||
|
||||
## Widget: Drift compensation
|
||||
|
||||
widget-drift_compensation-clear = ドリフト補正をクリアする
|
||||
|
||||
## Widget: Developer settings
|
||||
|
||||
@@ -161,7 +173,11 @@ tracker-infos-manufacturer = メーカ-
|
||||
tracker-infos-display_name = 表示名
|
||||
tracker-infos-custom_name = カスタム名称
|
||||
tracker-infos-url = トラッカーURL
|
||||
tracker-infos-version = ファームウェアバージョン
|
||||
tracker-infos-hardware_rev = ハードウエアのリビジョン
|
||||
tracker-infos-hardware_identifier = ハードウェアID
|
||||
tracker-infos-imu = 慣性計測センサー
|
||||
tracker-infos-board_type = メインボード
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -228,6 +244,9 @@ tracker_selection_menu-LEFT_CONTROLLER = { -tracker_selection-part(body-part: "
|
||||
tracker_selection_menu-unassigned = 未割り当てのトラッカー
|
||||
tracker_selection_menu-assigned = 割り当て済みのトラッカー
|
||||
tracker_selection_menu-dont_assign = 割り当てない
|
||||
# This line cares about multilines.
|
||||
# <b>text</b> means that the text should be bold.
|
||||
tracker_selection_menu-neck_warning = <b>警告:</b>首のトラッカーを締め付けすぎると、頭部の血液循環に危険が生じる可能性があります!
|
||||
tracker_selection_menu-neck_warning-done = リスクを理解しています
|
||||
tracker_selection_menu-neck_warning-cancel = キャンセル
|
||||
|
||||
@@ -245,6 +264,7 @@ settings-sidebar-fk_settings = FK設定
|
||||
settings-sidebar-gesture_control = ジェスチャーコントロール
|
||||
settings-sidebar-interface = インターフェース
|
||||
settings-sidebar-osc_router = OSCルーター
|
||||
settings-sidebar-osc_trackers = VRChatOSCトラッカー
|
||||
settings-sidebar-utils = ユーティリティ
|
||||
settings-sidebar-serial = シリアルコンソール
|
||||
|
||||
@@ -304,6 +324,7 @@ settings-general-fk_settings-leg_tweak-floor_clip = フロアクリップ
|
||||
# definition - Guesses when each foot is in contact with the ground and uses that information to improve tracking
|
||||
settings-general-fk_settings-leg_tweak-skating_correction = スケーティング補正
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-amount = スケーティング補正の強さ
|
||||
settings-general-fk_settings-leg_fk = 足のトラッキング
|
||||
settings-general-fk_settings-arm_fk = アームトラッキング
|
||||
settings-general-fk_settings-arm_fk-description = 腕の追従方法を変更する。
|
||||
settings-general-fk_settings-arm_fk-force_arms = Force arms from HMD
|
||||
@@ -350,6 +371,7 @@ settings-general-interface-serial_detection-label = シリアルデバイスの
|
||||
settings-general-interface-feedback_sound = フィードバック音
|
||||
settings-general-interface-feedback_sound-label = フィードバック音
|
||||
settings-general-interface-feedback_sound-volume = フィードバック音量
|
||||
settings-general-interface-theme = カラーテーマ
|
||||
settings-general-interface-lang = 言語を選択
|
||||
settings-general-interface-lang-description = 使用したいデフォルトの言語を変更する
|
||||
settings-general-interface-lang-placeholder = 使用する言語を選択する
|
||||
@@ -419,6 +441,7 @@ settings-osc-vrchat-network-address-placeholder = VRChatのIPアドレス
|
||||
settings-osc-vrchat-network-trackers = トラッカー
|
||||
settings-osc-vrchat-network-trackers-description = データの送受信を切り替える。
|
||||
settings-osc-vrchat-network-trackers-chest = 胸
|
||||
settings-osc-vrchat-network-trackers-hip = 腰
|
||||
settings-osc-vrchat-network-trackers-knees = 膝
|
||||
settings-osc-vrchat-network-trackers-feet = 足
|
||||
settings-osc-vrchat-network-trackers-elbows = 肘
|
||||
@@ -429,6 +452,12 @@ settings-osc-vmc = バーチャルモーションキャプチャ
|
||||
settings-osc-vmc-enable = 有効
|
||||
settings-osc-vmc-enable-label = 有効
|
||||
settings-osc-vmc-network = ネットワークポート
|
||||
settings-osc-vmc-network-port_in =
|
||||
.label = ポートイン
|
||||
.placeholder = ポートイン(デフォルト:39540)
|
||||
settings-osc-vmc-network-port_out =
|
||||
.label = ポートアウト
|
||||
.placeholder = ポートアウト(デフォルト:39539)
|
||||
settings-osc-vmc-network-address = ネットワークアドレス
|
||||
settings-osc-vmc-network-address-placeholder = IPV4アドレス
|
||||
settings-osc-vmc-vrm = VRMモデル
|
||||
@@ -438,6 +467,8 @@ settings-osc-vmc-vrm = VRMモデル
|
||||
onboarding-skip = 設定をスキップする
|
||||
onboarding-continue = 続ける
|
||||
onboarding-wip = 実行中
|
||||
onboarding-setup_warning-skip = セットアップをスキップする
|
||||
onboarding-setup_warning-cancel = セットアップを続行する
|
||||
|
||||
## Wi-Fi setup
|
||||
|
||||
@@ -460,7 +491,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = マウントキャリブレーションに戻る
|
||||
onboarding-reset_tutorial = リセットチュートリアル
|
||||
onboarding-reset_tutorial-description = この機能は終了していません。続けるを押してください。
|
||||
onboarding-reset_tutorial-skip = ステップをスキップする
|
||||
|
||||
## Setup start
|
||||
|
||||
@@ -488,7 +519,12 @@ onboarding-connect_tracker-description-p0 = さあ、楽しい部分に移りま
|
||||
onboarding-connect_tracker-description-p1 = まだ接続されていないトラッカーたちをUSBポートを通して接続するだけです。
|
||||
onboarding-connect_tracker-issue-serial = 接続に問題があります!
|
||||
onboarding-connect_tracker-usb = USBトラッカー
|
||||
onboarding-connect_tracker-connection_status-none = トラッカーを探しています
|
||||
onboarding-connect_tracker-connection_status-connecting = Wi-Fiの認証情報を送信中
|
||||
onboarding-connect_tracker-connection_status-looking_for_server = サーバーを探しています
|
||||
onboarding-connect_tracker-connection_status-connection_error = Wi-Fiに接続できません
|
||||
onboarding-connect_tracker-connection_status-could_not_find_server = サーバーが見つかりません
|
||||
onboarding-connect_tracker-connection_status-done = サーバーに接続されました
|
||||
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
|
||||
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
|
||||
# English in this case only has 2 plural rules, which are "one" and "other",
|
||||
@@ -504,10 +540,14 @@ onboarding-connect_tracker-next = すべてのトラッカーを接続しまし
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial = IMU校正チュートリアル
|
||||
onboarding-calibration_tutorial-subtitle = これにより、センサーのドリフトを減らすことが役立ちます
|
||||
onboarding-calibration_tutorial-status-calibrating = 校正中
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = ステッカー
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
@@ -530,8 +570,6 @@ onboarding-assign_trackers-next = すべてのトラッカーを割り当てま
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = おすすめされた
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
@@ -614,3 +652,10 @@ home-no_trackers = トラッカーを検出できません。もしくは割り
|
||||
|
||||
## Status system
|
||||
|
||||
status_system-StatusTrackerReset = 一つ以上のトラッカーが調整されていないため、完全なリセットを実行することをお勧めします
|
||||
status_system-StatusSteamVRDisconnected =
|
||||
{ $type ->
|
||||
[steamvr_feeder] SlimeVR Feederアプリに接続されていません
|
||||
*[other] SlimeVRドライバ経由でSteamVRに接続されていません
|
||||
}
|
||||
status_system-StatusTrackerError = { $trackerName } トラッカーにエラーが発生しています
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,11 +10,19 @@
|
||||
websocket-connecting = 서버에 연결하는 중...
|
||||
websocket-connection_lost = 서버와의 연결이 끊어졌어요. 다시 연결하는 중...
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = 새로운 버전 발견: { $version }
|
||||
version_update-description = "{ version_update-update }"를 눌러 설치 프로그램을 다운로드하세요.
|
||||
version_update-update = 업데이트
|
||||
version_update-close = 닫기
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = 내 트래커가 어떤 트래커인지 모르시겠다구요? 트래커를 흔들면 해당 항목이 빛날 거예요.
|
||||
tips-do_not_move_heels = 기록하는 동안 발뒤꿈치가 움직이지 않도록 조심하세요!
|
||||
tips-file_select = 파일을 <u>열거나,</u> 여기에 드래그&드롭하세요.
|
||||
tips-tap_setup = 목록에서 트래커를 선택하는 대신 할당할 트래커를 천천히 2번 탭해서 선택할 수 있어요.
|
||||
|
||||
## Body parts
|
||||
|
||||
@@ -95,11 +100,16 @@ navbar-mounting = 착용 방향 정렬
|
||||
navbar-onboarding = 설정 마법사
|
||||
navbar-settings = 설정
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = BVH 기록
|
||||
bvh-recording = 기록중...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = 트래킹 일시 중지
|
||||
tracking-paused = 트래킹 재개
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = 오버레이
|
||||
@@ -126,6 +136,7 @@ widget-developer_mode-more_info = 더 많은 정보 보기
|
||||
widget-imu_visualizer = 회전
|
||||
widget-imu_visualizer-rotation_raw = Raw
|
||||
widget-imu_visualizer-rotation_preview = 미리보기
|
||||
widget-imu_visualizer-rotation_hide = 숨기기
|
||||
|
||||
## Tracker status
|
||||
|
||||
@@ -164,6 +175,9 @@ tracker-infos-custom_name = 사용자 정의 이름
|
||||
tracker-infos-url = 트래커 URL
|
||||
tracker-infos-version = 펌웨어 버전
|
||||
tracker-infos-hardware_rev = 하드웨어 리비전
|
||||
tracker-infos-hardware_identifier = 하드웨어 ID
|
||||
tracker-infos-imu = IMU 센서
|
||||
tracker-infos-board_type = 메인보드
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -252,6 +266,7 @@ settings-sidebar-fk_settings = FK 설정
|
||||
settings-sidebar-gesture_control = 제스처 제어
|
||||
settings-sidebar-interface = 인터페이스
|
||||
settings-sidebar-osc_router = OSC 라우터
|
||||
settings-sidebar-osc_trackers = VRChat OSC 트래커
|
||||
settings-sidebar-utils = 유틸리티
|
||||
settings-sidebar-serial = 시리얼 콘솔
|
||||
|
||||
@@ -439,7 +454,7 @@ settings-osc-vrchat-network-address-placeholder = VRChat IP 주소
|
||||
settings-osc-vrchat-network-trackers = 트래커
|
||||
settings-osc-vrchat-network-trackers-description = 활성화해서 데이터 송수신
|
||||
settings-osc-vrchat-network-trackers-chest = Chest
|
||||
settings-osc-vrchat-network-trackers-waist = Waist
|
||||
settings-osc-vrchat-network-trackers-hip = 골반
|
||||
settings-osc-vrchat-network-trackers-knees = Knees
|
||||
settings-osc-vrchat-network-trackers-feet = Feet
|
||||
settings-osc-vrchat-network-trackers-elbows = Elbows
|
||||
@@ -511,7 +526,6 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = 착용 방향 정렬로 돌아가기
|
||||
onboarding-reset_tutorial = 정렬 튜토리얼
|
||||
onboarding-reset_tutorial-description = 이 기능은 아직 완성되지 않았어요, 지금은 일단 계속하기를 눌러주세요!
|
||||
|
||||
## Setup start
|
||||
|
||||
@@ -559,6 +573,22 @@ onboarding-connect_tracker-connected_trackers =
|
||||
}
|
||||
onboarding-connect_tracker-next = 모든 트래커를 잘 연결했어요
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial = IMU 캘리브레이션 튜토리얼
|
||||
onboarding-calibration_tutorial-subtitle = 트래커 틀어짐을 줄이는 데 도움이 될 거예요!
|
||||
onboarding-calibration_tutorial-status-calibrating = 캘리브레이팅
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
onboarding-assignment_tutorial = 슬라임 트래커를 착용하기 전에 준비하는 방법
|
||||
onboarding-assignment_tutorial-first_step = 1. 신체 부위가 적힌 스티커를 가지고 있다면 트래커에 붙여보세요
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = 스티커
|
||||
onboarding-assignment_tutorial-second_step = 2. 스트랩의 벨크로 테이프 쪽을 그림과 같은 방향으로 유지하면서 스트랩을 트래커에 끼우세요:
|
||||
onboarding-assignment_tutorial-second_step-continuation = 익스텐션 트래커의 벨크로 테이프는 다음과 같은 방향으로 끼워주세요:
|
||||
onboarding-assignment_tutorial-done = 스트랩과 스티커를 트래커에 잘 부착했어요!
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Wi-Fi 자격 증명으로 돌아가기
|
||||
@@ -642,13 +672,11 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
## Tracker mounting method choose
|
||||
|
||||
onboarding-choose_mounting = 착용 방향을 정렬하기 위해 어떤 방법을 사용할래요?
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = 착용 방향 정렬은 트래커가 몸에 착용된 방향을 찾아 수정하도록 도와줘요.
|
||||
onboarding-choose_mounting-auto_mounting = 자동으로 방향 설정
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = 권장
|
||||
onboarding-choose_mounting-auto_mounting-description = 이렇게 하면 두 가지 자세로 모든 트래커의 착용 방향을 자동으로 설정할 수 있어요
|
||||
onboarding-choose_mounting-manual_mounting = 수동으로 방향 설정
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = 무엇을 하려는 지 알고 있다면요
|
||||
onboarding-choose_mounting-manual_mounting-description = 이렇게 하면 각 트래커의 착용 방향을 직접 고를 수 있어요
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -682,7 +710,7 @@ onboarding-automatic_mounting-put_trackers_on-next = 모든 트래커를 착용
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = 신체 비율을 설정하 위해 어떤 방법을 사용할래요?
|
||||
onboarding-choose_proportions = 신체 비율을 설정하기 위해 어떤 방법을 사용할래요?
|
||||
onboarding-choose_proportions-auto_proportions = 자동으로 비율 설정
|
||||
# Italized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = 권장
|
||||
@@ -752,3 +780,6 @@ onboarding-automatic_proportions-done-description = 신체 비율 보정이 완
|
||||
## Home
|
||||
|
||||
home-no_trackers = 감지되거나 할당된 트래커가 없어요.
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -103,11 +100,16 @@ navbar-mounting = Bevestigings- kalibratie
|
||||
navbar-onboarding = Setupgids
|
||||
navbar-settings = Instellingen
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = BVH opnemen
|
||||
bvh-recording = Opname bezig...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = Pauzeer tracking
|
||||
tracking-paused = Herneem tracking
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Overlay
|
||||
@@ -521,7 +523,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Ga terug naar de bevestigingskalibratie
|
||||
onboarding-reset_tutorial = Reset tutorial
|
||||
onboarding-reset_tutorial-description = Deze stap is nog niet afgewerkt, druk gewoon op doorgaan.
|
||||
onboarding-reset_tutorial-skip = Stap overslaan
|
||||
|
||||
## Setup start
|
||||
|
||||
@@ -581,6 +583,9 @@ onboarding-calibration_tutorial-status-calibrating = Kalibreren
|
||||
onboarding-calibration_tutorial-status-success = Aardig!
|
||||
onboarding-calibration_tutorial-status-error = De tracker werd verplaatst
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Ga terug naar de instellingen voor WiFi-configuratie
|
||||
@@ -670,11 +675,11 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
onboarding-choose_mounting = Welke montagekalibratiemethode moet worden gebruikt?
|
||||
onboarding-choose_mounting-auto_mounting = Automatische bevestiging
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = Aanbevolen
|
||||
onboarding-choose_mounting-auto_mounting-label = Experimenteel
|
||||
onboarding-choose_mounting-auto_mounting-description = Dit detecteert automatisch de montagerichtingen voor al uw trackers door middel van 2 poses
|
||||
onboarding-choose_mounting-manual_mounting = Handmatige bevestiging
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = Als je weet wat je doet
|
||||
onboarding-choose_mounting-manual_mounting-label = Aanbevolen
|
||||
onboarding-choose_mounting-manual_mounting-description = Hiermee kunt u de montagerichting handmatig kiezen voor elke tracker
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -779,3 +784,6 @@ onboarding-automatic_proportions-done-description = Je kalibratie voor lichaamsv
|
||||
## Home
|
||||
|
||||
home-no_trackers = Geen trackers gedetecteerd of toegewezen
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ body_part-RIGHT_HAND = Prawa Dłoń
|
||||
body_part-RIGHT_UPPER_LEG = Prawe Udo
|
||||
body_part-RIGHT_LOWER_LEG = Prawe Podudzie
|
||||
body_part-RIGHT_FOOT = Prawa Stopa
|
||||
body_part-UPPER_CHEST = Górna część klatki piersiowej
|
||||
body_part-CHEST = Klatka Piersiowa
|
||||
body_part-WAIST = Talia
|
||||
body_part-HIP = Biodra
|
||||
@@ -53,8 +54,9 @@ skeleton_bone-NONE = Brak
|
||||
skeleton_bone-HEAD = Przesunięcie Głowy
|
||||
skeleton_bone-NECK = Długość Szyi
|
||||
skeleton_bone-torso_group = Długość torsu
|
||||
skeleton_bone-CHEST = Długość Klatki Piersiowej
|
||||
skeleton_bone-UPPER_CHEST = Górna długość klatki piersiowej
|
||||
skeleton_bone-CHEST_OFFSET = Przesunięcie Klatki Piersiowej
|
||||
skeleton_bone-CHEST = Długość Klatki Piersiowej
|
||||
skeleton_bone-WAIST = Długość Talii
|
||||
skeleton_bone-HIP = Długość Bioder
|
||||
skeleton_bone-HIP_OFFSET = Przesunięcie Bioder
|
||||
@@ -230,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } prawe udo?
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } prawa kostka?
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } prawa stopa?
|
||||
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } prawy kontroler?
|
||||
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } górnej części klatki piersiowej?
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part } klatka piersiowa?
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part } talia?
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part } biodro?
|
||||
@@ -529,7 +532,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Powrót do Konfiguracji Położenia trackerów
|
||||
onboarding-reset_tutorial = Zresetuj poradnik
|
||||
onboarding-reset_tutorial-description = Ta funkcja jeszcze nie jest skończona.
|
||||
onboarding-reset_tutorial-explanation = Podczas korzystania z trackerów mogą się one rozregulować z powodu dryfu odchylenia IMU lub z powodu fizycznego przeniesienia ich. Możesz to naprawić na kilka sposobów.
|
||||
onboarding-reset_tutorial-skip = Pomiń krok
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
@@ -713,11 +716,11 @@ onboarding-choose_mounting = Jakiej metody kalibracji montażu użyć?
|
||||
onboarding-choose_mounting-description = Orientacja montażu koryguje umieszczenie trackerów na ciele.
|
||||
onboarding-choose_mounting-auto_mounting = Automatyczne mocowanie
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = Zalecana
|
||||
onboarding-choose_mounting-auto_mounting-label = Eksperymentalny
|
||||
onboarding-choose_mounting-auto_mounting-description = To automatycznie wykryje kierunki montażu dla wszystkich twoich trackerów z 2 pozycji
|
||||
onboarding-choose_mounting-manual_mounting = Montaż ręczny
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = Jeśli wiesz, co robisz
|
||||
onboarding-choose_mounting-manual_mounting-label = Zalecany
|
||||
onboarding-choose_mounting-manual_mounting-description = Umożliwi to ręczne wybranie kierunku montażu dla każdego trackera
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -764,7 +767,7 @@ onboarding-choose_proportions-manual_proportions = Ręczne proporcje
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Drobne detale
|
||||
onboarding-choose_proportions-manual_proportions-description = Umożliwi to ręczne dostosowanie proporcji poprzez ich bezpośrednią modyfikację
|
||||
onboarding-choose_proportions-save = Zachowaj proporcje
|
||||
onboarding-choose_proportions-export = Eksportuj proporcje
|
||||
onboarding-choose_proportions-file_type = Proporcje ciała
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,10 +10,19 @@
|
||||
websocket-connecting = Conectando ao servidor
|
||||
websocket-connection_lost = Conexão perdida com o servidor. Reconectando...
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = Nova versão disponível: { $version }
|
||||
version_update-description = Ao clicar em "{ version_update-update }" irá baixar o instalador do SlimeVR para você.
|
||||
version_update-update = Atualizar
|
||||
version_update-close = Fechar
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Não tem certeza qual tracker é qual? Balance o tracker e ele destacará o item correspondente.
|
||||
tips-do_not_move_heels = Tenha certeza de não mexer seus calcanhares durante a gravação!
|
||||
tips-file_select = Arraste e solte arquivos para usar, ou <u>pesquise</u>.
|
||||
tips-tap_setup = Pode tocar lentamente 2 vezes no seu tracker para o escolher em vez de o selecionar no menu.
|
||||
|
||||
## Body parts
|
||||
|
||||
@@ -70,7 +76,6 @@ skeleton_bone-ELBOW_OFFSET = Compensação do Cotovelo
|
||||
reset-reset_all = Redefinir todas as proporções
|
||||
reset-full = Reset
|
||||
reset-mounting = Reset de Posição
|
||||
reset-quick = Reset Rápido
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
@@ -91,11 +96,14 @@ navbar-mounting = Calibragem de Posição
|
||||
navbar-onboarding = Assistente de Configuração
|
||||
navbar-settings = Opções
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = Gravar BVH
|
||||
bvh-recording = Gravando...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Overlay
|
||||
@@ -297,8 +305,6 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = Use at
|
||||
## FK/Tracking settings
|
||||
|
||||
settings-general-fk_settings = Opções de Tracker
|
||||
settings-general-fk_settings-leg_tweak = Ajustes de perna
|
||||
settings-general-fk_settings-leg_tweak-description = Atravessar o chão pode reduzir ou até eliminar o clipping(atravessar) com o chão porém pode causar problemas quando ajoelhado. Correção de Deslize corrige o ice skating(deslize dos trackers no chão), porém pode diminuir a precisão de certos padrões de movimento.
|
||||
# Floor clip:
|
||||
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
|
||||
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
|
||||
@@ -333,12 +339,6 @@ settings-general-gesture_control-taps =
|
||||
[one] 1 tap
|
||||
*[other] { $amount } taps
|
||||
}
|
||||
settings-general-gesture_control-quickResetEnabled = Ativar toque para reset rápido
|
||||
settings-general-gesture_control-quickResetDelay = Delay do reset rápido
|
||||
settings-general-gesture_control-quickResetTaps = Toques para o reset rápido
|
||||
settings-general-gesture_control-resetEnabled = Ativar toque para reset
|
||||
settings-general-gesture_control-resetDelay = Delay do reset
|
||||
settings-general-gesture_control-resetTaps = Toques para o reset
|
||||
settings-general-gesture_control-mountingResetEnabled = Toques para o reset de posição
|
||||
settings-general-gesture_control-mountingResetDelay = Delay do reset de posição
|
||||
settings-general-gesture_control-mountingResetTaps = Toques para o reset de posição
|
||||
@@ -426,11 +426,13 @@ settings-osc-vrchat-network-address-placeholder = Endereço de ip do VRChat
|
||||
settings-osc-vrchat-network-trackers = Trackers
|
||||
settings-osc-vrchat-network-trackers-description = Ligar ou desligar o envio e recepção de dados.
|
||||
settings-osc-vrchat-network-trackers-chest = Peito
|
||||
settings-osc-vrchat-network-trackers-waist = Cintura
|
||||
settings-osc-vrchat-network-trackers-knees = Joelhos
|
||||
settings-osc-vrchat-network-trackers-feet = Pés
|
||||
settings-osc-vrchat-network-trackers-elbows = Cotovelos
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
onboarding-skip = Pular configurações
|
||||
@@ -463,10 +465,6 @@ onboarding-reset_tutorial-description = Esse recurso não está concluído, apen
|
||||
## Setup start
|
||||
|
||||
onboarding-home = Bem vindo ao SlimeVR
|
||||
# This cares about multilines and it's centered!!
|
||||
onboarding-home-description =
|
||||
Trazendo full-body tracking
|
||||
para todos
|
||||
onboarding-home-start = Vamos configurar!
|
||||
|
||||
## Enter VR part of setup
|
||||
@@ -511,6 +509,12 @@ onboarding-connect_tracker-connected_trackers =
|
||||
} connected
|
||||
onboarding-connect_tracker-next = Eu conectei todos os meus trackers
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Voltar para as credenciais de Wi-Fi
|
||||
@@ -595,6 +599,9 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
*[unknown] Cintura está atribuído, porém a parte do corpo desconhecida não atribuída também precisa ser atribuída!
|
||||
}
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
onboarding-manual_mounting-back = Voltar para entrar no VR
|
||||
@@ -624,6 +631,9 @@ onboarding-automatic_mounting-put_trackers_on-title = Coloque seus trackers
|
||||
onboarding-automatic_mounting-put_trackers_on-description = Para calibrar as rotações de posicionamento, usaremos os trackers que você atribuiu. Coloque todos os seus trackers, você pode ver qual é qual na figura na direita.
|
||||
onboarding-automatic_mounting-put_trackers_on-next = Coloquei todos os meus trackers
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
onboarding-manual_proportions-back = Voltar para o tutorial de reset
|
||||
@@ -641,22 +651,14 @@ onboarding-automatic_proportions-prev_step = Passo anterior
|
||||
onboarding-automatic_proportions-put_trackers_on-title = Coloque seus trackers
|
||||
onboarding-automatic_proportions-put_trackers_on-description = Para calibrar suas proporções, usaremos os trackers que você atribuiu. Coloque todos os seus trackers, você pode ver quais são quais na figura à direita.
|
||||
onboarding-automatic_proportions-put_trackers_on-next = Coloquei todos os meus trackers
|
||||
onboarding-automatic_proportions-preparation-title = Preparação
|
||||
onboarding-automatic_proportions-preparation-description = Coloque uma cadeira diretamente atrás de você dentro da sua área de jogo(Play space). Esteja preparado para sentar durante a configuração de autobone.
|
||||
onboarding-automatic_proportions-preparation-next = Estou em frente a uma cadeira
|
||||
onboarding-automatic_proportions-start_recording-title = Esteja preparado para se mexer
|
||||
onboarding-automatic_proportions-start_recording-description = Começaremos a gravar algumas poses e movimentos específicos. Estes serão solicitados na próxima tela. Esteja preparado para começar quando o botão for pressionado!
|
||||
onboarding-automatic_proportions-start_recording-next = Começar Gravação
|
||||
onboarding-automatic_proportions-recording-title = GRAVAR
|
||||
onboarding-automatic_proportions-recording-description-p0 = Gravação em progresso...
|
||||
onboarding-automatic_proportions-recording-description-p1 = Faça os movimentos apresentados abaixo:
|
||||
onboarding-automatic_proportions-recording-steps-0 = Dobre os joelhos algumas vezes.
|
||||
onboarding-automatic_proportions-recording-steps-1 = Sente-se na cadeira e se levante.
|
||||
onboarding-automatic_proportions-recording-steps-2 = Gire seu tronco para esquerda, e incline para direita.
|
||||
onboarding-automatic_proportions-recording-steps-3 = Gire seu tronco para direita, e incline para esquerda.
|
||||
onboarding-automatic_proportions-recording-steps-4 = Mexa-se até o tempo terminar.
|
||||
onboarding-automatic_proportions-recording-processing = Processando o resultado
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 15)
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
|
||||
onboarding-automatic_proportions-recording-timer =
|
||||
{ $time ->
|
||||
[one] 1 second left
|
||||
@@ -674,3 +676,6 @@ onboarding-automatic_proportions-done-description = Sua calibragem de proporçã
|
||||
## Home
|
||||
|
||||
home-no_trackers = Nenhum tracker detectado ou atribuído
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,11 +10,19 @@
|
||||
websocket-connecting = Подключение к серверу
|
||||
websocket-connection_lost = Потеряно соединение с сервером. Переподключение...
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = Доступна новая версия: { $version }
|
||||
version_update-description = Нажав «{ version_update-update }», вы загрузите установщик SlimeVR.
|
||||
version_update-update = Обновить
|
||||
version_update-close = Закрыть
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Не уверены, какой это трекер? Встряхните его, и трекер выделится в списке.
|
||||
tips-do_not_move_heels = Убедитесь, что ваши пятки не двигаются во время записи!
|
||||
tips-file_select = Выберите или перетащите файлы для использования <u>выбрать</u>.
|
||||
tips-tap_setup = Вы можете медленно нажать 2 раза на свой трекер, чтобы выбрать его, вместо того чтобы выбирать его из меню.
|
||||
|
||||
## Body parts
|
||||
|
||||
@@ -95,11 +100,16 @@ navbar-mounting = Калибровка крепления
|
||||
navbar-onboarding = Мастер настройки
|
||||
navbar-settings = Настройки
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = Запись BVH
|
||||
bvh-recording = Запись...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = Приостановить отслеживание
|
||||
tracking-paused = Возобновить отслеживание
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Оверлей
|
||||
@@ -126,6 +136,7 @@ widget-developer_mode-more_info = Дополнительная информац
|
||||
widget-imu_visualizer = Вращение
|
||||
widget-imu_visualizer-rotation_raw = RAW
|
||||
widget-imu_visualizer-rotation_preview = Предпросмотр
|
||||
widget-imu_visualizer-rotation_hide = Скрыть
|
||||
|
||||
## Tracker status
|
||||
|
||||
@@ -164,6 +175,9 @@ tracker-infos-custom_name = Свое имя
|
||||
tracker-infos-url = URL трекера
|
||||
tracker-infos-version = Версия прошивки
|
||||
tracker-infos-hardware_rev = Ревизия устройства
|
||||
tracker-infos-hardware_identifier = ID оборудования
|
||||
tracker-infos-imu = Датчик IMU
|
||||
tracker-infos-board_type = Основная плата
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -176,7 +190,7 @@ tracker-settings-mounting_section = Положение крепления
|
||||
tracker-settings-mounting_section-description = Где прикреплен трекер?
|
||||
tracker-settings-mounting_section-edit = Изменить прикрепление
|
||||
tracker-settings-drift_compensation_section = Разрешить компенсацию дрифта
|
||||
tracker-settings-drift_compensation_section-description = Должен ли этот трекер компенсировать свой дрифт, когда включена компенсация дрифта?
|
||||
tracker-settings-drift_compensation_section-description = Должен ли этот трекер компенсировать свой дрифт?
|
||||
tracker-settings-drift_compensation_section-edit = Разрешить компенсацию дрифта
|
||||
# The .<name> means it's an attribute and it's related to the top key.
|
||||
# In this case that is the settings for the assignment section.
|
||||
@@ -252,6 +266,7 @@ settings-sidebar-fk_settings = Настройки трекеров
|
||||
settings-sidebar-gesture_control = Настройки жестов
|
||||
settings-sidebar-interface = Интерфейс
|
||||
settings-sidebar-osc_router = OSC роутер
|
||||
settings-sidebar-osc_trackers = VRChat OSC Трекеры
|
||||
settings-sidebar-utils = Утилиты
|
||||
settings-sidebar-serial = Консоль
|
||||
|
||||
@@ -442,6 +457,7 @@ settings-osc-vrchat-network-address-placeholder = VRChat ip адрес
|
||||
settings-osc-vrchat-network-trackers = Трекеры
|
||||
settings-osc-vrchat-network-trackers-description = Переключите отправку определенных трекеров через OSC.
|
||||
settings-osc-vrchat-network-trackers-chest = Грудь
|
||||
settings-osc-vrchat-network-trackers-hip = Таз
|
||||
settings-osc-vrchat-network-trackers-knees = Колени
|
||||
settings-osc-vrchat-network-trackers-feet = Ступни
|
||||
settings-osc-vrchat-network-trackers-elbows = Локти
|
||||
@@ -490,6 +506,7 @@ onboarding-setup_warning =
|
||||
<b>Предупреждение.</b> Для правильного отслеживания требуется первоначальная настройка,
|
||||
это необходимо, если вы впервые используете SlimeVR.
|
||||
onboarding-setup_warning-skip = Пропустить настройку
|
||||
onboarding-setup_warning-cancel = Продолжить настройку
|
||||
|
||||
## Wi-Fi setup
|
||||
|
||||
@@ -512,7 +529,26 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = Вернуться к калибровке крепления
|
||||
onboarding-reset_tutorial = Сбросить туториал
|
||||
onboarding-reset_tutorial-description = Эта функция не завершена, просто нажмите продолжить
|
||||
onboarding-reset_tutorial-explanation = Пока вы пользуетесь своими трекерами, они могут не выровняться из-за дрейфа IMU при рыскании или из-за того, что вы, возможно, переместили их физически. У вас есть несколько способов исправить это.
|
||||
onboarding-reset_tutorial-skip = Пропустить шаг
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
Коснитесь { $taps } раз выделенного трекера, чтобы активировать сброс рыскания.
|
||||
|
||||
Это заставит трекеры смотреть в том же направлении, что и ваш HMD.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-1 =
|
||||
Нажмите { $taps } раз выделенный трекер, чтобы запустить полный сброс.
|
||||
|
||||
Вы должны стоять для этого в (i-позе). Существует задержка в 3 секунды (настраиваемая), прежде чем сброс произойдет.
|
||||
Это полностью сбрасывает положение и вращение всех ваших трекеров. Это должно исправить большинство проблем.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-2 =
|
||||
Нажмите { $taps } несколько раз на выделенный трекер, чтобы активировать сброс настроек.
|
||||
|
||||
Сброс монтажа помогает узнать, как на самом деле на вас надеты трекеры, поэтому, если вы случайно переместили их и сильно изменили их ориентацию, это поможет.
|
||||
|
||||
Вы должны быть в позе, как будто вы катаетесь на лыжах, как показано в мастере автоматического монтажа, и у вас есть 3-секундная задержка (настраиваемая) перед тем, как она сработает.
|
||||
|
||||
## Setup start
|
||||
|
||||
@@ -563,6 +599,27 @@ onboarding-connect_tracker-connected_trackers =
|
||||
}
|
||||
onboarding-connect_tracker-next = Я подключил все трекеры!
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial = Учебное пособие по калибровке IMU
|
||||
onboarding-calibration_tutorial-subtitle = Это поможет уменьшить дрифт трекера!
|
||||
onboarding-calibration_tutorial-description = Каждый раз, когда вы включаете трекеры, они должны на мгновение отдохнуть на плоской поверхности для калибровки. Давайте сделаем то же самое, нажав кнопку «{ onboarding-calibration_tutorial-calibrate }», <b>не перемещайте их!</b>
|
||||
onboarding-calibration_tutorial-calibrate = Я положил свои трекеры на стол
|
||||
onboarding-calibration_tutorial-status-waiting = Ждем вас
|
||||
onboarding-calibration_tutorial-status-calibrating = Калибровка
|
||||
onboarding-calibration_tutorial-status-success = Хорошо!
|
||||
onboarding-calibration_tutorial-status-error = Трекер был перемещен
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
onboarding-assignment_tutorial = Как подготовить Slime Трекер перед тем, как надеть его
|
||||
onboarding-assignment_tutorial-first_step = 1. Наклейте стикер с частью тела (если он у вас есть) на трекер по вашему выбору.
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = Стикер
|
||||
onboarding-assignment_tutorial-second_step = 2. Прикрепите ремешок к вашему трекеру, придерживая лицевую сторону ремешка со стороны крючка и петли в следующем положении:
|
||||
onboarding-assignment_tutorial-second_step-continuation = Сторона крючка и петли для удлинителя должна быть в этой ориентации:
|
||||
onboarding-assignment_tutorial-done = Я наклеил стикеры и ремешки!
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Вернуться к вводу данных Wi-Fi
|
||||
@@ -651,6 +708,17 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
onboarding-choose_mounting = Какой метод калибровки монтажа использовать?
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = Ориентация крепления корректирует размещение трекеров на вашем теле.
|
||||
onboarding-choose_mounting-auto_mounting = Автоматическая привязка
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-label = Экспериментальный
|
||||
onboarding-choose_mounting-auto_mounting-description = Это автоматически определит направления монтажа для всех ваших трекеров из 2 поз
|
||||
onboarding-choose_mounting-manual_mounting = Ручная привязка
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-label = Рекомендованный
|
||||
onboarding-choose_mounting-manual_mounting-description = Это позволит вам выбрать направление монтажа вручную для каждого трекера
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
@@ -683,6 +751,21 @@ onboarding-automatic_mounting-put_trackers_on-next = Я включил и над
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = Какой метод калибровки пропорций использовать?
|
||||
# Multiline string
|
||||
onboarding-choose_proportions-description =
|
||||
Пропорции тела используются для определения размеров вашего тела. Они необходимы для расчета местоположения трекеров.
|
||||
Если пропорции вашего тела не соответствуют сохраненным, точность отслеживания будет хуже, и вы заметите такие вещи, как катание на коньках или скольжение, или ваше тело не совсем соответствует вашему аватару.
|
||||
onboarding-choose_proportions-auto_proportions = Автоматическая привязка
|
||||
# Italized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = Рекомендуется
|
||||
onboarding-choose_proportions-auto_proportions-description = Это позволит угадать ваши пропорции, записав образец ваших движений и передав его через алгоритм.
|
||||
onboarding-choose_proportions-manual_proportions = Ручные пропорции
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Для небольших штрихов
|
||||
onboarding-choose_proportions-manual_proportions-description = Это позволит вам настроить пропорции вручную, изменив их напрямую.
|
||||
onboarding-choose_proportions-export = Экспорт пропорций
|
||||
onboarding-choose_proportions-file_type = Файл пропорций тела
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -702,12 +785,30 @@ onboarding-automatic_proportions-prev_step = Предыдущий шаг
|
||||
onboarding-automatic_proportions-put_trackers_on-title = Наденьте ваши трекеры
|
||||
onboarding-automatic_proportions-put_trackers_on-description = Чтобы откалибровать ваши пропорции, мы собираемся использовать трекеры, которые вы только что назначили. Включите все свои трекеры, вы можете увидеть, какие из них какие на рисунке справа.
|
||||
onboarding-automatic_proportions-put_trackers_on-next = Я надел все свои трекеры
|
||||
onboarding-automatic_proportions-requirements-title = Требования
|
||||
# Each line of text is a different list item
|
||||
onboarding-automatic_proportions-requirements-description =
|
||||
У вас есть как минимум достаточно трекеров, чтобы отслеживать ваши ноги (обычно 5 трекеров).
|
||||
У вас есть трекеры и гарнитура.
|
||||
Вы носите трекеры и гарнитуру.
|
||||
Ваши трекеры и гарнитура подключены к серверу SlimeVR.
|
||||
Ваши трекеры и гарнитура правильно работают на сервере SlimeVR.
|
||||
Ваша гарнитура передает данные о местоположении на сервер SlimeVR (обычно это означает, что SteamVR запущен и подключен к SlimeVR с помощью драйвера SlimeVR SteamVR).
|
||||
onboarding-automatic_proportions-requirements-next = Я прочитал требования
|
||||
onboarding-automatic_proportions-start_recording-title = Будьте готовы к движению
|
||||
onboarding-automatic_proportions-start_recording-description = Теперь мы собираемся записать некоторые конкретные позы и движения. Они будут запрошены на следующем экране. Будьте готовы начать, когда кнопка будет нажата!
|
||||
onboarding-automatic_proportions-start_recording-next = Начать запись
|
||||
onboarding-automatic_proportions-recording-title = Запись
|
||||
onboarding-automatic_proportions-recording-description-p0 = Запись в процессе...
|
||||
onboarding-automatic_proportions-recording-description-p1 = Сделайте эти движения:
|
||||
# Each line of text is a different list item
|
||||
onboarding-automatic_proportions-recording-steps =
|
||||
Стоя прямо, покрутите головой по кругу.
|
||||
Наклоните спину вперед и присядьте на корточки. Сидя на корточках, посмотрите налево, затем направо.
|
||||
Поверните верхнюю часть туловища влево (против часовой стрелки), затем наклонитесь к земле.
|
||||
Поверните верхнюю часть туловища вправо (по часовой стрелке), затем наклонитесь к земле.
|
||||
Вращайте бедрами круговыми движениями, как будто вы используете хула-хуп.
|
||||
Если на запись осталось время, вы можете повторять эти действия до тех пор, пока она не будет завершена.
|
||||
onboarding-automatic_proportions-recording-processing = Обработка результата...
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
|
||||
onboarding-automatic_proportions-recording-timer =
|
||||
@@ -729,3 +830,13 @@ onboarding-automatic_proportions-done-description = Калибровка про
|
||||
## Home
|
||||
|
||||
home-no_trackers = Трекеры не обнаружены и не привязаны
|
||||
|
||||
## Status system
|
||||
|
||||
status_system-StatusTrackerReset = Рекомендуется выполнить полный сброс, так как один или несколько трекеров не настроены.
|
||||
status_system-StatusSteamVRDisconnected =
|
||||
{ $type ->
|
||||
[steamvr_feeder] В настоящее время не подключен к приложению SlimeVR Feeder.
|
||||
*[other] В настоящее время не подключен к SteamVR через драйвер SlimeVR.
|
||||
}
|
||||
status_system-StatusTrackerError = В трекере { $trackerName } обнаружена ошибка.
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -11,29 +8,69 @@
|
||||
## Websocket (server) status
|
||||
|
||||
websocket-connecting = กำลังเชื่อมต่อกับเซิร์ฟเวอร์
|
||||
websocket-connection_lost = ขาดการเชื่อมต่อกับเซิร์ฟเวอร์ กำลังลองเชื่อมใหม่
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = มีเวอใหม่พร้อมแล้ว: { $version }
|
||||
version_update-update = อัพเดท
|
||||
version_update-close = ปิด
|
||||
|
||||
## Tips
|
||||
|
||||
tips-do_not_move_heels = ให้แน่ใจว่าเท้าไม่ขยับระหว่างการอัด/บันทึก
|
||||
|
||||
## Body parts
|
||||
|
||||
body_part-HEAD = หัว
|
||||
body_part-NECK = คอ
|
||||
body_part-RIGHT_SHOULDER = ใหล่ขวา
|
||||
body_part-RIGHT_UPPER_ARM = แขนขวาส่วนบน
|
||||
body_part-RIGHT_LOWER_ARM = แขนขวาส่วนล่าง
|
||||
body_part-RIGHT_HAND = มือขวา
|
||||
body_part-RIGHT_UPPER_LEG = น่องขาขวา
|
||||
body_part-RIGHT_LOWER_LEG = ข้อเท้าขวา
|
||||
body_part-RIGHT_FOOT = เท้าขวา
|
||||
body_part-CHEST = หน้าอก
|
||||
body_part-WAIST = เอว
|
||||
body_part-HIP = สะโพก
|
||||
body_part-LEFT_SHOULDER = ใหล่ซ้าย
|
||||
body_part-LEFT_UPPER_ARM = แขนซ้ายส่วนบน
|
||||
body_part-LEFT_LOWER_ARM = แขนซ้ายส่วนล่าง
|
||||
body_part-LEFT_HAND = มือซ้าย
|
||||
body_part-LEFT_UPPER_LEG = น่องขาซ้าย
|
||||
body_part-LEFT_LOWER_LEG = ข้อเท้าซ้าย
|
||||
body_part-LEFT_FOOT = เท้าขวา
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = ไม่มี
|
||||
skeleton_bone-NECK = ความยาวขอ
|
||||
skeleton_bone-UPPER_ARM = ความยาวแขนส่วนบน
|
||||
skeleton_bone-LOWER_ARM = ความยาวแขนส่วนล่าง
|
||||
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = รีเซ็ตสัดส่วนร้างกายทั้งหมด
|
||||
reset-full = รีเซ็ตทั้งหมด
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
serial_detection-open_wifi = เชื่อมต่อ Wi-Fi
|
||||
serial_detection-close = ปิด
|
||||
|
||||
## Navigation bar
|
||||
|
||||
navbar-home = หน้าหลัก
|
||||
navbar-body_proportions = สัดส่วนร่างกาย
|
||||
navbar-onboarding = Setup Wizard
|
||||
navbar-settings = ตั้งค่า
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-recording = กำลังอัดบันทึก
|
||||
|
||||
## Tracking pause
|
||||
|
||||
|
||||
## Widget: Overlay settings
|
||||
@@ -44,45 +81,76 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
|
||||
|
||||
## Widget: Developer settings
|
||||
|
||||
widget-developer_mode = โหมดนักพัฒนา
|
||||
widget-developer_mode-sort_by_name = เรียงด้วยชื่อ
|
||||
widget-developer_mode-more_info = ข้อมูลเพิ่มเติม
|
||||
|
||||
## Widget: IMU Visualizer
|
||||
|
||||
widget-imu_visualizer = การหมุน
|
||||
widget-imu_visualizer-rotation_preview = ดูตัวอย่าง
|
||||
widget-imu_visualizer-rotation_hide = ซ่อน
|
||||
|
||||
## Tracker status
|
||||
|
||||
tracker-status-none = ไม่มีสถานะ
|
||||
tracker-status-busy = กังลังทำงาน
|
||||
tracker-status-error = ข้อผิดผลาด
|
||||
tracker-status-disconnected = หลุดการเชื่อมต่อ
|
||||
tracker-status-ok = OK
|
||||
|
||||
## Tracker status columns
|
||||
|
||||
tracker-table-column-name = ชื่อ
|
||||
tracker-table-column-type = พิมพ์
|
||||
tracker-table-column-battery = แบตเตอรี่
|
||||
tracker-table-column-ping = ความหน่วง
|
||||
tracker-table-column-temperature = อุณหภูมิ °C
|
||||
tracker-table-column-url = URL
|
||||
|
||||
## Tracker rotation
|
||||
|
||||
tracker-rotation-front = หน้า
|
||||
tracker-rotation-left = ซ้าย
|
||||
tracker-rotation-right = ขวา
|
||||
tracker-rotation-back = หลัง
|
||||
|
||||
## Tracker information
|
||||
|
||||
tracker-infos-manufacturer = ผู้ผลิต
|
||||
tracker-infos-version = เวอร์ชั่นของเฟิร์มแวร์
|
||||
tracker-infos-board_type = เมนบอร์ด
|
||||
|
||||
## Tracker settings
|
||||
|
||||
tracker-settings-assignment_section = กำหนด
|
||||
|
||||
## Tracker part card info
|
||||
|
||||
tracker-part_card-no_name = ไม่มีชื่อ
|
||||
|
||||
## Body assignment menu
|
||||
|
||||
|
||||
## Tracker assignment menu
|
||||
|
||||
tracker_selection_menu-neck_warning-done = ฉันเข้าใจในความเสี่ยง
|
||||
tracker_selection_menu-neck_warning-cancel = ยกเลิก
|
||||
|
||||
## Mounting menu
|
||||
|
||||
mounting_selection_menu-close = ปิด
|
||||
|
||||
## Sidebar settings
|
||||
|
||||
settings-sidebar-title = การตั้งค่า
|
||||
|
||||
## SteamVR settings
|
||||
|
||||
|
||||
## Tracker mechanics
|
||||
|
||||
settings-general-tracker_mechanics-filtering-amount = จำนวน
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
@@ -92,42 +160,85 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
|
||||
|
||||
## Interface settings
|
||||
|
||||
settings-general-interface-dev_mode = โหมดนักพัฒนา
|
||||
settings-general-interface-dev_mode-label = โหมดนักพัฒนา
|
||||
settings-general-interface-theme = สีธีม
|
||||
settings-general-interface-lang = เลือกภาษา
|
||||
settings-general-interface-lang-description = เลือกภาษาตั้งต้นที่คุณต้องการใช้
|
||||
settings-general-interface-lang-placeholder = เลือกภาษาที่ใช้
|
||||
|
||||
## Serial settings
|
||||
|
||||
settings-serial-reboot = เริ่มการทำงานใหม่
|
||||
settings-serial-factory_reset = รีเซ็ตเป็นค่าจากโรงงาน
|
||||
settings-serial-factory_reset-warning-ok = ฉันรู้ว่าฉันกําลังทําอะไรอยู่
|
||||
settings-serial-factory_reset-warning-cancel = ยกเลิก
|
||||
settings-serial-auto_dropdown_item = อัตโนมัติ
|
||||
|
||||
## OSC router settings
|
||||
|
||||
settings-osc-router-enable = เปิดใช้งาน
|
||||
settings-osc-router-enable-label = เปิดใช้งาน
|
||||
settings-osc-router-network = พอร์ตเครือข่าย
|
||||
|
||||
## OSC VRChat settings
|
||||
|
||||
settings-osc-vrchat-enable = เปิดใช้งาน
|
||||
settings-osc-vrchat-enable-label = เปิดใช้งาน
|
||||
settings-osc-vrchat-network-trackers-chest = หน้าอก
|
||||
settings-osc-vrchat-network-trackers-hip = สะโพก
|
||||
settings-osc-vrchat-network-trackers-knees = หัวเข่า
|
||||
settings-osc-vrchat-network-trackers-feet = เท้า
|
||||
settings-osc-vrchat-network-trackers-elbows = ข้อศอก
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
settings-osc-vmc-enable = เปิดใช้งาน
|
||||
settings-osc-vmc-enable-label = เปิดใช้งาน
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
onboarding-skip = ข้ามการตั้งค่า
|
||||
onboarding-continue = ดำเนินการต่อ
|
||||
onboarding-previous_step = ขั้นตอนก่อนหน้า
|
||||
onboarding-setup_warning-skip = ข้ามการตั้งค่า
|
||||
|
||||
## Wi-Fi setup
|
||||
|
||||
onboarding-wifi_creds-submit = ส่ง
|
||||
onboarding-wifi_creds-ssid =
|
||||
.label = ชื่อ Wi-Fi
|
||||
.placeholder = ใส่ชื่อ Wi-Fi
|
||||
onboarding-wifi_creds-password =
|
||||
.label = รหัสผ่าน
|
||||
.placeholder = ใส่รหัสผ่าน
|
||||
|
||||
## Mounting setup
|
||||
|
||||
|
||||
## Setup start
|
||||
|
||||
onboarding-home = ยินดีต้อนรับสู่ SlimeVR
|
||||
|
||||
## Enter VR part of setup
|
||||
|
||||
onboarding-enter_vr-ready = ฉันพร้อมแล้ว
|
||||
|
||||
## Setup done
|
||||
|
||||
onboarding-done-title = คุณพร้อมแล้ว!
|
||||
|
||||
## Tracker connection setup
|
||||
|
||||
onboarding-connect_tracker-connection_status-done = เชื่อมต่อกับเซิร์ฟเวอร์แล้ว
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial-calibrate = ฉันได้วาง tracker บนโต๊ะแล้ว
|
||||
onboarding-calibration_tutorial-status-success = เยี่ยม!
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
@@ -140,18 +251,39 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
onboarding-manual_mounting-next = ขั้นตอนถัดไป
|
||||
|
||||
## Tracker automatic mounting setup
|
||||
|
||||
onboarding-automatic_mounting-next = ขั้นตอนถัดไป
|
||||
onboarding-automatic_mounting-prev_step = ขั้นตอนก่อนหน้า
|
||||
onboarding-automatic_mounting-done-restart = ลองอีกครั้ง
|
||||
onboarding-automatic_mounting-preparation-title = จัดเตรียมพร้อม
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
# Italized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = แนะนำ
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
onboarding-manual_proportions-precision = ปรับด้วยความแม่นยํา
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
onboarding-automatic_proportions-title = วัดสัดส่วนร่างกายของคุณ
|
||||
onboarding-automatic_proportions-requirements-title = ต้องการ
|
||||
onboarding-automatic_proportions-requirements-next = ฉันได้อ่านสิ่งที่ต้องการแล้ว
|
||||
onboarding-automatic_proportions-start_recording-next = เริ่มการบันทึก
|
||||
onboarding-automatic_proportions-recording-title = REC
|
||||
onboarding-automatic_proportions-recording-description-p0 = กําลังบันทึก...
|
||||
onboarding-automatic_proportions-recording-description-p1 = ทําการเคลื่อนไหวที่แสดงด้านล่าง:
|
||||
onboarding-automatic_proportions-recording-processing = กำลังประมวลผลผลลัพธ์
|
||||
onboarding-automatic_proportions-verify_results-title = ยืนยันผลลัพธ์
|
||||
onboarding-automatic_proportions-verify_results-description = ตรวจสอบผลลัพธ์ด้านล่างว่าดูถูกต้องหรือไม่?
|
||||
|
||||
## Home
|
||||
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
### SlimeVR complete GUI translations
|
||||
|
||||
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
@@ -13,6 +10,12 @@
|
||||
websocket-connecting = Sunucuya bağlanılıyor
|
||||
websocket-connection_lost = Sunucuyla bağlantı kesildi. Tekrar bağlanılmaya çalışılıyor...
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = Yeni sürüm mevcut: { $version }
|
||||
version_update-update = Güncelle
|
||||
version_update-close = Kapat
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Hangi takipçi hangisi emin değil misin? Takipçilerden birini hareket ettirerek belirleyebilirsin.
|
||||
@@ -30,7 +33,7 @@ body_part-RIGHT_HAND = Sağ el
|
||||
body_part-RIGHT_UPPER_LEG = Sağ uyluk
|
||||
body_part-RIGHT_LOWER_LEG = Sağ ayak bileği
|
||||
body_part-RIGHT_FOOT = Sağ ayak
|
||||
body_part-RIGHT_CONTROLLER = Sağ oyun kolu
|
||||
body_part-UPPER_CHEST = Üst göğüs
|
||||
body_part-CHEST = Göğüs
|
||||
body_part-WAIST = Bel
|
||||
body_part-HIP = Kalça
|
||||
@@ -41,19 +44,21 @@ body_part-LEFT_HAND = Sol el
|
||||
body_part-LEFT_UPPER_LEG = Sol uyluk
|
||||
body_part-LEFT_LOWER_LEG = Sol ayak bileği
|
||||
body_part-LEFT_FOOT = Sol ayak
|
||||
body_part-LEFT_CONTROLLER = Sol oyun kolu
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = Yok
|
||||
skeleton_bone-HEAD = Kafa hizası
|
||||
skeleton_bone-NECK = Boyun Uzunluğu
|
||||
skeleton_bone-CHEST = Göğüs Uzunluğu
|
||||
skeleton_bone-torso_group = Gövde uzunluğu
|
||||
skeleton_bone-UPPER_CHEST = Üst Göğüs Uzunluğu
|
||||
skeleton_bone-CHEST_OFFSET = Göğüs hizası
|
||||
skeleton_bone-CHEST = Göğüs Uzunluğu
|
||||
skeleton_bone-WAIST = Bel Uzunluğu
|
||||
skeleton_bone-HIP = Kalça Uzunluğu
|
||||
skeleton_bone-HIP_OFFSET = Kalça hizası
|
||||
skeleton_bone-HIPS_WIDTH = Kalça Genişliği
|
||||
skeleton_bone-leg_group = Bacak uzunluğu
|
||||
skeleton_bone-UPPER_LEG = Üst Bacak Uzunluğu
|
||||
skeleton_bone-LOWER_LEG = Alt Bacak Uzunluğu
|
||||
skeleton_bone-FOOT_LENGTH = Ayak Uzunluğu
|
||||
@@ -61,32 +66,41 @@ skeleton_bone-FOOT_SHIFT = Ayak hizası
|
||||
skeleton_bone-SKELETON_OFFSET = İskelet hizası
|
||||
skeleton_bone-SHOULDERS_DISTANCE = Omuz Mesafesi
|
||||
skeleton_bone-SHOULDERS_WIDTH = Omuz Genişliği
|
||||
skeleton_bone-arm_group = Kol uzunluğu
|
||||
skeleton_bone-UPPER_ARM = Üst Kol Uzunluğu
|
||||
skeleton_bone-LOWER_ARM = Alt Kol Uzunluğu
|
||||
skeleton_bone-CONTROLLER_Y = Oyun kolu uzaklığı Y
|
||||
skeleton_bone-CONTROLLER_Z = Oyun kolu uzaklığı Z
|
||||
skeleton_bone-ELBOW_OFFSET = Dirsek hizası
|
||||
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = Tüm oranları sıfırla
|
||||
reset-full = Sıfırlama
|
||||
reset-quick = Hızlı Sıfırlama
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
serial_detection-new_device-p0 = Yeni seri cihaz algılandı!
|
||||
serial_detection-new_device-p1 = Wi-Fi bilgilerinizi girin!
|
||||
serial_detection-new_device-p2 = Lütfen onunla ne yapmak istediğinizi seçin
|
||||
serial_detection-open_wifi = Wi-Fi'ye bağlan
|
||||
serial_detection-open_serial = Seri Konsolu Aç
|
||||
serial_detection-submit = Gönder!
|
||||
serial_detection-close = Kapat
|
||||
|
||||
## Navigation bar
|
||||
|
||||
navbar-home = Ana Menü
|
||||
navbar-body_proportions = Vücut Oranları
|
||||
navbar-onboarding = Kurulum Sihirbazı
|
||||
navbar-settings = Ayarlar
|
||||
|
||||
## Bounding volume hierarchy recording
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = BVH Kaydet
|
||||
bvh-recording = Kaydediliyor
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = Takibi duraklat
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
@@ -96,15 +110,36 @@ navbar-settings = Ayarlar
|
||||
|
||||
## Widget: Developer settings
|
||||
|
||||
widget-developer_mode = Geliştirici Modu
|
||||
widget-developer_mode-high_contrast = Yüksek kontrast
|
||||
widget-developer_mode-precise_rotation = Hassas dönüş
|
||||
widget-developer_mode-sort_by_name = Ada göre sırala
|
||||
widget-developer_mode-more_info = Daha fazla bilgi
|
||||
|
||||
## Widget: IMU Visualizer
|
||||
|
||||
widget-imu_visualizer = Rotasyon
|
||||
widget-imu_visualizer-rotation_preview = Önizle
|
||||
widget-imu_visualizer-rotation_hide = Gizle
|
||||
|
||||
## Tracker status
|
||||
|
||||
tracker-status-none = Durum Yok
|
||||
tracker-status-busy = Meşgul
|
||||
tracker-status-error = Hata
|
||||
tracker-status-disconnected = Bağlantı kesildi
|
||||
tracker-status-ok = İYİ
|
||||
|
||||
## Tracker status columns
|
||||
|
||||
tracker-table-column-name = İsim
|
||||
tracker-table-column-type = Tür
|
||||
tracker-table-column-battery = Pil
|
||||
tracker-table-column-tps = TPS
|
||||
tracker-table-column-temperature = Sıcaklık °C
|
||||
tracker-table-column-rotation = Rotasyon X/Y/Z
|
||||
tracker-table-column-position = Pozisyon X/Y/Z
|
||||
tracker-table-column-url = URL
|
||||
|
||||
## Tracker rotation
|
||||
|
||||
@@ -118,34 +153,95 @@ tracker-rotation-back = Arka
|
||||
tracker-infos-manufacturer = Üretici
|
||||
tracker-infos-display_name = Görünen Ad
|
||||
tracker-infos-custom_name = Özel Ad
|
||||
tracker-infos-url = Takipçi URL'si
|
||||
tracker-infos-version = Yazılım Sürümü
|
||||
tracker-infos-hardware_rev = Donanım Revizyonu
|
||||
tracker-infos-hardware_identifier = Donanım Kimliği
|
||||
tracker-infos-imu = IMU Sensör
|
||||
tracker-infos-board_type = Ana kart
|
||||
|
||||
## Tracker settings
|
||||
|
||||
tracker-settings-back = Takipçi listesine geri dön
|
||||
tracker-settings-title = Takipçi ayarları
|
||||
tracker-settings-assignment_section-description = Tracker'in vücudun hangi kısmına atandığı.
|
||||
# The .<name> means it's an attribute and it's related to the top key.
|
||||
# In this case that is the settings for the assignment section.
|
||||
tracker-settings-name_section = Takipçi adı
|
||||
tracker-settings-name_section-placeholder = NightyBeast'in sol bacağı
|
||||
|
||||
## Tracker part card info
|
||||
|
||||
tracker-part_card-no_name = İsimsiz
|
||||
tracker-part_card-unassigned = Atanmamış
|
||||
|
||||
## Body assignment menu
|
||||
|
||||
body_assignment_menu = Bu takipçinin nerede olmasını istiyorsunuz?
|
||||
body_assignment_menu-manage_trackers = Tüm takipçileri yönet
|
||||
|
||||
## Tracker assignment menu
|
||||
|
||||
# A -translation_key (with a dash in the front) means that it's a label.
|
||||
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
|
||||
#
|
||||
# We are using it here because english doesn't require changing the text in each case but
|
||||
# maybe your language does.
|
||||
-tracker_selection-part = hangi takipçiyi atayacaksınız?
|
||||
tracker_selection_menu-HEAD = Başınıza { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_SHOULDER = Sağ omuzunuza { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_UPPER_ARM = Sağ üst kolunuza { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_LOWER_ARM = Sağ alt kolunuza { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_HAND = Sağ elinize { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_UPPER_LEG = Say kalçanıza { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = Sağ ayak bileğinize { -tracker_selection-part }
|
||||
tracker_selection_menu-RIGHT_FOOT = Sağ ayağınıza { -tracker_selection-part }
|
||||
tracker_selection_menu-UPPER_CHEST = Üst göğüsünüze { -tracker_selection-part }
|
||||
tracker_selection_menu-unassigned = Atanmamış takipçiler
|
||||
tracker_selection_menu-assigned = Atanan takipçiler
|
||||
tracker_selection_menu-neck_warning-done = Riskleri anlıyorum
|
||||
tracker_selection_menu-neck_warning-cancel = İptal
|
||||
|
||||
## Mounting menu
|
||||
|
||||
mounting_selection_menu = Bu takipçinin nerede olmasını istiyorsunuz?
|
||||
mounting_selection_menu-close = Kapat
|
||||
|
||||
## Sidebar settings
|
||||
|
||||
settings-sidebar-title = Ayarlar
|
||||
settings-sidebar-general = Genel
|
||||
settings-sidebar-interface = Arayüz
|
||||
settings-sidebar-osc_router = OSC yönlendirici
|
||||
settings-sidebar-osc_trackers = VRChat OSC Takipçileri
|
||||
settings-sidebar-serial = Seri konsol
|
||||
|
||||
## SteamVR settings
|
||||
|
||||
settings-general-steamvr = SteamVR
|
||||
settings-general-steamvr-subtitle = SteamVR takipçileri
|
||||
settings-general-steamvr-trackers-waist = Bel
|
||||
settings-general-steamvr-trackers-chest = Göğüs
|
||||
settings-general-steamvr-trackers-feet = Ayaklar
|
||||
settings-general-steamvr-trackers-knees = Dizler
|
||||
settings-general-steamvr-trackers-elbows = Dirsekler
|
||||
settings-general-steamvr-trackers-hands = Eller
|
||||
|
||||
## Tracker mechanics
|
||||
|
||||
settings-general-tracker_mechanics-filtering-amount = Miktar
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
settings-general-fk_settings-leg_fk = Bacak takibi
|
||||
settings-general-fk_settings-arm_fk = Kol takibi
|
||||
settings-general-fk_settings-skeleton_settings = İskelet ayarları
|
||||
settings-general-fk_settings-skeleton_settings-description = İskelet ayarlarını açın veya kapatın. Bunları açık bırakmanız önerilir.
|
||||
settings-general-fk_settings-skeleton_settings-extended_spine = Uzatılmış omurga
|
||||
settings-general-fk_settings-skeleton_settings-extended_pelvis = Uzatılmış pelvis
|
||||
settings-general-fk_settings-skeleton_settings-extended_knees = Uzatılmış diz
|
||||
settings-general-fk_settings-vive_emulation-title = Vive emülasyonu
|
||||
settings-general-fk_settings-vive_emulation-label = Vive emülasyonunu etkinleştir
|
||||
|
||||
## Gesture control settings (tracker tapping)
|
||||
|
||||
@@ -172,6 +268,9 @@ settings-osc-vrchat-enable = Etkinleştir
|
||||
settings-osc-vrchat-enable-label = Etkinleştir
|
||||
settings-osc-vrchat-network-address = Ağ adresi
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
|
||||
@@ -193,15 +292,30 @@ settings-osc-vrchat-network-address = Ağ adresi
|
||||
## Tracker connection setup
|
||||
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
|
||||
## Tracker assignment warnings
|
||||
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
|
||||
## Tracker automatic mounting setup
|
||||
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
|
||||
@@ -210,3 +324,6 @@ settings-osc-vrchat-network-address = Ağ adresi
|
||||
|
||||
## Home
|
||||
|
||||
|
||||
## Status system
|
||||
|
||||
|
||||
718
gui/public/i18n/uk/translation.ftl
Normal file
718
gui/public/i18n/uk/translation.ftl
Normal file
@@ -0,0 +1,718 @@
|
||||
# Please developers (not translators) don't reuse a key inside another key
|
||||
# or concat text with a translation string in the code, use the appropriate
|
||||
# features like variables and selectors in each appropriate case!
|
||||
# And also comment the string if it's something not easy to translate, so you help
|
||||
# translators on what it means
|
||||
|
||||
|
||||
## Websocket (server) status
|
||||
|
||||
websocket-connecting = Підключення до сервера
|
||||
websocket-connection_lost = З'єднання з сервером втрачено. Повторне підключення...
|
||||
|
||||
## Update notification
|
||||
|
||||
version_update-title = Доступна нова версія: { $version }
|
||||
version_update-description = Натискання "{ version_update-update }" почнеться завантаження SlimeVR installer.
|
||||
version_update-update = Оновлення
|
||||
version_update-close = Закрити
|
||||
|
||||
## Tips
|
||||
|
||||
tips-find_tracker = Не знаєте який трекер вибирати? Потрясіть трекер і він підсвітить відповідний пункт.
|
||||
tips-do_not_move_heels = Переконайтеся, що не рухаєте п'ятами під час запису!
|
||||
tips-file_select = Перетягніть файли для використання або <u>знайдіть</u>.
|
||||
tips-tap_setup = Ви можете повільно постукати 2 рази по трекеру, щоб вибрати його, замість того, щоб вибирати його з меню.
|
||||
|
||||
## Body parts
|
||||
|
||||
body_part-NONE = Не призначено
|
||||
body_part-HEAD = Голова
|
||||
body_part-NECK = Шия
|
||||
body_part-RIGHT_SHOULDER = Праве плече
|
||||
body_part-RIGHT_UPPER_ARM = Права верхня частина руки
|
||||
body_part-RIGHT_LOWER_ARM = Права нижня частина руки
|
||||
body_part-RIGHT_HAND = Права рука
|
||||
body_part-RIGHT_UPPER_LEG = Праве стегно
|
||||
body_part-RIGHT_LOWER_LEG = Права щиколотка
|
||||
body_part-RIGHT_FOOT = Права нога
|
||||
body_part-UPPER_CHEST = Верхня частина грудей
|
||||
body_part-CHEST = Груди
|
||||
body_part-WAIST = Талія
|
||||
body_part-HIP = Стегно
|
||||
body_part-LEFT_SHOULDER = Ліве плече
|
||||
body_part-LEFT_UPPER_ARM = Ліва верхня частина руки
|
||||
body_part-LEFT_LOWER_ARM = Ліва нижня частина руки
|
||||
body_part-LEFT_HAND = Ліва рука
|
||||
body_part-LEFT_UPPER_LEG = Ліве стегно
|
||||
body_part-LEFT_LOWER_LEG = Ліва щиколотка
|
||||
body_part-LEFT_FOOT = Ліва нога
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = Нічого
|
||||
skeleton_bone-HEAD = Зсув голови
|
||||
skeleton_bone-NECK = Довжина шиї
|
||||
skeleton_bone-torso_group = Довжина тулуба
|
||||
skeleton_bone-UPPER_CHEST = Довжина верхньої частини грудей
|
||||
skeleton_bone-CHEST_OFFSET = Зміщення грудної клітини
|
||||
skeleton_bone-CHEST = Довжина грудей
|
||||
skeleton_bone-WAIST = Довжина талії
|
||||
skeleton_bone-HIP = Довжина стегна
|
||||
skeleton_bone-HIP_OFFSET = Зміщення стегна
|
||||
skeleton_bone-HIPS_WIDTH = Ширина стегон
|
||||
skeleton_bone-leg_group = Довжина ніг
|
||||
skeleton_bone-UPPER_LEG = Довжина верхньої частини ноги
|
||||
skeleton_bone-LOWER_LEG = Довжина гомілки
|
||||
skeleton_bone-FOOT_LENGTH = Довжина стопи
|
||||
skeleton_bone-FOOT_SHIFT = Зміщення стопи
|
||||
skeleton_bone-SKELETON_OFFSET = Зміщення скелета
|
||||
skeleton_bone-SHOULDERS_DISTANCE = Відстань між плечима
|
||||
skeleton_bone-SHOULDERS_WIDTH = Ширина плечей
|
||||
skeleton_bone-arm_group = Довжина руки
|
||||
skeleton_bone-UPPER_ARM = Довжина верхньої частини руки
|
||||
skeleton_bone-LOWER_ARM = Довжина нижньої частини руки
|
||||
skeleton_bone-HAND_Y = Відстань рук Y
|
||||
skeleton_bone-HAND_Z = Відстань руки Z
|
||||
skeleton_bone-ELBOW_OFFSET = Зміщення ліктя
|
||||
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = Скинути всі пропорції
|
||||
reset-full = Повне скидання
|
||||
reset-mounting = Скинути закріплення
|
||||
reset-yaw = Скинути нахил
|
||||
|
||||
## Serial detection stuff
|
||||
|
||||
serial_detection-new_device-p0 = Виявлено новий послідовний пристрій!
|
||||
serial_detection-new_device-p1 = Введіть дані вашого Wi-Fi!
|
||||
serial_detection-new_device-p2 = Будь ласка, виберіть, що ви хочете з ним зробити
|
||||
serial_detection-open_wifi = Підключити до Wi-Fi
|
||||
serial_detection-open_serial = Відкрити послідовну консоль
|
||||
serial_detection-submit = Підтвердити!
|
||||
serial_detection-close = Закрити
|
||||
|
||||
## Navigation bar
|
||||
|
||||
navbar-home = Домашня сторінка
|
||||
navbar-body_proportions = Пропорції тіла
|
||||
navbar-trackers_assign = Призначення трекера
|
||||
navbar-mounting = Калібрування закріплення
|
||||
navbar-onboarding = Майстер налаштування
|
||||
navbar-settings = Параметри
|
||||
|
||||
## Biovision hierarchy recording
|
||||
|
||||
bvh-start_recording = Запис BVH
|
||||
bvh-recording = Запис...
|
||||
|
||||
## Tracking pause
|
||||
|
||||
tracking-unpaused = Призупинити трекінг
|
||||
tracking-paused = Продовжити трекінг
|
||||
|
||||
## Widget: Overlay settings
|
||||
|
||||
widget-overlay = Накладання
|
||||
widget-overlay-is_visible_label = Показати накладання у SteamVR
|
||||
widget-overlay-is_mirrored_label = Відображення накладання як дзеркала
|
||||
|
||||
## Widget: Drift compensation
|
||||
|
||||
widget-drift_compensation-clear = Очистити компенсацію дрейфу
|
||||
|
||||
## Widget: Developer settings
|
||||
|
||||
widget-developer_mode = Режим розробника
|
||||
widget-developer_mode-high_contrast = Висока контрастність
|
||||
widget-developer_mode-precise_rotation = Точне обертання
|
||||
widget-developer_mode-fast_data_feed = Швидка подача даних
|
||||
widget-developer_mode-filter_slimes_and_hmd = Фільтрація слаймів і шолому
|
||||
widget-developer_mode-sort_by_name = Сортування за назвою
|
||||
widget-developer_mode-raw_slime_rotation = Необроблене обертання
|
||||
widget-developer_mode-more_info = Детальніше
|
||||
|
||||
## Widget: IMU Visualizer
|
||||
|
||||
widget-imu_visualizer = Обертання
|
||||
widget-imu_visualizer-rotation_raw = Необроблене
|
||||
widget-imu_visualizer-rotation_preview = Попередній перегляд
|
||||
widget-imu_visualizer-rotation_hide = Приховати
|
||||
|
||||
## Tracker status
|
||||
|
||||
tracker-status-none = Немає статусу
|
||||
tracker-status-busy = Зайнятий
|
||||
tracker-status-error = Помилка
|
||||
tracker-status-disconnected = Відключено
|
||||
tracker-status-occluded = Закрито
|
||||
tracker-status-ok = OK
|
||||
|
||||
## Tracker status columns
|
||||
|
||||
tracker-table-column-name = Ім'я
|
||||
tracker-table-column-type = Тип
|
||||
tracker-table-column-battery = Батарея
|
||||
tracker-table-column-ping = Пінг
|
||||
tracker-table-column-tps = TPS
|
||||
tracker-table-column-temperature = Темп. °C
|
||||
tracker-table-column-linear-acceleration = Прискорення X/Y/Z
|
||||
tracker-table-column-rotation = Обертання X/Y/Z
|
||||
tracker-table-column-position = Позиція X/Y/Z
|
||||
tracker-table-column-url = URL
|
||||
|
||||
## Tracker rotation
|
||||
|
||||
tracker-rotation-front = Спереду
|
||||
tracker-rotation-left = Зліва
|
||||
tracker-rotation-right = Справа
|
||||
tracker-rotation-back = Ззаду
|
||||
|
||||
## Tracker information
|
||||
|
||||
tracker-infos-manufacturer = Виробник
|
||||
tracker-infos-display_name = Відображуване ім'я
|
||||
tracker-infos-custom_name = Персональне ім'я
|
||||
tracker-infos-url = URL трекера
|
||||
tracker-infos-version = Версія прошивки
|
||||
tracker-infos-hardware_rev = Ревізія обладнання
|
||||
tracker-infos-hardware_identifier = Ідентифікатор обладнання
|
||||
tracker-infos-imu = IMU Сенсор
|
||||
tracker-infos-board_type = Основна плата
|
||||
|
||||
## Tracker settings
|
||||
|
||||
tracker-settings-back = Повернутися до списку трекерів
|
||||
tracker-settings-title = Налаштування трекеру
|
||||
tracker-settings-assignment_section = Призначення
|
||||
tracker-settings-assignment_section-description = До якої частини тіла призначенний трекер.
|
||||
tracker-settings-assignment_section-edit = Редагування призначення
|
||||
tracker-settings-mounting_section = Позиція закріпу
|
||||
tracker-settings-mounting_section-description = Де закріплено трекер?
|
||||
tracker-settings-mounting_section-edit = Редагувати закріплення
|
||||
tracker-settings-drift_compensation_section = Дозволити компенсацію дрейфу
|
||||
tracker-settings-drift_compensation_section-description = Чи повинен цей трекер компенсувати свій дрейф, коли включена компенсація дрейфу?
|
||||
tracker-settings-drift_compensation_section-edit = Дозволити компенсацію дрейфу
|
||||
# The .<name> means it's an attribute and it's related to the top key.
|
||||
# In this case that is the settings for the assignment section.
|
||||
tracker-settings-name_section = Ім'я трекера
|
||||
tracker-settings-name_section-description = Дайте йому миле прізвисько °^°
|
||||
tracker-settings-name_section-placeholder = Ліва нога NightyBeast
|
||||
|
||||
## Tracker part card info
|
||||
|
||||
tracker-part_card-no_name = Немає імені
|
||||
tracker-part_card-unassigned = Непризначений
|
||||
|
||||
## Body assignment menu
|
||||
|
||||
body_assignment_menu = Де ви хочете, щоб був цей трекер?
|
||||
body_assignment_menu-description = Виберіть місце, куди потрібно призначити цей трекер. Крім того, ви можете керувати всіма трекерами одночасно, а не по одному.
|
||||
body_assignment_menu-show_advanced_locations = Відображення розширених точок розташувань
|
||||
body_assignment_menu-manage_trackers = Керування всіма трекерами
|
||||
body_assignment_menu-unassign_tracker = Відв'язати трекер
|
||||
|
||||
## Tracker assignment menu
|
||||
|
||||
# A -translation_key (with a dash in the front) means that it's a label.
|
||||
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
|
||||
#
|
||||
# We are using it here because english doesn't require changing the text in each case but
|
||||
# maybe your language does.
|
||||
-tracker_selection-part = Який трекер призначити к
|
||||
tracker_selection_menu-NONE = Який трекер ви хочете відв'язати?
|
||||
tracker_selection_menu-HEAD = { -tracker_selection-part } голові?
|
||||
tracker_selection_menu-NECK = { -tracker_selection-part } шиї?
|
||||
tracker_selection_menu-RIGHT_SHOULDER = { -tracker_selection-part } правому плечу?
|
||||
tracker_selection_menu-RIGHT_UPPER_ARM = { -tracker_selection-part } правій верхній частині руці?
|
||||
tracker_selection_menu-RIGHT_LOWER_ARM = { -tracker_selection-part } правій нижній частині руці?
|
||||
tracker_selection_menu-RIGHT_HAND = { -tracker_selection-part } правій руці?
|
||||
tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } правому стегну?
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } правій щиколотці?
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } правій ступні?
|
||||
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } правому контролеру?
|
||||
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } верхня частина грудей?
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part } грудям?
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part } талії?
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part } стегну?
|
||||
tracker_selection_menu-LEFT_SHOULDER = { -tracker_selection-part } Лівому плечу?
|
||||
tracker_selection_menu-LEFT_UPPER_ARM = { -tracker_selection-part } лівій верхній частині руки?
|
||||
tracker_selection_menu-LEFT_LOWER_ARM = { -tracker_selection-part } лівій нижній частині руці?
|
||||
tracker_selection_menu-LEFT_HAND = { -tracker_selection-part } лівій руці?
|
||||
tracker_selection_menu-LEFT_UPPER_LEG = { -tracker_selection-part } лівому стегну?
|
||||
tracker_selection_menu-LEFT_LOWER_LEG = { -tracker_selection-part } лівій щиколотці
|
||||
tracker_selection_menu-LEFT_FOOT = { -tracker_selection-part } лівій ступні?
|
||||
tracker_selection_menu-LEFT_CONTROLLER = { -tracker_selection-part } лівому контролеру?
|
||||
tracker_selection_menu-unassigned = Непризначені трекери
|
||||
tracker_selection_menu-assigned = Призначені трекери
|
||||
tracker_selection_menu-dont_assign = Відв'язати
|
||||
# This line cares about multilines.
|
||||
# <b>text</b> means that the text should be bold.
|
||||
tracker_selection_menu-neck_warning =
|
||||
<b>Попередження:</b> Трекер шиї може бути смертельно небезпечним, якщо його регулювати занадто щільно,
|
||||
Ремінь може скоротити кровообіг до вашої голови!
|
||||
tracker_selection_menu-neck_warning-done = Я розумію ризики
|
||||
tracker_selection_menu-neck_warning-cancel = Скасувати
|
||||
|
||||
## Mounting menu
|
||||
|
||||
mounting_selection_menu = Де ви хочете, щоб був цей трекер?
|
||||
mounting_selection_menu-close = Закрити
|
||||
|
||||
## Sidebar settings
|
||||
|
||||
settings-sidebar-title = Параметри
|
||||
settings-sidebar-general = Загальні
|
||||
settings-sidebar-tracker_mechanics = Механіки трекера
|
||||
settings-sidebar-fk_settings = Налаштування відстеження
|
||||
settings-sidebar-gesture_control = Управління жестами
|
||||
settings-sidebar-interface = Інтерфейс
|
||||
settings-sidebar-osc_router = OSC роутер
|
||||
settings-sidebar-osc_trackers = VRChat OSC трекери
|
||||
settings-sidebar-utils = Утиліти
|
||||
settings-sidebar-serial = Послідовна консоль
|
||||
|
||||
## SteamVR settings
|
||||
|
||||
settings-general-steamvr = SteamVR
|
||||
settings-general-steamvr-subtitle = SteamVR трекери
|
||||
# Not all translation keys support multiline, only the ones that specify it will actually
|
||||
# split it in lines (that also means you can split in lines however you want in those).
|
||||
# The first spaces (not tabs) for indentation will be ignored, just to make the file look nice when writing.
|
||||
# This one is one of this cases that cares about multilines
|
||||
settings-general-steamvr-description =
|
||||
Увімкніть або вимкніть певні SteamVR трекери.
|
||||
Корисно для ігор або програм, які підтримують лише певні трекери.
|
||||
settings-general-steamvr-trackers-waist = Талія
|
||||
settings-general-steamvr-trackers-chest = Груди
|
||||
settings-general-steamvr-trackers-feet = Ступні
|
||||
settings-general-steamvr-trackers-knees = Коліна
|
||||
settings-general-steamvr-trackers-elbows = Лікті
|
||||
settings-general-steamvr-trackers-hands = Руки
|
||||
|
||||
## Tracker mechanics
|
||||
|
||||
settings-general-tracker_mechanics = Механіки трекера
|
||||
settings-general-tracker_mechanics-filtering = Фільтрація
|
||||
# This also cares about multilines
|
||||
settings-general-tracker_mechanics-filtering-description =
|
||||
Виберіть тип фільтрації для своїх трекерів.
|
||||
Передбачення передбачає рух, а згладжування згладжує рух.
|
||||
settings-general-tracker_mechanics-filtering-type = Тип фільтрації
|
||||
settings-general-tracker_mechanics-filtering-type-none = Без фільтрації
|
||||
settings-general-tracker_mechanics-filtering-type-none-description = Використовуйте обертання як є. Ніякої фільтрації не зробить.
|
||||
settings-general-tracker_mechanics-filtering-type-smoothing = Згладжування
|
||||
settings-general-tracker_mechanics-filtering-type-smoothing-description = Згладжує рухи, але додає деяку затримку.
|
||||
settings-general-tracker_mechanics-filtering-type-prediction = Передбачення
|
||||
settings-general-tracker_mechanics-filtering-type-prediction-description = Зменшує затримку і робить рухи більш швидкими, але може посилити тремтіння.
|
||||
settings-general-tracker_mechanics-filtering-amount = Кількість
|
||||
settings-general-tracker_mechanics-drift_compensation = Компенсація дрейфу
|
||||
# This cares about multilines
|
||||
settings-general-tracker_mechanics-drift_compensation-description =
|
||||
Компенсує дрейф нахилу IMU, застосовуючи зворотне обертання.
|
||||
Змініть суму компенсації та до того, скільки скидань враховано.
|
||||
settings-general-tracker_mechanics-drift_compensation-enabled-label = Компенсація дрейфу
|
||||
settings-general-tracker_mechanics-drift_compensation-amount-label = Сума компенсації
|
||||
settings-general-tracker_mechanics-drift_compensation-max_resets-label = Використання до x останніх скидань
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
settings-general-fk_settings = Налаштування відстеження
|
||||
# Floor clip:
|
||||
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
|
||||
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
|
||||
settings-general-fk_settings-leg_tweak-floor_clip = Прив'язка до підлоги
|
||||
# Skating correction:
|
||||
# why the name - without this enabled the feet will often slide across the ground as if your skating across the ground,
|
||||
# since this largely prevents this it corrects for it hence skating correction (note this may be renamed to sliding correction)
|
||||
# definition - Guesses when each foot is in contact with the ground and uses that information to improve tracking
|
||||
settings-general-fk_settings-leg_tweak-skating_correction = корекція ковзання
|
||||
settings-general-fk_settings-leg_tweak-toe_snap = корекція пальців ноги
|
||||
settings-general-fk_settings-leg_tweak-foot_plant = корекція ступні
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-amount = Сила корекції ковзання
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-description = Корекція ковзання коригує катання на ковзанах, але може знизити точність певних моделей руху. Увімкнувши це, обов'язково повністю скиньте та відкалібруйте у грі.
|
||||
settings-general-fk_settings-leg_tweak-floor_clip-description = Прив'язка до підлоги може зменшити або навіть прибрати проходження через підлогу. Коли вмикаєте, обов'язково зробіть повне скидання і перекалібровку у грі
|
||||
settings-general-fk_settings-leg_tweak-toe_snap-description = Корекція пальців ноги намагається вгадати обертання ваших ступень, якщо трекери для них не використовуються
|
||||
settings-general-fk_settings-leg_tweak-foot_plant-description = Корекція ступні повертає ваші ступні так, щоб вони були паралельні землі при контакті
|
||||
settings-general-fk_settings-leg_fk = Трекінг ноги
|
||||
settings-general-fk_settings-arm_fk = Трекінг руки
|
||||
settings-general-fk_settings-arm_fk-description = Намагатися відстежувати руки за допомогою шолома, навіть якщо є інформація о позиції руки
|
||||
settings-general-fk_settings-arm_fk-force_arms = Відстеження рук з шолома
|
||||
settings-general-fk_settings-skeleton_settings = Налаштування скелета
|
||||
settings-general-fk_settings-skeleton_settings-description = Увімкніть або вимкніть налаштування скелета. Рекомендується залишити їх увімкненими.
|
||||
settings-general-fk_settings-skeleton_settings-extended_spine = Подовжений хребет
|
||||
settings-general-fk_settings-skeleton_settings-extended_pelvis = Розширений таз
|
||||
settings-general-fk_settings-skeleton_settings-extended_knees = Подовжене коліно
|
||||
settings-general-fk_settings-vive_emulation-title = Емуляція Vive
|
||||
settings-general-fk_settings-vive_emulation-description = Емуляція проблем з трекером талії, які є у трекерів Vive. Це жарт і погіршує відстеження.
|
||||
settings-general-fk_settings-vive_emulation-label = Увімкнути емуляцію Vive
|
||||
|
||||
## Gesture control settings (tracker tapping)
|
||||
|
||||
settings-general-gesture_control = Управління жестами
|
||||
settings-general-gesture_control-subtitle = Скидання на основі дотику
|
||||
settings-general-gesture_control-description = Дозволяє запускати скидання, торкнувшись трекера. Трекер найвищий на вашому тулубі використовується для скидання нахилу, трекер найвищий на лівій нозі використовується для повного скидання, а трекер найвищий на правій нозі використовується для скидання закріплення. Слід зазначити, що дотики повинні відбутися протягом 0,6 секунди для реєстрації.
|
||||
# This is a unit: 3 taps, 2 taps, 1 tap
|
||||
# $amount (Number) - Amount of taps (touches to the tracker's case)
|
||||
settings-general-gesture_control-taps =
|
||||
{ $amount ->
|
||||
[one] 1 дотик
|
||||
[few] 2 дотика
|
||||
[many] { $amount } дотиків
|
||||
*[other] { $amount } дотиків
|
||||
}
|
||||
settings-general-gesture_control-yawResetEnabled = Увімкнути дотик, щоб скинути нахил
|
||||
settings-general-gesture_control-yawResetDelay = Затримка скидання нахилу
|
||||
settings-general-gesture_control-yawResetTaps = Дотики для скидання нахилу
|
||||
settings-general-gesture_control-fullResetEnabled = Увімкнути дотик для повного скидання
|
||||
settings-general-gesture_control-fullResetDelay = Затримка повного скидання
|
||||
settings-general-gesture_control-fullResetTaps = Дотики для повного скидання
|
||||
settings-general-gesture_control-mountingResetEnabled = Увімкнути дотик для скидання прив'язки
|
||||
settings-general-gesture_control-mountingResetDelay = Затримка скидання прив'язки
|
||||
settings-general-gesture_control-mountingResetTaps = Дотики для скидання прив'язки
|
||||
|
||||
## Interface settings
|
||||
|
||||
settings-general-interface = Інтерфейс
|
||||
settings-general-interface-dev_mode = Режим розробника
|
||||
settings-general-interface-dev_mode-description = Цей режим може бути корисним, якщо вам потрібні поглиблені дані або для взаємодії з підключеними трекерами на більш просунутому рівні.
|
||||
settings-general-interface-dev_mode-label = Режим розробника
|
||||
settings-general-interface-serial_detection = Виявлення послідовного пристрою
|
||||
settings-general-interface-serial_detection-description = Цей параметр відображатиме спливаюче вікно кожного разу, коли ви підключаєте новий послідовний пристрій, який може бути трекером. Це допомагає покращити процес налаштування трекера.
|
||||
settings-general-interface-serial_detection-label = Виявлення послідовного пристрою
|
||||
settings-general-interface-feedback_sound = Звук зворотного зв'язку
|
||||
settings-general-interface-feedback_sound-description = Ця опція відтворюватиме звуковий сигнал при спрацьовуванні скидання
|
||||
settings-general-interface-feedback_sound-label = Звук зворотного зв'язку
|
||||
settings-general-interface-feedback_sound-volume = Гучність звуку зворотного зв'язку
|
||||
settings-general-interface-theme = Варіація оформлення
|
||||
settings-general-interface-lang = Виберіть мову
|
||||
settings-general-interface-lang-description = Змініть мову за замовчуванням, яку ви хочете використовувати.
|
||||
settings-general-interface-lang-placeholder = Виберіть мову для використання
|
||||
|
||||
## Serial settings
|
||||
|
||||
settings-serial = Послідовна консоль
|
||||
# This cares about multilines
|
||||
settings-serial-description =
|
||||
Це інформаційна стрічка для послідовного зв'язку.
|
||||
Може бути корисним, якщо вам потрібно знати, що прошивка не працює.
|
||||
settings-serial-connection_lost = Підключення до послідовного пристрою втрачене, повторне підключення...
|
||||
settings-serial-reboot = Перезавантажити
|
||||
settings-serial-factory_reset = Скидання до заводських налаштувань
|
||||
# This cares about multilines
|
||||
# <b>text</b> means that the text should be bold
|
||||
settings-serial-factory_reset-warning =
|
||||
<b>Попередження:</b> Це скине трекер до заводських налаштувань.
|
||||
Це означає, що Wi-Fi та налаштування калібрування <b>будуть втрачені!</b>
|
||||
settings-serial-factory_reset-warning-ok = Я знаю, що роблю
|
||||
settings-serial-factory_reset-warning-cancel = Скасувати
|
||||
settings-serial-get_infos = Отримати інформацію
|
||||
settings-serial-serial_select = Вибір послідовного порту
|
||||
settings-serial-auto_dropdown_item = Автоматично
|
||||
|
||||
## OSC router settings
|
||||
|
||||
settings-osc-router = OSC роутер
|
||||
# This cares about multilines
|
||||
settings-osc-router-description =
|
||||
Пересилання повідомлень OSC з іншої програми.
|
||||
Корисно для використання іншої програми OSC з VRChat, наприклад.
|
||||
settings-osc-router-enable = Увімкнути
|
||||
settings-osc-router-enable-description = Увімкнути пересилання повідомлень.
|
||||
settings-osc-router-enable-label = Увімкнути
|
||||
settings-osc-router-network = Мережеві порти
|
||||
# This cares about multilines
|
||||
settings-osc-router-network-description =
|
||||
Встановіть порти для прослуховування і відправки даних.
|
||||
Вони можуть бути такими ж, як і інші порти, що використовуються на сервері SlimeVR.
|
||||
settings-osc-router-network-port_in =
|
||||
.label = Вхідний Порт
|
||||
.placeholder = Вхідний Порт (зазвичай: 9002)
|
||||
settings-osc-router-network-port_out =
|
||||
.label = Вихідний Порт
|
||||
.placeholder = Вихідний Порт (зазвичай: 9000)
|
||||
settings-osc-router-network-address = Мережева адреса
|
||||
settings-osc-router-network-address-description = Укажіть адресу для надсилання даних за адресою.
|
||||
settings-osc-router-network-address-placeholder = IPV4-адреса
|
||||
|
||||
## OSC VRChat settings
|
||||
|
||||
settings-osc-vrchat = VRChat OSC трекери
|
||||
# This cares about multilines
|
||||
settings-osc-vrchat-description =
|
||||
Змініть специфічні для VRChat налаштування для отримання даних шолому та надсилання
|
||||
даних трекерів для FBT без SteamVR (наприклад, автономний Quest).
|
||||
settings-osc-vrchat-enable = Увімкнути
|
||||
settings-osc-vrchat-enable-description = Перемикайте відправку та отримання даних.
|
||||
settings-osc-vrchat-enable-label = Увімкнути
|
||||
settings-osc-vrchat-network = Мережеві порти
|
||||
settings-osc-vrchat-network-description = Встановіть порти для прослуховування і відправки даних в VRChat.
|
||||
settings-osc-vrchat-network-port_in =
|
||||
.label = Вхідний Порт
|
||||
.placeholder = Вхідний Порт (зазвичай: 9001)
|
||||
settings-osc-vrchat-network-port_out =
|
||||
.label = Вихідний Порт
|
||||
.placeholder = Вихідний Порт (зазвичай: 9000)
|
||||
settings-osc-vrchat-network-address = Мережева адреса
|
||||
settings-osc-vrchat-network-address-description = Виберіть, за якою адресою надсилати дані до VRChat (перевірте налаштування Wi-Fi на своєму пристрої).
|
||||
settings-osc-vrchat-network-address-placeholder = IP-адреса VRChat
|
||||
settings-osc-vrchat-network-trackers = Трекери
|
||||
settings-osc-vrchat-network-trackers-description = Перемикання відправку конкретних трекерів через OSC.
|
||||
settings-osc-vrchat-network-trackers-chest = Груди
|
||||
settings-osc-vrchat-network-trackers-hip = Бедро
|
||||
settings-osc-vrchat-network-trackers-knees = Коліна
|
||||
settings-osc-vrchat-network-trackers-feet = Ступні
|
||||
settings-osc-vrchat-network-trackers-elbows = Лікті
|
||||
|
||||
## VMC OSC settings
|
||||
|
||||
settings-osc-vmc = Віртуальне захоплення руху
|
||||
# This cares about multilines
|
||||
settings-osc-vmc-description =
|
||||
Змінення настройок протоколу VMC (Virtual Motion Capture)
|
||||
щоб надсилати дані про кістки SlimeVR та отримувати дані про кістки з інших програм.
|
||||
settings-osc-vmc-enable = Увімкнути
|
||||
settings-osc-vmc-enable-description = Перемикайте відправку та отримання даних.
|
||||
settings-osc-vmc-enable-label = Увімкнути
|
||||
settings-osc-vmc-network = Мережеві порти
|
||||
settings-osc-vmc-network-description = Встановіть порти для прослуховування і відправки даних по VMC
|
||||
settings-osc-vmc-network-port_in =
|
||||
.label = Вхідний Порт
|
||||
.placeholder = Вхідний Порт (зазвичай: 39540)
|
||||
settings-osc-vmc-network-port_out =
|
||||
.label = Вихідний Порт
|
||||
.placeholder = Вихідний Порт (зазвичай: 39539)
|
||||
settings-osc-vmc-network-address = Мережева адреса
|
||||
settings-osc-vmc-network-address-description = Виберіть, за якою адресою надсилати дані через VMC
|
||||
settings-osc-vmc-network-address-placeholder = IPV4-адреса
|
||||
settings-osc-vmc-vrm = Модель VRM
|
||||
settings-osc-vmc-vrm-description = Завантажте модель VRM, щоб дозволити головний якір і забезпечити більш високу сумісність з іншими програмами
|
||||
settings-osc-vmc-vrm-model_unloaded = Модель не завантажена
|
||||
settings-osc-vmc-vrm-model_loaded =
|
||||
{ $titled ->
|
||||
[true] Модель завантажена: { $name }
|
||||
*[other] Завантажена модель без назви
|
||||
}
|
||||
settings-osc-vmc-vrm-file_select = Перетягніть модель для використання або <u>знайдіть</u>
|
||||
settings-osc-vmc-anchor_hip = Якір у стегон
|
||||
settings-osc-vmc-anchor_hip-description = Закріпіть стеження на стегнах, корисно для сидячих VTubing. Якщо вимкнено, завантажте модель VRM.
|
||||
settings-osc-vmc-anchor_hip-label = Якір у стегон
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
onboarding-skip = Пропустити налаштування
|
||||
onboarding-continue = Продовжити
|
||||
onboarding-wip = В роботі
|
||||
onboarding-previous_step = Попередній крок
|
||||
onboarding-setup_warning =
|
||||
<b>Попередження:</b> Початкова настройка потрібна для хорошого відстеження,
|
||||
це потрібно, якщо ви вперше використовуєте SlimeVR.
|
||||
onboarding-setup_warning-skip = Пропустити налаштування
|
||||
onboarding-setup_warning-cancel = Продовжити налаштування
|
||||
|
||||
## Wi-Fi setup
|
||||
|
||||
onboarding-wifi_creds-back = Повернутися до вступу
|
||||
onboarding-wifi_creds = Введіть дані Wi-Fi
|
||||
# This cares about multilines
|
||||
onboarding-wifi_creds-description =
|
||||
Трекери використовуватимуть ці дані для бездротового підключення.
|
||||
Будь ласка, використовуйте дані, до яких ви зараз підключені.
|
||||
onboarding-wifi_creds-skip = Пропустити налаштування Wi-Fi
|
||||
onboarding-wifi_creds-submit = Підтвердити!
|
||||
onboarding-wifi_creds-ssid =
|
||||
.label = Назва Wi-Fi
|
||||
.placeholder = Введіть назву Wi-Fi
|
||||
onboarding-wifi_creds-password =
|
||||
.label = Пароль
|
||||
.placeholder = Введіть Пароль
|
||||
|
||||
## Mounting setup
|
||||
|
||||
onboarding-reset_tutorial-back = Повернутися до розділу Калібрування прив'язки
|
||||
onboarding-reset_tutorial = Інструкція по скиданню
|
||||
onboarding-reset_tutorial-explanation = Коли ви використовуєте свої трекери, вони можуть вийти з вирівнювання через дрейф нахилу IMU або тому, що ви могли їх фізично перемістити. Це можна виправити кількома способами.
|
||||
onboarding-reset_tutorial-skip = Пропустити крок
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
Торкніться { $taps } виділеного трекера, щоб запустити скидання нахилу.
|
||||
|
||||
Це змусить трекери дивитися в тому ж напрямку, що і ваш шолом.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-1 =
|
||||
Торкніться { $taps } виділеного трекера, щоб ініціювати повне скидання.
|
||||
|
||||
Для цього потрібно стояти (i-поза). Існує затримка 3 секунди (налаштовується), перш ніж це дійсно станеться.
|
||||
Це повністю скидає положення та обертання всіх ваших трекерів. Це має вирішити більшість проблем.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-2 =
|
||||
Торкніться { $taps } виділеного елемента стеження, щоб активувати скидання прив'язки.
|
||||
|
||||
Скидання прив'язки допомагає в тому, як трекери насправді надягнені на вас, тому, якщо ви випадково перемістили їх і змінили місце прикріплення на велику кількість, це допоможе.
|
||||
|
||||
Вам потрібно бути в позі, ніби ви катаєтеся на лижах, як показано на майстрі автоматичної прив'язки, і у вас є 3-секундна затримка (налаштовується), перш ніж вона спрацює.
|
||||
|
||||
## Setup start
|
||||
|
||||
onboarding-home = Ласкаво просимо до SlimeVR
|
||||
onboarding-home-start = Давайте налаштуємося!
|
||||
|
||||
## Enter VR part of setup
|
||||
|
||||
onboarding-enter_vr-back = Повернутися до Прив'язки трекерів
|
||||
onboarding-enter_vr-title = Час вступати у VR!
|
||||
onboarding-enter_vr-description = Увімкніть усі свої трекери, а потім вступіть у VR!
|
||||
onboarding-enter_vr-ready = Я готовий
|
||||
|
||||
## Setup done
|
||||
|
||||
onboarding-done-title = Все готово!
|
||||
onboarding-done-description = Насолоджуйтесь досвідом трекінгу всього тіла
|
||||
onboarding-done-close = Закрити налаштування
|
||||
|
||||
## Tracker connection setup
|
||||
|
||||
onboarding-connect_tracker-back = Повернутися до даних Wi-Fi
|
||||
onboarding-connect_tracker-title = Підключіть трекери
|
||||
onboarding-connect_tracker-description-p0 = Тепер перейдемо до найцікавішого, з'єднання усіх трекерів!
|
||||
onboarding-connect_tracker-description-p1 = Просто підключіть все, що ще не підключено, через USB-порт.
|
||||
onboarding-connect_tracker-issue-serial = У мене виникли проблеми з підключенням!
|
||||
onboarding-connect_tracker-usb = USB-трекер
|
||||
onboarding-connect_tracker-connection_status-none = Шукаємо трекери
|
||||
onboarding-connect_tracker-connection_status-serial_init = Підключення до послідовного пристрою
|
||||
onboarding-connect_tracker-connection_status-provisioning = Надсилання даних Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-connecting = Спроба підключення до Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-looking_for_server = Шукаю сервер
|
||||
onboarding-connect_tracker-connection_status-connection_error = Не вдається підключитися до мережі Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-could_not_find_server = Не вдалося знайти сервер
|
||||
onboarding-connect_tracker-connection_status-done = Підключено до сервера
|
||||
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
|
||||
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
|
||||
# English in this case only has 2 plural rules, which are "one" and "other",
|
||||
# we use 0 in an explicit way because there is no plural rule in english for 0, so we directly say
|
||||
# if $amount is 0 then we say "No trackers connected"
|
||||
onboarding-connect_tracker-connected_trackers =
|
||||
{ $amount ->
|
||||
[0] Трекери не підключенно
|
||||
[one] 1 трекер підключенний
|
||||
[few] { $amount } трекерів підключенно
|
||||
[many] { $amount } трекерів підключенно
|
||||
*[other] { $amount } трекерів підключенно
|
||||
}
|
||||
onboarding-connect_tracker-next = Я підключив усі свої трекери
|
||||
|
||||
## Tracker calibration tutorial
|
||||
|
||||
onboarding-calibration_tutorial = Інструкція з калібрування IMU
|
||||
onboarding-calibration_tutorial-subtitle = Це допоможе зменшити дрейф трекера!
|
||||
onboarding-calibration_tutorial-description = Кожен раз, коли ви вмикаєте трекери, їм потрібно на мить відпочити на рівній поверхні для калібрування. Давайте зробимо те ж саме, натиснувши кнопку "{ onboarding-calibration_tutorial-calibrate }", <b>не переміщайте їх!</b>
|
||||
onboarding-calibration_tutorial-calibrate = Я поклав свої трекери на стіл
|
||||
onboarding-calibration_tutorial-status-waiting = Чекаємо на Вас
|
||||
onboarding-calibration_tutorial-status-calibrating = Калібрування
|
||||
onboarding-calibration_tutorial-status-success = Добре!
|
||||
onboarding-calibration_tutorial-status-error = Трекер переміщено
|
||||
|
||||
## Tracker assignment tutorial
|
||||
|
||||
onboarding-assignment_tutorial = Як підготувати Slime трекер перед його надяганням
|
||||
onboarding-assignment_tutorial-first_step = 1. Розмістіть наліпку з частиною тіла (якщо вона у вас є) на трекері відповідно до вашого вибору
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = Наліпка
|
||||
onboarding-assignment_tutorial-second_step = 2. Прикріпіть ремінь до трекера, зберігаючи гачок і петльову сторону лицьової сторони ремінця в такій орієнтації:
|
||||
onboarding-assignment_tutorial-second_step-continuation = Гачок і петльова сторона для подовжувача повинні бути в такій орієнтації:
|
||||
onboarding-assignment_tutorial-done = Я наклеїв наліпки і закріпив ремінці!
|
||||
|
||||
## Tracker assignment setup
|
||||
|
||||
onboarding-assign_trackers-back = Повернутися до даних Wi-Fi
|
||||
onboarding-assign_trackers-title = Призначити трекери
|
||||
onboarding-assign_trackers-description = Давайте виберемо, який трекер куди йде. Натисніть на місце, де ви хочете розмістити трекер
|
||||
# Look at translation of onboarding-connect_tracker-connected_trackers on how to use plurals
|
||||
# $assigned (Number) - Trackers that have been assigned a body part
|
||||
# $trackers (Number) - Trackers connected to the server
|
||||
onboarding-assign_trackers-assigned =
|
||||
{ $trackers ->
|
||||
[one] { $assigned } з 1 трекеру призначенно
|
||||
[few] { $assigned } з { $trackers } трекерів призначенно
|
||||
[many] { $assigned } з { $trackers } трекерів призначенно
|
||||
*[other] { $assigned } з { $trackers } трекерів призначенно
|
||||
}
|
||||
onboarding-assign_trackers-advanced = Відобразити розширені розташування призначень
|
||||
onboarding-assign_trackers-next = Я призначив усі трекери
|
||||
|
||||
## Tracker assignment warnings
|
||||
|
||||
# Note for devs, number is used for representing boolean states per bit.
|
||||
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
|
||||
onboarding-assign_trackers-warning-LEFT_FOOT =
|
||||
{ $unassigned ->
|
||||
[0] Ліва нога призначенна, але треба ще ліва щиколотка, ліве стегно та на вибір груди, бедро або талія повинні бути призначенні
|
||||
[1] Ліва ступня призначенна, але в тебе повинно бути ліве стегно і на вибір груди, бедро або талія теж призначенні
|
||||
[2] Ліва ступня призначенна, але в тебе повинно бути ще ліва щиколотка та на вибір груди, бедро або талія теж призначенні
|
||||
[3] Ліва ступня призначенна, але тобі ще треба на вибір груди, бедро або талія теж призначенні
|
||||
[4] Ліва ступня призначенна, але тобі ще потрібно ліва щиколотка і ліве стегно теж призначенні
|
||||
[5] Ліва ступня призначенна, але тобі ще потрібно ліве стегно теж призначити
|
||||
[6] Ліва ступня призначенна, але тобі ще треба ліву щиколотку теж призначити
|
||||
*[other] Ліва ступня призначенна, але тобі ще треба Невідома кількість непризначенних частин тіла теж призначенні
|
||||
}
|
||||
|
||||
## Tracker mounting method choose
|
||||
|
||||
onboarding-choose_mounting = Який метод калібрування закріплення використовувати?
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = Орієнтація кріплення коригується для розміщення трекерів на вашому тілі.
|
||||
onboarding-choose_mounting-auto_mounting = Автоматична прив'язка
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-label = Експериментальний
|
||||
onboarding-choose_mounting-auto_mounting-description = Це автоматично визначить напрямки прив'язки для всіх ваших трекерів з 2 поз
|
||||
onboarding-choose_mounting-manual_mounting = Ручна прив'язка
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-label = Рекомендується
|
||||
onboarding-choose_mounting-manual_mounting-description = Це дозволить вибрати напрямок прив'язки вручну для кожного трекера
|
||||
|
||||
## Tracker manual mounting setup
|
||||
|
||||
onboarding-manual_mounting-back = Повернутися до VR
|
||||
onboarding-manual_mounting = Ручне закріплення
|
||||
onboarding-manual_mounting-description = Натисніть на кожен трекер і виберіть, в який бік вони прив'язані
|
||||
onboarding-manual_mounting-auto_mounting = Автоматична прив'язка
|
||||
onboarding-manual_mounting-next = Наступний крок
|
||||
|
||||
## Tracker automatic mounting setup
|
||||
|
||||
onboarding-automatic_mounting-back = Повернутися до VR
|
||||
onboarding-automatic_mounting-next = Наступний крок
|
||||
onboarding-automatic_mounting-prev_step = Попередній крок
|
||||
onboarding-automatic_mounting-done-restart = Спробуйте знову
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
onboarding-automatic_proportions-prev_step = Попередній крок
|
||||
onboarding-automatic_proportions-requirements-next = Я ознайомився з вимогами
|
||||
onboarding-automatic_proportions-start_recording-title = Приготуйтеся рухатися
|
||||
onboarding-automatic_proportions-start_recording-next = Почати запис
|
||||
onboarding-automatic_proportions-recording-title = ЗАПИС
|
||||
onboarding-automatic_proportions-recording-description-p1 = Повторюйте рухи, показані нижче:
|
||||
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
|
||||
onboarding-automatic_proportions-recording-timer =
|
||||
{ $time ->
|
||||
[one] залишилась { $time } секунда
|
||||
[few] залишилось { $time } секунди
|
||||
[many] залишилось { $time } секунд
|
||||
*[other] залишилось { $time } секунд
|
||||
}
|
||||
onboarding-automatic_proportions-verify_results-title = Перевірити результати
|
||||
onboarding-automatic_proportions-verify_results-processing = Обробка результату
|
||||
|
||||
## Home
|
||||
|
||||
|
||||
## Status system
|
||||
|
||||
@@ -13,7 +13,7 @@ websocket-connection_lost = 与服务器的连接丢失,正在尝试重新连
|
||||
## Update notification
|
||||
|
||||
version_update-title = 新版本可用:{ $version }
|
||||
version_update-description = 点击“更新”将为您下载SlimeVR安装程序。
|
||||
version_update-description = 点击“{ version_update-update }”将为您下载 SlimeVR 安装程序。
|
||||
version_update-update = 更新
|
||||
version_update-close = 关闭
|
||||
|
||||
@@ -53,8 +53,8 @@ skeleton_bone-NONE = 无
|
||||
skeleton_bone-HEAD = 头部偏移
|
||||
skeleton_bone-NECK = 颈部长度
|
||||
skeleton_bone-torso_group = 躯干长度
|
||||
skeleton_bone-CHEST = 胸部长度
|
||||
skeleton_bone-CHEST_OFFSET = 胸部偏移
|
||||
skeleton_bone-CHEST = 胸部长度
|
||||
skeleton_bone-WAIST = 腰部长度
|
||||
skeleton_bone-HIP = 髋部长度
|
||||
skeleton_bone-HIP_OFFSET = 髋部偏移
|
||||
@@ -526,7 +526,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = 返回到佩戴校准
|
||||
onboarding-reset_tutorial = 重置教程
|
||||
onboarding-reset_tutorial-description = 跟踪器在使用时可能会由于IMU的偏航漂移而失准,或者可能因为您对它们进行了物理上的移动。您有几种方法来解决这个问题。
|
||||
onboarding-reset_tutorial-explanation = 追踪器在使用时可能会由于IMU的航向角漂移或是因为您移动了它们而失准。您有几种方法来解决这个问题。
|
||||
onboarding-reset_tutorial-skip = 跳过步骤
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
@@ -597,7 +597,7 @@ onboarding-connect_tracker-next = 所有的追踪器都连接好了
|
||||
|
||||
onboarding-calibration_tutorial = IMU校准教程
|
||||
onboarding-calibration_tutorial-subtitle = 这将有助于减少追踪器漂移!
|
||||
onboarding-calibration_tutorial-description = 每次开启追踪器时,它们都需要在平坦的表面上放置片刻以进行自校准。你可以通过点击“校准”按钮来手动校准, <b>校准过程中不要移动它们!</b>
|
||||
onboarding-calibration_tutorial-description = 每次开启追踪器时,它们都需要在平坦的表面上放置片刻以进行自校准。你也可以通过点击“{ onboarding-calibration_tutorial-calibrate }”按钮来手动校准, <b>校准过程中不要移动追踪器!</b>
|
||||
onboarding-calibration_tutorial-calibrate = 我已经把追踪器放在桌子上了
|
||||
onboarding-calibration_tutorial-status-waiting = 等待你的操作
|
||||
onboarding-calibration_tutorial-status-calibrating = 校准中
|
||||
@@ -701,11 +701,11 @@ onboarding-choose_mounting = 使用哪种方法校准佩戴朝向?
|
||||
onboarding-choose_mounting-description = 佩戴方向校准用于确定您身上的追踪器的朝向。
|
||||
onboarding-choose_mounting-auto_mounting = 自动设置佩戴方向
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = 推荐
|
||||
onboarding-choose_mounting-auto_mounting-label = 实验功能
|
||||
onboarding-choose_mounting-auto_mounting-description = 这将需要你做2个动作以自动检测所有追踪器的佩戴方向
|
||||
onboarding-choose_mounting-manual_mounting = 手动设置佩戴方向
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = 如果你清楚自己在做什么
|
||||
onboarding-choose_mounting-manual_mounting-label = 推荐
|
||||
onboarding-choose_mounting-manual_mounting-description = 这将需要你手动选择每个追踪器的佩戴方向
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -726,7 +726,7 @@ onboarding-automatic_mounting-next = 下一步
|
||||
onboarding-automatic_mounting-prev_step = 上一步
|
||||
onboarding-automatic_mounting-done-title = 佩戴方向已校准。
|
||||
onboarding-automatic_mounting-done-description = 你的佩戴方向校准完成!
|
||||
onboarding-automatic_mounting-done-restart = 返回以开始
|
||||
onboarding-automatic_mounting-done-restart = 再试一次
|
||||
onboarding-automatic_mounting-mounting_reset-title = 佩戴重置
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. 双腿弯曲以滑雪的姿势蹲下,上身向前倾斜,手臂弯曲。
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 按下佩戴重置按钮并等待 3 秒钟,然后追踪器的佩戴方向将被重置。
|
||||
@@ -752,7 +752,7 @@ onboarding-choose_proportions-manual_proportions = 手动调整身体比例
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = 用于精细调整
|
||||
onboarding-choose_proportions-manual_proportions-description = 这将需要你手动修改以调整你的身体比例
|
||||
onboarding-choose_proportions-save = 储存身体比例到文件
|
||||
onboarding-choose_proportions-export = 导出身体比例
|
||||
onboarding-choose_proportions-file_type = 身体比例文件
|
||||
|
||||
## Tracker manual proportions setup
|
||||
|
||||
@@ -13,7 +13,7 @@ websocket-connection_lost = 與伺服器的連線已中斷,正在嘗試重新
|
||||
## Update notification
|
||||
|
||||
version_update-title = 有可用的新版本:{ $version }
|
||||
version_update-description = 按下「更新」將為您下載 SlimeVR 安裝程式。
|
||||
version_update-description = 按下「{ version_update-update }」將為您下載 SlimeVR 安裝程式。
|
||||
version_update-update = 更新
|
||||
version_update-close = 關閉
|
||||
|
||||
@@ -36,6 +36,7 @@ body_part-RIGHT_HAND = 右手
|
||||
body_part-RIGHT_UPPER_LEG = 右大腿
|
||||
body_part-RIGHT_LOWER_LEG = 右小腿
|
||||
body_part-RIGHT_FOOT = 右腳
|
||||
body_part-UPPER_CHEST = 上胸
|
||||
body_part-CHEST = 胸部
|
||||
body_part-WAIST = 腰部
|
||||
body_part-HIP = 臀部
|
||||
@@ -53,8 +54,9 @@ skeleton_bone-NONE = 無
|
||||
skeleton_bone-HEAD = 頭部偏移
|
||||
skeleton_bone-NECK = 頸部長度
|
||||
skeleton_bone-torso_group = 軀幹長度
|
||||
skeleton_bone-CHEST = 胸部長度
|
||||
skeleton_bone-UPPER_CHEST = 上胸長度
|
||||
skeleton_bone-CHEST_OFFSET = 胸部偏移
|
||||
skeleton_bone-CHEST = 胸部長度
|
||||
skeleton_bone-WAIST = 腰部長度
|
||||
skeleton_bone-HIP = 臀部長度
|
||||
skeleton_bone-HIP_OFFSET = 臀部偏移
|
||||
@@ -230,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part }右大腿?
|
||||
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part }右小腿?
|
||||
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part }右腳?
|
||||
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part }右控制器?
|
||||
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part }上胸?
|
||||
tracker_selection_menu-CHEST = { -tracker_selection-part }胸部?
|
||||
tracker_selection_menu-WAIST = { -tracker_selection-part }腰部?
|
||||
tracker_selection_menu-HIP = { -tracker_selection-part }臀部?
|
||||
@@ -524,7 +527,7 @@ onboarding-wifi_creds-password =
|
||||
|
||||
onboarding-reset_tutorial-back = 返回到配戴校正
|
||||
onboarding-reset_tutorial = 重置教學
|
||||
onboarding-reset_tutorial-description = 使用追蹤器時會產生偏移,可能是因為慣性測量單元 (IMU) 會出現左右飄移,或是您移動了追蹤器所造成的。您有幾種方法來修正這個問題。
|
||||
onboarding-reset_tutorial-explanation = 當您使用追蹤器時追蹤器可能會跑位,原因來自於慣性測量單元 (IMU) 產生了左右飄移,或是您移動了追蹤器的實體位置。您有幾種方法來修正這個問題。
|
||||
onboarding-reset_tutorial-skip = 跳過本步驟
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 =
|
||||
@@ -595,7 +598,7 @@ onboarding-connect_tracker-next = 所有的追蹤器都連接好了
|
||||
|
||||
onboarding-calibration_tutorial = IMU 校正教學
|
||||
onboarding-calibration_tutorial-subtitle = 進行這項操作可以有效減少追蹤器發生飄移的機會
|
||||
onboarding-calibration_tutorial-description = 每次在打開追蹤器的開關時,需要將追蹤器放置在平面一會兒來進行自動校正。您也可以透過按下“校正”按鈕來進行手動校正,<b>校正過程中請勿移動追蹤器</b>。
|
||||
onboarding-calibration_tutorial-description = 每次在打開追蹤器的開關時,需要將追蹤器放置在平面一會兒來進行自動校正。您也可以透過按下「{ onboarding-calibration_tutorial-calibrate }」按鈕來進行手動校正,<b>校正過程中請勿移動追蹤器</b>。
|
||||
onboarding-calibration_tutorial-calibrate = 追蹤器已經放置在桌上了
|
||||
onboarding-calibration_tutorial-status-waiting = 正在等待您完成動作
|
||||
onboarding-calibration_tutorial-status-calibrating = 校正中
|
||||
@@ -608,7 +611,7 @@ onboarding-assignment_tutorial = 戴上 Slime 追蹤器前的準備事項
|
||||
onboarding-assignment_tutorial-first_step = 1. 若有標示身體部位的貼紙,可在您所要分配使用的追蹤器上貼上。
|
||||
# This text has a character limit of around 11 characters, so please keep it short
|
||||
onboarding-assignment_tutorial-sticker = 貼紙
|
||||
onboarding-assignment_tutorial-second_step = 2. 將綁帶有魔鬼氈的一面,依照下圖所示的方向穿過追蹤器:
|
||||
onboarding-assignment_tutorial-second_step = 2. 將綁帶有魔鬼氈(魔術貼)的一面,依照下圖所示的方向穿過追蹤器:
|
||||
onboarding-assignment_tutorial-second_step-continuation = 延伸追蹤器應照下圖所示:
|
||||
onboarding-assignment_tutorial-done = 我把貼紙跟綁帶都弄上了
|
||||
|
||||
@@ -699,11 +702,11 @@ onboarding-choose_mounting = 要使用哪一種配戴校正方式?
|
||||
onboarding-choose_mounting-description = 配戴校正可以校正追蹤器放在身上的位置。
|
||||
onboarding-choose_mounting-auto_mounting = 自動配戴校正
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-subtitle = 推薦使用
|
||||
onboarding-choose_mounting-auto_mounting-label = 實驗功能
|
||||
onboarding-choose_mounting-auto_mounting-description = 本選項會透過兩個身體姿勢,判斷所有追蹤器的配戴方位
|
||||
onboarding-choose_mounting-manual_mounting = 手動配戴校正
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-subtitle = 如果你清楚你要做什麼的話
|
||||
onboarding-choose_mounting-manual_mounting-label = 推薦使用
|
||||
onboarding-choose_mounting-manual_mounting-description = 本選項可以讓你選擇每個追蹤器的配戴方位
|
||||
|
||||
## Tracker manual mounting setup
|
||||
@@ -724,7 +727,7 @@ onboarding-automatic_mounting-next = 下一步
|
||||
onboarding-automatic_mounting-prev_step = 上一步
|
||||
onboarding-automatic_mounting-done-title = 配戴方向已校正。
|
||||
onboarding-automatic_mounting-done-description = 你的配戴方向校準完成!
|
||||
onboarding-automatic_mounting-done-restart = 返回以開始
|
||||
onboarding-automatic_mounting-done-restart = 再試一次
|
||||
onboarding-automatic_mounting-mounting_reset-title = 配戴重置
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. 雙腿彎曲以滑雪的姿勢蹲下,上身向前傾斜,手臂彎曲。
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. 按下「配戴重置」按鈕並等待 3 秒鐘,追蹤器的配戴方向將被重置。
|
||||
@@ -750,7 +753,7 @@ onboarding-choose_proportions-manual_proportions = 手動軀幹比例校正
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = 適合進行微調
|
||||
onboarding-choose_proportions-manual_proportions-description = 本選項可以讓你直接修改軀幹比例的設定值
|
||||
onboarding-choose_proportions-save = 儲存軀幹比例
|
||||
onboarding-choose_proportions-export = 匯出軀幹比例
|
||||
onboarding-choose_proportions-file_type = 軀幹比例描述檔
|
||||
|
||||
## Tracker manual proportions setup
|
||||
@@ -779,7 +782,7 @@ onboarding-automatic_proportions-requirements-description =
|
||||
你需要穿戴上追蹤器與頭戴顯示器。
|
||||
你的追蹤器與頭戴顯示器都已經連接到 SlimeVR 伺服器。
|
||||
你的追蹤器與頭戴顯示器在 SlimeVR 伺服器中運作正常。
|
||||
你的頭戴顯示器會回報定位資料給 SlimeVR 伺服器(通常為執行 SteamVR 並透過 SlimeVR 的 SteamVR 附加元件來連接 SlimeVR)。
|
||||
你的頭戴顯示器會回報定位資料給 SlimeVR 伺服器(通常為執行 SteamVR 並透過 SlimeVR 的 SteamVR 驅動程式來連接 SlimeVR)。
|
||||
onboarding-automatic_proportions-requirements-next = 我已閱讀使用需求
|
||||
onboarding-automatic_proportions-start_recording-title = 準備擺動作囉
|
||||
onboarding-automatic_proportions-start_recording-description = 我們現在要記錄一些特定的姿勢和動作,將會在下一個畫面中提示。當按鈕被按下時,準備好開始!
|
||||
|
||||
@@ -14,6 +14,7 @@ import { SlimeVRIcon } from './icon/SlimeVRIcon';
|
||||
import { UpperArmIcon } from './icon/UpperArmIcon';
|
||||
import { UpperLegIcon } from './icon/UpperLegIcon';
|
||||
import { WaistIcon } from './icon/WaistIcon';
|
||||
import { UpperChestIcon } from './icon/UpperChestIcon';
|
||||
|
||||
// All body parts that are right or left, are by default left!
|
||||
export const mapPart: Record<
|
||||
@@ -26,6 +27,9 @@ export const mapPart: Record<
|
||||
currentLocales: string[];
|
||||
}) => JSX.Element
|
||||
> = {
|
||||
[BodyPart.UPPER_CHEST]: ({ width }) => (
|
||||
<UpperChestIcon width={width}></UpperChestIcon>
|
||||
),
|
||||
[BodyPart.CHEST]: ({ width }) => <ChestIcon width={width}></ChestIcon>,
|
||||
[BodyPart.HEAD]: ({ width }) => <HeadsetIcon width={width}></HeadsetIcon>,
|
||||
[BodyPart.HIP]: ({ width }) => <HipIcon width={width}></HipIcon>,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
import {
|
||||
Control,
|
||||
Controller,
|
||||
UseFormGetValues,
|
||||
useWatch,
|
||||
} from 'react-hook-form';
|
||||
import { a11yClick } from '../utils/a11y';
|
||||
|
||||
export interface DropdownItem {
|
||||
@@ -17,6 +22,7 @@ export function Dropdown({
|
||||
display = 'fit',
|
||||
placeholder,
|
||||
control,
|
||||
getValues,
|
||||
name,
|
||||
items = [],
|
||||
}: {
|
||||
@@ -26,13 +32,29 @@ export function Dropdown({
|
||||
display?: 'fit' | 'block';
|
||||
placeholder: string;
|
||||
control: Control<any>;
|
||||
getValues: UseFormGetValues<any>;
|
||||
name: string;
|
||||
items: DropdownItem[];
|
||||
}) {
|
||||
const itemRefs: Record<string, HTMLLIElement> = {};
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const formValue = {
|
||||
...{ value: useWatch({ control, name }) as string },
|
||||
...{ value: getValues(name) as string },
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
const curItem = itemRefs[formValue.value];
|
||||
const dropdownParent = curItem
|
||||
? (curItem.closest('.dropdown-scroll') as HTMLElement | null)
|
||||
: null;
|
||||
if (curItem && dropdownParent) {
|
||||
dropdownParent.scroll({
|
||||
top: curItem.offsetTop - dropdownParent.offsetHeight / 2,
|
||||
});
|
||||
}
|
||||
|
||||
function onWheelEvent() {
|
||||
if (isOpen && !document.querySelector('div.dropdown-scroll:hover')) {
|
||||
setOpen(false);
|
||||
@@ -55,9 +77,9 @@ export function Dropdown({
|
||||
const isInDropdownScroll = document
|
||||
.querySelector('div.dropdown-scroll')
|
||||
?.contains(event.target as HTMLDivElement);
|
||||
const isInDropdown = document
|
||||
.querySelector('div.dropdown')
|
||||
?.contains(event.target as HTMLDivElement);
|
||||
const isInDropdown = !!(event.target as HTMLDivElement).closest(
|
||||
'.dropdown'
|
||||
);
|
||||
if (isOpen && !isInDropdownScroll && !isInDropdown) {
|
||||
setOpen(false);
|
||||
}
|
||||
@@ -139,7 +161,7 @@ export function Dropdown({
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute z-10 rounded shadow min-w-max max-h-[50vh]',
|
||||
'overflow-y-auto dropdown-scroll',
|
||||
'overflow-y-auto dropdown-scroll overflow-x-hidden',
|
||||
display === 'fit' && 'w-fit',
|
||||
display === 'block' && 'w-full',
|
||||
direction === 'up' && 'bottom-[45px]',
|
||||
@@ -151,21 +173,19 @@ export function Dropdown({
|
||||
alignment === 'left' && 'left-0'
|
||||
)}
|
||||
>
|
||||
<ul className="py-1 text-sm flex flex-col ">
|
||||
<ul className="py-1 text-sm flex flex-col">
|
||||
{items.map((item) => (
|
||||
<li
|
||||
className={classNames(
|
||||
'py-2 px-4 min-w-max cursor-pointer pr-2',
|
||||
'py-2 px-4 min-w-max cursor-pointer',
|
||||
variant == 'primary' &&
|
||||
'hover:bg-background-50 text-background-20 hover:text-background-10',
|
||||
'checked-hover:bg-background-50 text-background-20 ' +
|
||||
'checked-hover:text-background-10',
|
||||
variant == 'secondary' &&
|
||||
'hover:bg-background-60 text-background-20 hover:text-background-10',
|
||||
'checked-hover:bg-background-60 text-background-20 ' +
|
||||
'checked-hover:text-background-10',
|
||||
variant == 'tertiary' &&
|
||||
value !== item.value &&
|
||||
'bg-accent-background-30 hover:bg-accent-background-20',
|
||||
variant == 'tertiary' &&
|
||||
value === item.value &&
|
||||
'bg-accent-background-20'
|
||||
'bg-accent-background-30 checked-hover:bg-accent-background-20'
|
||||
)}
|
||||
onClick={() => {
|
||||
onChange(item.value);
|
||||
@@ -176,8 +196,10 @@ export function Dropdown({
|
||||
onChange(item.value);
|
||||
setOpen(false);
|
||||
}}
|
||||
ref={(ref) => (ref ? (itemRefs[item.value] = ref) : {})}
|
||||
key={item.value}
|
||||
tabIndex={0}
|
||||
data-checked={item.value === value}
|
||||
>
|
||||
{item.label}
|
||||
</li>
|
||||
|
||||
@@ -15,9 +15,11 @@ export function LangSelector({
|
||||
const { changeLocales } = useContext(LangContext);
|
||||
const { l10n } = useLocalization();
|
||||
const { config, setConfig } = useConfig();
|
||||
const { control, watch, handleSubmit } = useForm<{ lang: string }>({
|
||||
defaultValues: { lang: config?.lang || 'en' },
|
||||
});
|
||||
const { control, watch, handleSubmit, getValues } = useForm<{ lang: string }>(
|
||||
{
|
||||
defaultValues: { lang: config?.lang || 'en' },
|
||||
}
|
||||
);
|
||||
|
||||
const languagesItems = useMemo(
|
||||
() => langs.map(({ key, name }) => ({ label: name, value: key })),
|
||||
@@ -37,6 +39,7 @@ export function LangSelector({
|
||||
return (
|
||||
<Dropdown
|
||||
control={control}
|
||||
getValues={getValues}
|
||||
name="lang"
|
||||
placeholder={l10n.getString(
|
||||
'settings-general-interface-lang-placeholder'
|
||||
|
||||
@@ -15,6 +15,13 @@ export function PersonFrontIcon({ width }: { width?: number }) {
|
||||
href="/images/front-standing-pose.png"
|
||||
></image>
|
||||
{/* <path d="M84.53 224.074C83.953 230.874 88.569 266.874 90.951 280.984C92.085 287.671 95.195 298.565 94.076 304.349C92.476 312.411 92.017 322.843 92.896 328.918C93.451 332.607 95.196 349.618 92.696 355.845C91.389 359.108 88.996 375.832 88.996 375.832C82.756 391.587 86.278 390.812 86.278 390.812C88.21 393.183 91.519 390.998 91.519 390.998C92.1549 391.464 92.9388 391.682 93.7241 391.612C94.5094 391.542 95.2421 391.188 95.785 390.616C97.949 392.407 100.471 390.396 100.471 390.396C103.189 391.807 105.71 389.205 105.71 389.205C107.271 389.991 107.653 388.998 107.653 388.998C112.337 388.698 105.039 373.706 105.039 373.706C103.291 360.242 106.773 352.748 106.773 352.748C118.178 318.926 118.758 309.948 114.199 297.204C112.915 293.524 112.59 292.067 113.181 290.47C114.547 286.783 113.551 271.953 115.217 266.064C118.431 254.706 121.602 225.903 123.254 212.464C125.475 194.364 115.388 170.088 115.388 170.088C113.179 160.21 116.418 125.016 116.418 125.016C120.941 132.054 120.768 144.477 120.768 144.477C120.05 157.506 131.294 177.42 131.294 177.42C136.694 185.649 138.742 193.456 138.742 194.036C138.742 196.407 138.223 202.145 138.223 202.145L138.43 207.145C138.803 209.721 139.034 212.316 139.123 214.918C138.28 227.953 140.35 225.501 140.35 225.501C142.098 225.501 144.018 215.011 144.018 215.011C144.018 217.711 143.357 225.811 144.818 228.869C146.564 232.512 147.848 228.244 147.871 227.387C148.333 210.787 149.33 215.138 149.33 215.138C150.301 228.602 151.494 231.644 153.63 230.591C155.25 229.818 153.769 214.433 153.769 214.433C156.544 223.572 158.649 225.027 158.649 225.027C163.229 228.243 160.397 219.361 159.76 217.602C156.371 208.256 156.267 205.017 156.267 205.017C160.501 213.417 163.692 213.104 163.692 213.104C167.822 211.786 160.083 199.894 155.548 194.197C153.234 191.297 150.248 187.408 149.384 185.097C147.973 181.188 146.907 168.62 146.907 168.62C146.48 153.79 142.813 147.348 142.813 147.348C136.544 137.314 135.365 118.598 135.365 118.598L135.09 87C132.89 65.445 117.01 65.29 117.01 65.29C100.957 62.9 98.723 57.714 98.723 57.714C95.323 52.821 97.266 43.44 97.266 43.44C100.087 41.145 101.175 35.053 101.175 35.053C105.859 31.461 105.63 26.205 103.466 26.262C101.73 26.308 102.123 24.87 102.123 24.87C105.052 1.208 84.046 0 84.046 0H80.836C80.836 0 59.821 1.208 62.746 24.864C62.746 24.864 63.139 26.304 61.388 26.256C59.23 26.199 59.029 31.456 63.696 35.047C63.696 35.047 64.783 41.137 67.605 43.434C67.605 43.434 69.548 52.814 66.148 57.708C66.148 57.708 63.922 62.894 47.861 65.284C47.861 65.284 31.952 65.44 29.788 86.994L29.488 118.594C29.488 118.594 28.331 137.311 22.038 147.344C22.038 147.344 18.389 153.787 17.967 168.616C17.967 168.616 16.898 181.184 15.492 185.093C14.635 187.393 11.653 191.276 9.32001 194.193C4.74601 199.878 -2.94199 211.745 1.17101 213.1C1.17101 213.1 4.37901 213.412 8.59601 205.013C8.59601 205.013 8.50901 208.229 5.12501 217.598C4.46001 219.334 1.63201 228.217 6.21301 225.024C6.21301 225.024 8.33501 223.567 11.093 214.43C11.093 214.43 9.61301 229.815 11.26 230.588C13.412 231.642 14.586 228.599 15.56 215.135C15.56 215.135 16.56 210.787 17.017 227.384C17.04 228.241 18.295 232.509 20.049 228.866C21.529 225.811 20.864 217.727 20.864 215.008C20.864 215.008 22.764 225.498 24.536 225.498C24.536 225.498 26.624 227.95 25.767 214.915C25.628 212.786 26.375 208.415 26.467 207.142L26.667 202.142C26.667 202.142 26.146 196.417 26.146 194.033C26.146 193.442 28.194 185.646 33.594 177.417C33.594 177.417 44.826 157.494 44.103 144.474C44.103 144.474 43.947 132.051 48.47 125.013C48.47 125.013 51.68 160.205 49.505 170.085C49.505 170.085 39.405 194.358 41.629 212.461C43.27 225.937 46.435 254.702 49.657 266.061C51.34 271.938 50.345 286.761 51.693 290.467C52.301 292.076 51.982 293.558 50.675 297.201C46.141 309.947 46.718 318.925 58.123 352.745C58.123 352.745 61.633 360.239 59.859 373.703C59.859 373.703 52.572 388.695 57.239 388.995C57.239 388.995 57.604 389.988 59.182 389.202C59.182 389.202 61.703 391.802 64.427 390.393C64.427 390.393 66.95 392.407 69.106 390.613C69.6451 391.185 70.3751 391.54 71.158 391.61C71.9409 391.681 72.7225 391.462 73.355 390.995C73.355 390.995 76.664 393.227 78.63 390.809C78.63 390.809 82.123 391.584 75.904 375.829C75.904 375.829 73.522 359.129 72.209 355.842C69.709 349.621 71.474 332.57 72.009 328.915C72.87 322.806 72.409 312.398 70.835 304.346C69.684 298.575 72.801 287.679 73.952 280.981C76.317 266.881 80.952 230.881 80.373 224.071L82.288 224.743C83.0863 224.756 83.8692 224.522 84.53 224.074Z" /> */}
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82"
|
||||
cy="114"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.UPPER_CHEST]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="82"
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
export function ArrowDownIcon() {
|
||||
export function ArrowDownIcon({ size = 24 }: { size?: number }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="24"
|
||||
height="24"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArrowUpIcon({ size = 24 }: { size: number }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width={size}
|
||||
height={size}
|
||||
className="rotate-180"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
|
||||
|
||||
13
gui/src/components/commons/icon/UpperChestIcon.tsx
Normal file
13
gui/src/components/commons/icon/UpperChestIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export function UpperChestIcon({ width = 24 }: { width?: number }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width={width}
|
||||
viewBox="0 0 50 50"
|
||||
>
|
||||
<path d="M 20 2 C 18.156 2 16.700141 3.1345312 15.619141 4.0195312 C 12.698141 4.2755313 4 7.245 4 15 C 4 17.305 4.551 19.068203 4.875 19.908203 C 4.262 20.969203 3 23.679 3 28 C 3 28.352 3.184375 28.677422 3.484375 28.857422 L 8.484375 31.857422 C 8.835375 32.068422 9.2804219 32.044922 9.6074219 31.794922 C 10.290422 31.270922 10.833578 30.512578 11.267578 29.642578 C 11.118578 28.880578 11 28.05 11 27 L 11 21 C 11 20.448 11.448 20 12 20 C 12.43 20 12.790641 20.27425 12.931641 20.65625 C 12.944641 20.68425 12.982 20.797656 13 20.847656 C 13.197 21.404656 14.365 24 19 24 C 23.133 24 23.984531 19.976687 24.019531 19.804688 C 24.113531 19.329687 24.535 19.018531 25 19.019531 C 25.465 19.019531 25.886469 19.330687 25.980469 19.804688 C 26.015469 19.976687 26.867 24 31 24 C 35.635 24 36.803 21.405656 37 20.847656 C 37.018 20.797656 37.055359 20.68425 37.068359 20.65625 C 37.209359 20.27525 37.57 20 38 20 C 38.552 20 39 20.448 39 21 L 39 27 C 39 28.05 38.881422 28.881578 38.732422 29.642578 C 39.166422 30.513578 39.709578 31.269969 40.392578 31.792969 C 40.719578 32.042969 41.164625 32.066469 41.515625 31.855469 L 46.515625 28.855469 C 46.815625 28.676469 47 28.352 47 28 C 47 23.679 45.738 20.969203 45.125 19.908203 C 45.449 19.068203 46 17.305 46 15 C 46 7.245 37.301859 4.2755312 34.380859 4.0195312 C 33.299859 3.1345312 31.844 2 30 2 L 25 2 L 20 2 z M 25 22.609375 C 23.953 24.323375 22.059 26 19 26 C 16.075 26 14.195 25.130766 13 24.134766 L 13 27 C 13 28.281 13.194875 29.155016 13.421875 30.166016 C 13.692875 31.375016 14 32.746 14 35 C 14 35.027 13.987375 35.049172 13.984375 35.076172 C 13.994375 35.366172 14 35.669 14 36 L 14 42 C 14 44.997 18.628641 47.438484 19.556641 47.896484 C 19.693641 47.965484 19.847 48 20 48 L 25 48 L 30 48 C 30.153 48 30.306359 47.965484 30.443359 47.896484 C 31.371359 47.438484 36 44.997 36 42 L 36 36 C 36 35.669 36.005625 35.366172 36.015625 35.076172 C 36.012625 35.049172 36 35.027 36 35 C 36 32.746 36.307125 31.375016 36.578125 30.166016 C 36.805125 29.155016 37 28.281 37 27 L 37 24.134766 C 35.805 25.130766 33.925 26 31 26 C 27.941 26 26.047 24.323375 25 22.609375 z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,12 @@ import { BodyInteractions } from '../commons/BodyInteractions';
|
||||
import { TrackerPartCard } from '../tracker/TrackerPartCard';
|
||||
import { BodyPartError } from './pages/trackers-assign/TrackerAssignment';
|
||||
|
||||
export const SPINE_PARTS = [BodyPart.HIP, BodyPart.CHEST, BodyPart.WAIST];
|
||||
export const SPINE_PARTS = [
|
||||
BodyPart.UPPER_CHEST,
|
||||
BodyPart.CHEST,
|
||||
BodyPart.WAIST,
|
||||
BodyPart.HIP,
|
||||
];
|
||||
export const ASSIGNMENT_RULES: Partial<
|
||||
Record<BodyPart, (BodyPart | BodyPart[])[]>
|
||||
> = {
|
||||
@@ -26,6 +31,8 @@ export const ASSIGNMENT_RULES: Partial<
|
||||
[BodyPart.RIGHT_UPPER_LEG]: [SPINE_PARTS],
|
||||
[BodyPart.HIP]: [BodyPart.CHEST],
|
||||
[BodyPart.WAIST]: [BodyPart.CHEST],
|
||||
// TODO chest OR upperChest.
|
||||
// Also don't warn if no legs.
|
||||
};
|
||||
|
||||
export function BodyAssignment({
|
||||
@@ -177,6 +184,17 @@ export function BodyAssignment({
|
||||
}
|
||||
rightControls={
|
||||
<div className="flex flex-col justify-between h-full">
|
||||
{advanced && (
|
||||
<TrackerPartCard
|
||||
onlyAssigned={onlyAssigned}
|
||||
roleError={rolesWithErrors[BodyPart.UPPER_CHEST]?.label}
|
||||
td={trackerPartGrouped[BodyPart.UPPER_CHEST]}
|
||||
role={BodyPart.UPPER_CHEST}
|
||||
onClick={() => onRoleSelected(BodyPart.UPPER_CHEST)}
|
||||
direction="left"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TrackerPartCard
|
||||
onlyAssigned={onlyAssigned}
|
||||
roleError={rolesWithErrors[BodyPart.CHEST]?.label}
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { useLayout } from '../../hooks/layout';
|
||||
import { useOnboarding } from '../../hooks/onboarding';
|
||||
import { MainLayoutRoute } from '../MainLayout';
|
||||
import { TopBar } from '../TopBar';
|
||||
import { useBreakpoint } from '../../hooks/breakpoint';
|
||||
import { SkipSetupButton } from './SkipSetupButton';
|
||||
import { SkipSetupWarningModal } from './SkipSetupWarningModal';
|
||||
|
||||
export function OnboardingLayout({ children }: { children: ReactNode }) {
|
||||
const { layoutHeight, ref } = useLayout<HTMLDivElement>();
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { state } = useOnboarding();
|
||||
const { state, skipSetup } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
|
||||
return !state.alonePage ? (
|
||||
<>
|
||||
<TopBar progress={state.progress}></TopBar>
|
||||
<div
|
||||
ref={ref}
|
||||
className="flex-grow xs:pt-10 mobile:pt-2"
|
||||
className="flex-grow relative"
|
||||
style={{ height: layoutHeight }}
|
||||
>
|
||||
<div className="absolute top-12 mobile:top-0 right-2 z-50">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
</div>
|
||||
{children}
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -3,8 +3,6 @@ import { useOnboarding } from '../../../hooks/onboarding';
|
||||
import { Button } from '../../commons/Button';
|
||||
import { Typography } from '../../commons/Typography';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../SkipSetupButton';
|
||||
import { ProgressBar } from '../../commons/ProgressBar';
|
||||
import { LoaderIcon, SlimeState } from '../../commons/icon/LoaderIcon';
|
||||
import { useCountdown } from '../../../hooks/countdown';
|
||||
@@ -22,8 +20,7 @@ export const IMU_CALIBRATION_TIME = 4;
|
||||
|
||||
export function CalibrationTutorialPage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress } = useOnboarding();
|
||||
const [calibrationStatus, setCalibrationStatus] = useState(
|
||||
CalibrationStatus.WAITING
|
||||
);
|
||||
@@ -72,11 +69,6 @@ export function CalibrationTutorialPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex w-full h-full justify-center xs:px-20 mobile:px-5 pb-5 gap-14">
|
||||
<div className="flex gap-4 self-center mobile:z-10">
|
||||
<div className="flex flex-col max-w-md gap-3">
|
||||
@@ -160,11 +152,6 @@ export function CalibrationTutorialPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -20,8 +20,6 @@ import { ProgressBar } from '../../commons/ProgressBar';
|
||||
import { TipBox } from '../../commons/TipBox';
|
||||
import { Typography } from '../../commons/Typography';
|
||||
import { TrackerCard } from '../../tracker/TrackerCard';
|
||||
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../SkipSetupButton';
|
||||
import { useBnoExists } from '../../../hooks/imu-logic';
|
||||
import { useBreakpoint } from '../../../hooks/breakpoint';
|
||||
|
||||
@@ -60,16 +58,13 @@ const statusProgressMap = {
|
||||
export function ConnectTrackersPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { layoutHeight, ref } = isMobile
|
||||
? { layoutHeight: 0, ref: undefined }
|
||||
: useLayout<HTMLDivElement>();
|
||||
const { layoutHeight, ref } = useLayout<HTMLDivElement>();
|
||||
const { useConnectedTrackers } = useTrackers();
|
||||
const { applyProgress, state, skipSetup } = useOnboarding();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const navigate = useNavigate();
|
||||
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
|
||||
const [provisioningStatus, setProvisioningStatus] =
|
||||
useState<WifiProvisioningStatus>(WifiProvisioningStatus.NONE);
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
|
||||
applyProgress(0.4);
|
||||
|
||||
@@ -132,14 +127,9 @@ export function ConnectTrackersPage() {
|
||||
}, [provisioningStatus]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full items-center relative overflow-y-auto px-4 pb-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col h-full items-center px-4 pb-4">
|
||||
<div className="flex gap-10 mobile:flex-col w-full xs:max-w-7xl">
|
||||
<div className="flex flex-col w-full max-w-sm">
|
||||
<div className="flex flex-col w-full xs:max-w-sm">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-connect_tracker-title')}
|
||||
</Typography>
|
||||
@@ -237,20 +227,24 @@ export function ConnectTrackersPage() {
|
||||
</Typography>
|
||||
|
||||
<div
|
||||
className="xs:flex-grow xs:overflow-y-scroll"
|
||||
className="overflow-y-scroll mt-2"
|
||||
ref={ref}
|
||||
style={isMobile ? { height: layoutHeight - BOTTOM_HEIGHT } : {}}
|
||||
style={
|
||||
isMobile && state.alonePage
|
||||
? { height: layoutHeight - BOTTOM_HEIGHT }
|
||||
: { height: layoutHeight }
|
||||
}
|
||||
>
|
||||
<div className="grid lg:grid-cols-2 md:grid-cols-1 gap-2 xs:mx-3 pt-3">
|
||||
<div className="grid lg:grid-cols-2 md:grid-cols-1 gap-2 pr-1">
|
||||
{Array.from({
|
||||
...connectedTrackers,
|
||||
length: Math.max(connectedTrackers.length, isMobile ? 1 : 20),
|
||||
length: Math.max(connectedTrackers.length, 1),
|
||||
}).map((tracker, index) => (
|
||||
<div key={index}>
|
||||
{!tracker && (
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-xl h-16 mobile:animate-pulse',
|
||||
'rounded-xl h-16 animate-pulse',
|
||||
state.alonePage
|
||||
? 'bg-background-80'
|
||||
: 'bg-background-70'
|
||||
@@ -270,11 +264,6 @@ export function ConnectTrackersPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useState } from 'react';
|
||||
import { useOnboarding } from '../../../hooks/onboarding';
|
||||
import { Button } from '../../commons/Button';
|
||||
import { SlimeVRIcon } from '../../commons/icon/SimevrIcon';
|
||||
import { LangSelector } from '../../commons/LangSelector';
|
||||
import { Typography } from '../../commons/Typography';
|
||||
import { SkipSetupButton } from '../SkipSetupButton';
|
||||
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
|
||||
|
||||
export function HomePage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress } = useOnboarding();
|
||||
|
||||
applyProgress(0.1);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative px-4">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full justify-center px-4">
|
||||
<div className="flex flex-col gap-5 items-center z-10 scale-150 mb-20">
|
||||
<SlimeVRIcon></SlimeVRIcon>
|
||||
<Typography variant="mobile-title">
|
||||
@@ -64,11 +55,6 @@ export function HomePage() {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { useOnboarding } from '../../../hooks/onboarding';
|
||||
import { Button } from '../../commons/Button';
|
||||
import { Typography } from '../../commons/Typography';
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../SkipSetupButton';
|
||||
import {
|
||||
BodyPart,
|
||||
ResetResponseT,
|
||||
@@ -23,8 +21,7 @@ import { useBreakpoint } from '../../../hooks/breakpoint';
|
||||
export function ResetTutorialPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress } = useOnboarding();
|
||||
const { useAssignedTrackers } = useTrackers();
|
||||
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
|
||||
const [curIndex, setCurIndex] = useState(0);
|
||||
@@ -122,18 +119,13 @@ export function ResetTutorialPage() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex xs:flex-row mobile:flex-col w-full h-full xs:justify-center xs:px-20 mobile:px-4 gap-8 self-center">
|
||||
<div className="flex flex-col gap-3 xs:w-96 self-center">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-reset_tutorial')}
|
||||
</Typography>
|
||||
<Typography color="secondary">
|
||||
{l10n.getString('onboarding-reset_tutorial-description')}
|
||||
{l10n.getString('onboarding-reset_tutorial-explanation')}
|
||||
</Typography>
|
||||
<div className="flex">
|
||||
<Button variant="secondary" to="/onboarding/mounting/choose">
|
||||
@@ -197,16 +189,16 @@ export function ResetTutorialPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const TORSO_PARTS = [BodyPart.CHEST, BodyPart.WAIST, BodyPart.HIP];
|
||||
export const TORSO_PARTS = [
|
||||
BodyPart.UPPER_CHEST,
|
||||
BodyPart.CHEST,
|
||||
BodyPart.WAIST,
|
||||
BodyPart.HIP,
|
||||
];
|
||||
export const LEFT_LEG_PARTS = [
|
||||
BodyPart.LEFT_UPPER_LEG,
|
||||
BodyPart.LEFT_LOWER_LEG,
|
||||
|
||||
@@ -4,19 +4,15 @@ import { useWifiForm } from '../../../hooks/wifi-form';
|
||||
import { Button } from '../../commons/Button';
|
||||
import { Input } from '../../commons/Input';
|
||||
import { Typography } from '../../commons/Typography';
|
||||
import { useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../SkipSetupButton';
|
||||
import classNames from 'classnames';
|
||||
import { useTrackers } from '../../../hooks/tracker';
|
||||
import { useBnoExists } from '../../../hooks/imu-logic';
|
||||
|
||||
export function WifiCredsPage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { control, handleSubmit, submitWifiCreds, formState } = useWifiForm();
|
||||
const { useConnectedTrackers } = useTrackers();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const connectedTrackers = useConnectedTrackers();
|
||||
|
||||
applyProgress(0.2);
|
||||
@@ -29,11 +25,6 @@ export function WifiCredsPage() {
|
||||
onSubmit={handleSubmit(submitWifiCreds)}
|
||||
>
|
||||
<div className="flex flex-col w-full h-full xs:justify-center items-center relative ">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex mobile:flex-col xs:gap-10 px-4">
|
||||
<div className="flex flex-col max-w-sm">
|
||||
<Typography variant="main-title">
|
||||
@@ -117,11 +108,6 @@ export function WifiCredsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ import { useLocalization } from '@fluent/react';
|
||||
import { useOnboarding } from '../../../../hooks/onboarding';
|
||||
import { Button } from '../../../commons/Button';
|
||||
import { Typography } from '../../../commons/Typography';
|
||||
import { useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import { useTrackers } from '../../../../hooks/tracker';
|
||||
import { useBnoExists } from '../../../../hooks/imu-logic';
|
||||
import { StickerSlime } from './StickerSlime';
|
||||
@@ -13,8 +10,7 @@ import { ExtensionArrow } from './ExtensionArrow';
|
||||
|
||||
export function AssignmentTutorialPage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress } = useOnboarding();
|
||||
const { useConnectedTrackers } = useTrackers();
|
||||
const connectedTrackers = useConnectedTrackers();
|
||||
const bnoExists = useBnoExists(connectedTrackers);
|
||||
@@ -23,83 +19,71 @@ export function AssignmentTutorialPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative">
|
||||
<SkipSetupButton
|
||||
visible={true}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col w-full h-full justify-center xs:px-20 gap-3 pb-2">
|
||||
<div className="overflow-y-auto px-4">
|
||||
<div className="mb-10 self-center">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-assignment_tutorial')}
|
||||
</Typography>
|
||||
<div className="flex flex-col gap-5 h-full items-center overflow-y-auto w-full xs:justify-center relative py-2">
|
||||
<div className="flex flex-col w-full xs:justify-center xs:px-20 gap-3 pb-2 px-4">
|
||||
<div className="mb-10 self-center">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-assignment_tutorial')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex flex-col self-center justify-center gap-5 px-2">
|
||||
<div className="flex gap-12 xs:flex-row mobile:flex-col self-center justify-center">
|
||||
<div className="flex flex-col gap-5 xs:w-1/4">
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-first_step'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="stroke-background-10 fill-background-10 flex justify-center">
|
||||
<StickerSlime width="65%"></StickerSlime>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 xs:w-1/4">
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-second_step'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="fill-background-10 stroke-background-10 flex justify-center">
|
||||
<TrackerArrow width="75%"></TrackerArrow>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-second_step-continuation'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="fill-background-10 stroke-background-10 flex justify-center">
|
||||
<ExtensionArrow width="75%"></ExtensionArrow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col self-center justify-center gap-5 px-2">
|
||||
<div className="flex gap-12 xs:flex-row mobile:flex-col self-center justify-center">
|
||||
<div className="flex flex-col gap-5 xs:w-1/4">
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-first_step'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="stroke-background-10 fill-background-10 flex justify-center">
|
||||
<StickerSlime width="65%"></StickerSlime>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 xs:w-1/4">
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-second_step'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="fill-background-10 stroke-background-10 flex justify-center">
|
||||
<TrackerArrow width="75%"></TrackerArrow>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'onboarding-assignment_tutorial-second_step-continuation'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="fill-background-10 stroke-background-10 flex justify-center">
|
||||
<ExtensionArrow width="75%"></ExtensionArrow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="secondary"
|
||||
to={
|
||||
bnoExists
|
||||
? '/onboarding/calibration-tutorial'
|
||||
: '/onboarding/wifi-creds'
|
||||
}
|
||||
>
|
||||
{l10n.getString('onboarding-previous_step')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
to="/onboarding/trackers-assign"
|
||||
className="ml-auto"
|
||||
>
|
||||
{l10n.getString('onboarding-assignment_tutorial-done')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="secondary"
|
||||
to={
|
||||
bnoExists
|
||||
? '/onboarding/calibration-tutorial'
|
||||
: '/onboarding/wifi-creds'
|
||||
}
|
||||
>
|
||||
{l10n.getString('onboarding-previous_step')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
to="/onboarding/trackers-assign"
|
||||
className="ml-auto"
|
||||
>
|
||||
{l10n.getString('onboarding-assignment_tutorial-done')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useState } from 'react';
|
||||
import { RpcMessage, SkeletonResetAllRequestT } from 'solarxr-protocol';
|
||||
import {
|
||||
AutoboneContextC,
|
||||
@@ -16,16 +15,13 @@ import { PutTrackersOnStep } from './autobone-steps/PutTrackersOn';
|
||||
import { Recording } from './autobone-steps/Recording';
|
||||
import { StartRecording } from './autobone-steps/StartRecording';
|
||||
import { VerifyResultsStep } from './autobone-steps/VerifyResults';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import { useCountdown } from '../../../../hooks/countdown';
|
||||
|
||||
export function AutomaticProportionsPage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { sendRPCPacket } = useWebsocketAPI();
|
||||
const context = useProvideAutobone();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { isCounting, startCountdown, timer } = useCountdown({
|
||||
onCountdownEnd: () => {
|
||||
sendRPCPacket(
|
||||
@@ -39,13 +35,8 @@ export function AutomaticProportionsPage() {
|
||||
|
||||
return (
|
||||
<AutoboneContextC.Provider value={context}>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative px-4 pb-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col w-full h-full justify-center max-w-3xl gap-5">
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center overflow-y-auto overflow-x-hidden relative px-4 pb-4">
|
||||
<div className="flex flex-col w-full xs:h-full xs:justify-center max-w-3xl gap-5">
|
||||
<div className="flex flex-col max-w-lg gap-3">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-automatic_proportions-title')}
|
||||
@@ -85,11 +76,6 @@ export function AutomaticProportionsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</AutoboneContextC.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import classNames from 'classnames';
|
||||
import { MouseEventHandler, ReactNode, useEffect } from 'react';
|
||||
import {
|
||||
MouseEventHandler,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
useRef,
|
||||
UIEvent,
|
||||
MouseEvent,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import {
|
||||
LabelType,
|
||||
ProportionChangeType,
|
||||
@@ -8,7 +16,7 @@ import {
|
||||
} from '../../../../hooks/manual-proportions';
|
||||
import { useLocaleConfig } from '../../../../i18n/config';
|
||||
import { Typography } from '../../../commons/Typography';
|
||||
import { useBreakpoint } from '../../../../hooks/breakpoint';
|
||||
import { ArrowDownIcon, ArrowUpIcon } from '../../../commons/icon/ArrowIcons';
|
||||
|
||||
function IncrementButton({
|
||||
children,
|
||||
@@ -21,7 +29,8 @@ function IncrementButton({
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={classNames(
|
||||
'p-3 rounded-lg xs:w-16 xs:h-16 mobile:w-10 flex flex-col justify-center items-center bg-background-60 hover:bg-opacity-50'
|
||||
'p-3 rounded-lg xs:w-16 xs:h-16 mobile:w-10 flex flex-col justify-center items-center',
|
||||
'bg-background-60 hover:bg-opacity-50 active:bg-accent-background-30'
|
||||
)}
|
||||
>
|
||||
<Typography variant="mobile-title" bold>
|
||||
@@ -40,25 +49,25 @@ export function BodyProportions({
|
||||
type: 'linear' | 'ratio';
|
||||
variant: 'onboarding' | 'alone';
|
||||
}) {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
|
||||
const [bodyParts, _ratioMode, currentSelection, dispatch, setRatioMode] =
|
||||
useManualProportions();
|
||||
const { currentLocales } = useLocaleConfig();
|
||||
const { bodyParts, dispatch, state, setRatioMode } = useManualProportions();
|
||||
const { l10n } = useLocalization();
|
||||
const { currentLocales } = useLocaleConfig();
|
||||
|
||||
const srcollerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const cmFormat = Intl.NumberFormat(currentLocales, {
|
||||
style: 'unit',
|
||||
unit: 'centimeter',
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
const configFormat = Intl.NumberFormat(currentLocales, {
|
||||
signDisplay: 'always',
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
const percentageFormat = Intl.NumberFormat(currentLocales, {
|
||||
style: 'percent',
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
const configFormat = Intl.NumberFormat(currentLocales, {
|
||||
signDisplay: 'always',
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (type === 'linear') {
|
||||
@@ -68,131 +77,229 @@ export function BodyProportions({
|
||||
}
|
||||
}, [type]);
|
||||
|
||||
useEffect(() => {
|
||||
if (srcollerRef.current && bodyParts.length > 0) {
|
||||
moveToIndex(1);
|
||||
}
|
||||
}, [srcollerRef, bodyParts.length]);
|
||||
|
||||
const handleUIEvent = (e: UIEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
|
||||
const itemHeight = target.offsetHeight / 3;
|
||||
|
||||
const atSnappingPoint = target.scrollTop % itemHeight === 0;
|
||||
|
||||
if (atSnappingPoint) {
|
||||
const index = target.scrollTop / itemHeight;
|
||||
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
|
||||
const id = elem.getAttribute('itemid');
|
||||
if (id) selectNew(id);
|
||||
}
|
||||
};
|
||||
|
||||
const clickPart = (id: string) => (e: MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
selectNew(id);
|
||||
};
|
||||
|
||||
const moveToIndex = (index: number, smooth = true) => {
|
||||
// We add one because of the offset placeholder
|
||||
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
|
||||
elem?.scrollIntoView({
|
||||
behavior: smooth ? 'smooth' : 'auto',
|
||||
block: 'center',
|
||||
});
|
||||
|
||||
const id = elem.getAttribute('itemid');
|
||||
if (id) selectNew(id);
|
||||
};
|
||||
|
||||
const selectNew = (id: string) => {
|
||||
const part = bodyParts.find(({ label }) => label === id);
|
||||
if (!part) return;
|
||||
|
||||
const { value: originalValue, label, type, ...props } = part;
|
||||
|
||||
const value =
|
||||
'index' in props && props.index !== undefined
|
||||
? props.bones[props.index].value
|
||||
: originalValue;
|
||||
|
||||
switch (type) {
|
||||
case LabelType.Bone: {
|
||||
if (!('bone' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
value,
|
||||
type: ProportionChangeType.Bone,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case LabelType.Group: {
|
||||
if (!('bones' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
value,
|
||||
type: ProportionChangeType.Group,
|
||||
index: undefined,
|
||||
parentLabel: label,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case LabelType.GroupPart: {
|
||||
if (!('index' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
// If this isn't done, we are replacing total
|
||||
// with percentage value
|
||||
value: originalValue,
|
||||
type: ProportionChangeType.Group,
|
||||
index: props.index,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const seletedLabel = useMemo(() => {
|
||||
return bodyParts.find(({ label }) => label === state.currentLabel);
|
||||
}, [state]);
|
||||
|
||||
const move = (action: 'next' | 'prev') => {
|
||||
const elem = srcollerRef.current?.querySelector(
|
||||
`div[itemid=${state.currentLabel}]`
|
||||
);
|
||||
|
||||
const moveId = (id: string, elem: HTMLDivElement) => {
|
||||
elem?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
selectNew(id);
|
||||
};
|
||||
|
||||
if (action === 'prev') {
|
||||
const prevElem = elem?.previousSibling as HTMLDivElement;
|
||||
const prevId = prevElem.getAttribute('itemid');
|
||||
if (!prevId) return;
|
||||
moveId(prevId, prevElem);
|
||||
}
|
||||
|
||||
if (action === 'next') {
|
||||
const nextElem = elem?.nextSibling as HTMLDivElement;
|
||||
const nextId = nextElem.getAttribute('itemid');
|
||||
if (!nextId) return;
|
||||
moveId(nextId, nextElem);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div
|
||||
className={classNames(
|
||||
'flex flex-col xs:overflow-y-scroll xs:overflow-x-hidden xs:max-h-[450px] xs:h-[54vh]',
|
||||
'w-full px-1 gap-3 xs:gradient-mask-b-90'
|
||||
)}
|
||||
>
|
||||
<>
|
||||
{bodyParts.map(({ label, type, value: originalValue, ...props }) => {
|
||||
const value =
|
||||
'index' in props && props.index !== undefined
|
||||
? props.bones[props.index].value
|
||||
: originalValue;
|
||||
const selected = isMobile || currentSelection.label === label;
|
||||
|
||||
const selectNew = () => {
|
||||
switch (type) {
|
||||
case LabelType.Bone: {
|
||||
if (!('bone' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
value,
|
||||
type: ProportionChangeType.Bone,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case LabelType.Group: {
|
||||
if (!('bones' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
value,
|
||||
type: ProportionChangeType.Group,
|
||||
index: undefined,
|
||||
parentLabel: label,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case LabelType.GroupPart: {
|
||||
if (!('index' in props)) throw 'unreachable';
|
||||
dispatch({
|
||||
...props,
|
||||
label,
|
||||
// If this isn't done, we are replacing total
|
||||
// with percentage value
|
||||
value: originalValue,
|
||||
type: ProportionChangeType.Group,
|
||||
index: props.index,
|
||||
});
|
||||
(bodyParts.length > 0 && (
|
||||
<div className="flex w-full gap-3">
|
||||
<div className="flex items-center mobile:justify-center mobile:flex-col gap-2 my-2">
|
||||
{!precise && (
|
||||
<div className="mobile:order-2">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.05,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -5,
|
||||
})
|
||||
}
|
||||
>
|
||||
{configFormat.format(-5)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
)}
|
||||
<div className="mobile:order-1">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.01,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -1,
|
||||
})
|
||||
}
|
||||
};
|
||||
>
|
||||
{configFormat.format(-1)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
{precise && (
|
||||
<div className="mobile:order-2">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.005,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -0.5,
|
||||
})
|
||||
}
|
||||
>
|
||||
{configFormat.format(-0.5)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-grow flex-col">
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
onClick={() => move('prev')}
|
||||
className={classNames(
|
||||
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
|
||||
'items-center fill-background-10',
|
||||
srcollerRef?.current?.scrollTop ?? 0 > 0
|
||||
? 'opacity-100 active:bg-accent-background-30'
|
||||
: 'opacity-50'
|
||||
)}
|
||||
>
|
||||
<ArrowUpIcon size={32}></ArrowUpIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={srcollerRef}
|
||||
onScroll={handleUIEvent}
|
||||
className="h-60 flex-grow flex-col overflow-y-auto snap-y snap-mandatory snap-always no-scrollbar"
|
||||
>
|
||||
<div className="h-20 snap-start "></div>
|
||||
{bodyParts.map((part) => {
|
||||
const { label, value: originalValue, type, ...props } = part;
|
||||
const value =
|
||||
'index' in props && props.index !== undefined
|
||||
? props.bones[props.index].value
|
||||
: originalValue;
|
||||
|
||||
return (
|
||||
<div className="flex" key={label}>
|
||||
const selected = state.currentLabel === label;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex gap-2 transition-opacity duration-300',
|
||||
!selected && 'opacity-0 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
{!precise && (
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.05,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -5,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(-5)}
|
||||
</IncrementButton>
|
||||
)}
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.01,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -1,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(-1)}
|
||||
</IncrementButton>
|
||||
{precise && (
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: -0.005,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: -0.5,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(-0.5)}
|
||||
</IncrementButton>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-grow flex-col px-2"
|
||||
onClick={selectNew}
|
||||
key={label}
|
||||
itemID={label}
|
||||
onClick={clickPart(label)}
|
||||
className="snap-start h-20 flex-col flex justify-center"
|
||||
>
|
||||
<div
|
||||
key={label}
|
||||
className={classNames(
|
||||
'p-3 rounded-lg xs:h-16 flex w-full items-center justify-between xs:px-6 mobile:px-3 transition-colors duration-300 bg-background-60',
|
||||
(selected && 'opacity-100') || 'opacity-50'
|
||||
'h-16 p-3 rounded-lg flex w-full items-center justify-between px-6 transition-colors',
|
||||
'duration-300 bg-background-60',
|
||||
(selected && 'opacity-100') ||
|
||||
'opacity-50 active:bg-accent-background-30'
|
||||
)}
|
||||
>
|
||||
<Typography variant="section-title" bold>
|
||||
@@ -211,70 +318,87 @@ export function BodyProportions({
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex gap-2 transition-opacity duration-300',
|
||||
!selected && 'opacity-0 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
{precise && (
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.005,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 0.5,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(+0.5)}
|
||||
</IncrementButton>
|
||||
)}
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.01,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 1,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(+1)}
|
||||
</IncrementButton>
|
||||
{!precise && (
|
||||
<IncrementButton
|
||||
onClick={() => {
|
||||
selectNew();
|
||||
return type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.05,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 5,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{configFormat.format(+5)}
|
||||
</IncrementButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
<div className="h-20 snap-start"></div>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
onClick={() => move('next')}
|
||||
className={classNames(
|
||||
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
|
||||
'items-center fill-background-10',
|
||||
srcollerRef?.current?.scrollTop !==
|
||||
(srcollerRef?.current?.scrollHeight ?? 0) -
|
||||
(srcollerRef?.current?.offsetHeight ?? 0)
|
||||
? 'opacity-100 active:bg-accent-background-30'
|
||||
: 'opacity-50'
|
||||
)}
|
||||
>
|
||||
<ArrowDownIcon size={32}></ArrowDownIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mobile:justify-center mobile:flex-col gap-2">
|
||||
{precise && (
|
||||
<div className="mobile:order-2">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.005,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 0.5,
|
||||
})
|
||||
}
|
||||
>
|
||||
{configFormat.format(+0.5)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mobile:order-1">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.01,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 1,
|
||||
})
|
||||
}
|
||||
>
|
||||
{configFormat.format(+1)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
{!precise && (
|
||||
<div className="mobile:order-2">
|
||||
<IncrementButton
|
||||
onClick={() =>
|
||||
seletedLabel?.type === LabelType.GroupPart
|
||||
? dispatch({
|
||||
type: ProportionChangeType.Ratio,
|
||||
value: 0.05,
|
||||
})
|
||||
: dispatch({
|
||||
type: ProportionChangeType.Linear,
|
||||
value: 5,
|
||||
})
|
||||
}
|
||||
>
|
||||
{configFormat.format(+5)}
|
||||
</IncrementButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)) || <></>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ import { PersonFrontIcon } from '../../../commons/PersonFrontIcon';
|
||||
import { Typography } from '../../../commons/Typography';
|
||||
import { BodyProportions } from './BodyProportions';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useBreakpoint } from '../../../../hooks/breakpoint';
|
||||
|
||||
export function ButtonsControl() {
|
||||
@@ -49,8 +47,7 @@ export function ButtonsControl() {
|
||||
export function ManualProportionsPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
|
||||
applyProgress(0.9);
|
||||
|
||||
@@ -68,14 +65,9 @@ export function ManualProportionsPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center overflow-y-auto relative">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col w-full h-full xs:max-w-5xl xs:justify-center">
|
||||
<div className="flex gap-8 justify-center">
|
||||
<div className="flex flex-col w-full xs:max-w-2xl gap-3 items-center">
|
||||
<div className="flex gap-8 justify-center h-full xs:items-center">
|
||||
<div className="flex flex-col w-full xs:max-w-2xl gap-3 items-center mobile:justify-around">
|
||||
<div className="flex flex-col">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-manual_proportions-title')}
|
||||
@@ -100,29 +92,25 @@ export function ManualProportionsPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<BodyProportions
|
||||
precise={precise}
|
||||
type={ratio ? 'ratio' : 'linear'}
|
||||
variant={state.alonePage ? 'alone' : 'onboarding'}
|
||||
></BodyProportions>
|
||||
<div className="w-full px-2">
|
||||
<BodyProportions
|
||||
precise={precise}
|
||||
type={ratio ? 'ratio' : 'linear'}
|
||||
variant={state.alonePage ? 'alone' : 'onboarding'}
|
||||
></BodyProportions>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-col flex-grow gap-3 rounded-xl fill-background-50 items-center hidden md:flex">
|
||||
<PersonFrontIcon width={200}></PersonFrontIcon>
|
||||
</div>
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<div className="flex gap-3 mt-5 mx-4">
|
||||
<div className="flex gap-3 my-5 mx-4 justify-between">
|
||||
<ButtonsControl></ButtonsControl>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useOnboarding } from '../../../../hooks/onboarding';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import classNames from 'classnames';
|
||||
import { Typography } from '../../../commons/Typography';
|
||||
import { Button } from '../../../commons/Button';
|
||||
@@ -20,8 +18,7 @@ import { useIsTauri } from '../../../../hooks/breakpoint';
|
||||
export function ProportionsChoose() {
|
||||
const isTauri = useIsTauri();
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
|
||||
const [animated, setAnimated] = useState(false);
|
||||
|
||||
@@ -58,11 +55,6 @@ export function ProportionsChoose() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center mobile:overflow-y-auto relative px-4 pb-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col gap-4 justify-center">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-choose_proportions')}
|
||||
@@ -186,16 +178,11 @@ export function ProportionsChoose() {
|
||||
)
|
||||
}
|
||||
>
|
||||
{l10n.getString('onboarding-choose_proportions-save')}
|
||||
{l10n.getString('onboarding-choose_proportions-export')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,6 @@ import { MountingResetStep } from './mounting-steps/MountingReset';
|
||||
import { PreparationStep } from './mounting-steps/Preparation';
|
||||
import { PutTrackersOnStep } from './mounting-steps/PutTrackersOn';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { useState } from 'react';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
|
||||
const steps: Step[] = [
|
||||
{ type: 'numbered', component: PutTrackersOnStep },
|
||||
@@ -18,19 +15,13 @@ const steps: Step[] = [
|
||||
];
|
||||
export function AutomaticMountingPage() {
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
|
||||
applyProgress(0.7);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-2 h-full items-center w-full xs:justify-center relative overflow-y-auto overflow-x-hidden px-4 pb-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col w-full h-full xs:justify-center xs:max-w-3xl gap-5">
|
||||
<div className="flex flex-col xs:max-w-lg gap-3">
|
||||
<Typography variant="main-title">
|
||||
@@ -48,11 +39,6 @@ export function AutomaticMountingPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,16 +11,13 @@ import { Typography } from '../../../commons/Typography';
|
||||
import { BodyAssignment } from '../../BodyAssignment';
|
||||
import { MountingSelectionMenu } from './MountingSelectionMenu';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import { useBreakpoint } from '../../../../hooks/breakpoint';
|
||||
|
||||
export function ManualMountingPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { sendRPCPacket } = useWebsocketAPI();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
|
||||
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
|
||||
|
||||
@@ -70,11 +67,6 @@ export function ManualMountingPage() {
|
||||
onDirectionSelected={onDirectionSelected}
|
||||
></MountingSelectionMenu>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex xs:flex-row mobile:flex-col h-full px-8 xs:w-full xs:justify-center mobile:px-4 items-center">
|
||||
<div className="flex flex-col w-full xs:max-w-sm gap-3">
|
||||
<Typography variant="main-title">
|
||||
@@ -109,11 +101,6 @@ export function ManualMountingPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useOnboarding } from '../../../../hooks/onboarding';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useState } from 'react';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import classNames from 'classnames';
|
||||
import { Typography } from '../../../commons/Typography';
|
||||
import { Button } from '../../../commons/Button';
|
||||
@@ -18,11 +17,6 @@ export function MountingChoose() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto px-4 pb-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
<div className="flex flex-col gap-4 justify-center">
|
||||
<div className="xs:w-10/12 xs:max-w-[666px]">
|
||||
<Typography variant="main-title">
|
||||
@@ -58,7 +52,7 @@ export function MountingChoose() {
|
||||
</Typography>
|
||||
<Typography variant="vr-accessible" italic>
|
||||
{l10n.getString(
|
||||
'onboarding-choose_mounting-auto_mounting-subtitle'
|
||||
'onboarding-choose_mounting-auto_mounting-label'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
@@ -106,7 +100,7 @@ export function MountingChoose() {
|
||||
</Typography>
|
||||
<Typography variant="vr-accessible" italic>
|
||||
{l10n.getString(
|
||||
'onboarding-choose_mounting-manual_mounting-subtitle'
|
||||
'onboarding-choose_mounting-manual_mounting-label'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
@@ -26,8 +26,6 @@ import { Typography } from '../../../commons/Typography';
|
||||
import { ASSIGNMENT_RULES, BodyAssignment } from '../../BodyAssignment';
|
||||
import { NeckWarningModal } from '../../NeckWarningModal';
|
||||
import { TrackerSelectionMenu } from './TrackerSelectionMenu';
|
||||
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
|
||||
import { SkipSetupButton } from '../../SkipSetupButton';
|
||||
import { useConfig } from '../../../../hooks/config';
|
||||
import { playTapSetupSound } from '../../../../sounds/sounds';
|
||||
import { useBreakpoint } from '../../../../hooks/breakpoint';
|
||||
@@ -48,7 +46,7 @@ export function TrackersAssignPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { useAssignedTrackers, trackers } = useTrackers();
|
||||
const { applyProgress, skipSetup, state } = useOnboarding();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
|
||||
|
||||
const { control, watch } = useForm<{ advanced: boolean }>({
|
||||
@@ -57,7 +55,6 @@ export function TrackersAssignPage() {
|
||||
const { advanced } = watch();
|
||||
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
|
||||
const assignedTrackers = useAssignedTrackers();
|
||||
const [skipWarning, setSkipWarning] = useState(false);
|
||||
|
||||
const { config } = useConfig();
|
||||
const [tapDetectionSettings, setTapDetectionSettings] = useState<Omit<
|
||||
@@ -255,13 +252,6 @@ export function TrackersAssignPage() {
|
||||
onClose={() => closeChokerWarning(true)}
|
||||
accept={() => closeChokerWarning(false)}
|
||||
></NeckWarningModal>
|
||||
<div className="relative mx-4 top-4">
|
||||
<SkipSetupButton
|
||||
visible={!state.alonePage}
|
||||
modalVisible={skipWarning}
|
||||
onClick={() => setSkipWarning(true)}
|
||||
></SkipSetupButton>
|
||||
</div>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full justify-center">
|
||||
<div className="flex flex-col w-full overflow-y-auto px-4 xs:items-center">
|
||||
<div className="flex mobile:flex-col md:gap-8 mobile:gap-4 mobile:pb-4">
|
||||
@@ -330,11 +320,6 @@ export function TrackersAssignPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SkipSetupWarningModal
|
||||
accept={skipSetup}
|
||||
onClose={() => setSkipWarning(false)}
|
||||
isOpen={skipWarning}
|
||||
></SkipSetupWarningModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function SettingSelectorMobile() {
|
||||
},
|
||||
];
|
||||
|
||||
const { control, watch, handleSubmit, setValue } = useForm<{
|
||||
const { control, watch, handleSubmit, setValue, getValues } = useForm<{
|
||||
link: string;
|
||||
}>({
|
||||
defaultValues: { link: links[0].value.url },
|
||||
@@ -65,6 +65,7 @@ export function SettingSelectorMobile() {
|
||||
<div className="fixed top-12 z-50 px-4 w-full">
|
||||
<Dropdown
|
||||
control={control}
|
||||
getValues={getValues}
|
||||
display="block"
|
||||
items={links.map(({ label, value: { url: value } }) => ({
|
||||
label,
|
||||
|
||||
@@ -17,9 +17,19 @@ export function SettingsPageLayout({
|
||||
if (!pageRef.current || !typedState || !typedState.scrollTo) {
|
||||
return;
|
||||
}
|
||||
const elem = pageRef.current.querySelector(`#${typedState.scrollTo}`);
|
||||
const elem = pageRef.current.querySelector(
|
||||
`#${typedState.scrollTo}`
|
||||
) as HTMLElement | null;
|
||||
if (elem) {
|
||||
elem.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
// stupid way of doing this, just get the closest overflow-y-auto
|
||||
// usually its just the parentElem
|
||||
const closestScroll = elem.closest(
|
||||
'.overflow-y-auto'
|
||||
) as HTMLElement | null;
|
||||
if (closestScroll) {
|
||||
// The 45 is just enough padding for making the scroll look perfect
|
||||
closestScroll.scroll({ top: elem.offsetTop - 45, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}, [state]);
|
||||
|
||||
|
||||
@@ -51,9 +51,10 @@ export function Serial() {
|
||||
|
||||
const [tryFactoryReset, setTryFactoryReset] = useState(false);
|
||||
|
||||
const { control, watch, handleSubmit, reset } = useForm<SerialForm>({
|
||||
defaultValues: { port: 'Auto' },
|
||||
});
|
||||
const { control, watch, handleSubmit, reset, getValues } =
|
||||
useForm<SerialForm>({
|
||||
defaultValues: { port: 'Auto' },
|
||||
});
|
||||
|
||||
const { port } = watch();
|
||||
|
||||
@@ -234,6 +235,7 @@ export function Serial() {
|
||||
{isMobile && (
|
||||
<Dropdown
|
||||
control={control}
|
||||
getValues={getValues}
|
||||
name="port"
|
||||
display="block"
|
||||
placeholder={l10n.getString(
|
||||
@@ -250,6 +252,7 @@ export function Serial() {
|
||||
{!isMobile && (
|
||||
<Dropdown
|
||||
control={control}
|
||||
getValues={getValues}
|
||||
name="port"
|
||||
display="fit"
|
||||
placeholder={l10n.getString('settings-serial-serial_select')}
|
||||
|
||||
@@ -64,7 +64,7 @@ export interface BoneState {
|
||||
type: BoneType.Single;
|
||||
bone: SkeletonBone;
|
||||
value: number;
|
||||
label: string;
|
||||
currentLabel: string;
|
||||
}
|
||||
|
||||
export interface GroupState {
|
||||
@@ -77,7 +77,7 @@ export interface GroupState {
|
||||
value: number;
|
||||
}[];
|
||||
value: number;
|
||||
label: string;
|
||||
currentLabel: string;
|
||||
index?: number;
|
||||
parentLabel: string;
|
||||
}
|
||||
@@ -87,6 +87,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
|
||||
case ProportionChangeType.Bone: {
|
||||
return {
|
||||
...action,
|
||||
currentLabel: action.label,
|
||||
type: BoneType.Single,
|
||||
};
|
||||
}
|
||||
@@ -94,6 +95,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
|
||||
case ProportionChangeType.Group: {
|
||||
return {
|
||||
...action,
|
||||
currentLabel: action.label,
|
||||
type: BoneType.Group,
|
||||
};
|
||||
}
|
||||
@@ -114,7 +116,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
|
||||
|
||||
case ProportionChangeType.Ratio: {
|
||||
if (state.type === BoneType.Single || state.index === undefined) {
|
||||
throw new Error(`Unexpected increase of bone ${state.label}`);
|
||||
throw new Error(`Unexpected increase of bone ${state.currentLabel}`);
|
||||
}
|
||||
|
||||
const newState: GroupState = JSON.parse(JSON.stringify(state));
|
||||
@@ -182,7 +184,12 @@ export interface GroupPartLabel {
|
||||
const BONE_MAPPING: Map<string, SkeletonBone[]> = new Map([
|
||||
[
|
||||
'skeleton_bone-torso_group',
|
||||
[SkeletonBone.CHEST, SkeletonBone.HIP, SkeletonBone.WAIST],
|
||||
[
|
||||
SkeletonBone.UPPER_CHEST,
|
||||
SkeletonBone.CHEST,
|
||||
SkeletonBone.HIP,
|
||||
SkeletonBone.WAIST,
|
||||
],
|
||||
],
|
||||
['skeleton_bone-leg_group', [SkeletonBone.UPPER_LEG, SkeletonBone.LOWER_LEG]],
|
||||
['skeleton_bone-arm_group', [SkeletonBone.UPPER_ARM, SkeletonBone.LOWER_ARM]],
|
||||
@@ -192,16 +199,16 @@ export const INVALID_BONE: BoneState = {
|
||||
type: BoneType.Single,
|
||||
bone: SkeletonBone.NONE,
|
||||
value: 0,
|
||||
label: 'invalid-bone',
|
||||
currentLabel: 'invalid-bone',
|
||||
};
|
||||
|
||||
export function useManualProportions(): [
|
||||
Label[],
|
||||
boolean,
|
||||
ProportionState,
|
||||
(change: ProportionChange) => void,
|
||||
(ratio: boolean) => void
|
||||
] {
|
||||
export function useManualProportions(): {
|
||||
bodyParts: Label[];
|
||||
ratioMode: boolean;
|
||||
state: ProportionState;
|
||||
dispatch: (change: ProportionChange) => void;
|
||||
setRatioMode: (ratio: boolean) => void;
|
||||
} {
|
||||
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
|
||||
const [config, setConfig] = useState<Omit<SkeletonConfigResponseT, 'pack'> | null>(
|
||||
null
|
||||
@@ -278,6 +285,7 @@ export function useManualProportions(): [
|
||||
useLayoutEffect(() => {
|
||||
dispatch({
|
||||
...INVALID_BONE,
|
||||
label: INVALID_BONE.currentLabel,
|
||||
type: ProportionChangeType.Bone,
|
||||
});
|
||||
}, [ratio]);
|
||||
@@ -315,7 +323,7 @@ export function useManualProportions(): [
|
||||
const part = bodyParts.find(
|
||||
(it) =>
|
||||
it.type === LabelType.Group &&
|
||||
(it.label === state.label || it.label === state.parentLabel)
|
||||
(it.label === state.currentLabel || it.label === state.parentLabel)
|
||||
) as GroupLabel | undefined;
|
||||
|
||||
// Check if we found the group we were looking for
|
||||
@@ -345,7 +353,7 @@ export function useManualProportions(): [
|
||||
setConfig(conf);
|
||||
}, [state]);
|
||||
|
||||
return [bodyParts, ratio, state, dispatch, setRatio];
|
||||
return { bodyParts, ratioMode: ratio, state, dispatch, setRatioMode: setRatio };
|
||||
}
|
||||
|
||||
function roundedStep(value: number, step: number, add: boolean): number {
|
||||
|
||||
@@ -65,7 +65,7 @@ export const langs = [
|
||||
key: 'ko',
|
||||
},
|
||||
{
|
||||
name: '🇳🇴 Norsk bokmål',
|
||||
name: '🇳🇴 Norsk bokmål',
|
||||
key: 'nb-NO',
|
||||
},
|
||||
{
|
||||
@@ -84,6 +84,10 @@ export const langs = [
|
||||
name: '🇷🇺 Русский',
|
||||
key: 'ru',
|
||||
},
|
||||
{
|
||||
name: '🇺🇦 Українська',
|
||||
key: 'uk',
|
||||
},
|
||||
{
|
||||
name: '🇻🇳 Tiếng Việt',
|
||||
key: 'vi',
|
||||
|
||||
@@ -257,6 +257,17 @@ body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
||||
@@ -230,6 +230,9 @@ const config = {
|
||||
'trans-flag': `linear-gradient(135deg, ${colors['trans-blue'][800]} 40%, ${colors['trans-blue'][700]} 40% 70%, ${colors['trans-blue'][600]} 70% 100%)`,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
checked: 'checked=true'
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
forms,
|
||||
@@ -250,6 +253,9 @@ const config = {
|
||||
'.text-standard-bold': textConfig(rem(12), 700),
|
||||
});
|
||||
}),
|
||||
plugin(function({ addVariant }) {
|
||||
addVariant('checked-hover', ['&:hover', '&[data-checked=true]'])
|
||||
})
|
||||
],
|
||||
} satisfies Config
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import dev.slimevr.autobone.errors.*
|
||||
import dev.slimevr.config.AutoBoneConfig
|
||||
import dev.slimevr.poseframeformat.PoseFrameIO
|
||||
import dev.slimevr.poseframeformat.PoseFrames
|
||||
import dev.slimevr.poseframeformat.player.TrackerFramesPlayer
|
||||
import dev.slimevr.tracking.processor.BoneType
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigManager
|
||||
@@ -32,6 +31,7 @@ class AutoBone(server: VRServer) {
|
||||
arrayOf(
|
||||
BoneType.HEAD,
|
||||
BoneType.NECK,
|
||||
BoneType.UPPER_CHEST,
|
||||
BoneType.CHEST,
|
||||
BoneType.WAIST,
|
||||
BoneType.HIP, // This now works when using body proportion error! It's not the
|
||||
@@ -44,6 +44,7 @@ class AutoBone(server: VRServer) {
|
||||
val heightOffsets = FastList(
|
||||
arrayOf(
|
||||
BoneType.NECK,
|
||||
BoneType.UPPER_CHEST,
|
||||
BoneType.CHEST,
|
||||
BoneType.WAIST,
|
||||
BoneType.HIP,
|
||||
@@ -68,14 +69,14 @@ class AutoBone(server: VRServer) {
|
||||
var heightError = HeightError()
|
||||
var positionError = PositionError()
|
||||
var positionOffsetError = PositionOffsetError()
|
||||
|
||||
// #endregion
|
||||
|
||||
private val rand = Random()
|
||||
|
||||
val config: AutoBoneConfig
|
||||
val globalConfig: AutoBoneConfig
|
||||
|
||||
init {
|
||||
config = server.configManager.vrConfig.autoBone
|
||||
globalConfig = server.configManager.vrConfig.autoBone
|
||||
this.server = server
|
||||
loadConfigValues()
|
||||
}
|
||||
@@ -87,6 +88,7 @@ class AutoBone(server: VRServer) {
|
||||
return when (bone) {
|
||||
BoneType.HEAD -> getOffset.apply(SkeletonConfigOffsets.HEAD)
|
||||
BoneType.NECK -> getOffset.apply(SkeletonConfigOffsets.NECK)
|
||||
BoneType.UPPER_CHEST -> getOffset.apply(SkeletonConfigOffsets.UPPER_CHEST)
|
||||
BoneType.CHEST -> getOffset.apply(SkeletonConfigOffsets.CHEST)
|
||||
BoneType.WAIST -> getOffset.apply(SkeletonConfigOffsets.WAIST)
|
||||
BoneType.HIP -> getOffset.apply(SkeletonConfigOffsets.HIP)
|
||||
@@ -112,7 +114,7 @@ class AutoBone(server: VRServer) {
|
||||
offsets.clear()
|
||||
|
||||
// Get current or default skeleton configs
|
||||
val skeleton = humanPoseManager
|
||||
val skeleton = server.humanPoseManager
|
||||
val getOffset: Function<SkeletonConfigOffsets, Float> =
|
||||
if (skeleton != null) {
|
||||
Function { key: SkeletonConfigOffsets -> skeleton.getOffset(key) }
|
||||
@@ -160,17 +162,6 @@ class AutoBone(server: VRServer) {
|
||||
return dot2 - dot1
|
||||
}
|
||||
|
||||
private val humanPoseManager: HumanPoseManager?
|
||||
/**
|
||||
* A simple utility method to get the [HumanSkeleton] from the
|
||||
* [VRServer]
|
||||
*
|
||||
* @return The [HumanSkeleton] associated with the [VRServer],
|
||||
* or null if there is none available
|
||||
* @see {@link VRServer}, {@link HumanSkeleton}
|
||||
*/
|
||||
get() = server.humanPoseManager
|
||||
|
||||
fun applyConfig(
|
||||
configConsumer: BiConsumer<SkeletonConfigOffsets, Float>,
|
||||
offsets: Map<BoneType, Float> = this.offsets,
|
||||
@@ -184,9 +175,14 @@ class AutoBone(server: VRServer) {
|
||||
if (neckOffset != null) {
|
||||
configConsumer.accept(SkeletonConfigOffsets.NECK, neckOffset)
|
||||
}
|
||||
val upperChestOffset = offsets[BoneType.UPPER_CHEST]
|
||||
val chestOffset = offsets[BoneType.CHEST]
|
||||
val waistOffset = offsets[BoneType.WAIST]
|
||||
val hipOffset = offsets[BoneType.HIP]
|
||||
if (upperChestOffset != null) {
|
||||
configConsumer
|
||||
.accept(SkeletonConfigOffsets.UPPER_CHEST, upperChestOffset)
|
||||
}
|
||||
if (chestOffset != null) {
|
||||
configConsumer
|
||||
.accept(SkeletonConfigOffsets.CHEST, chestOffset)
|
||||
@@ -246,7 +242,7 @@ class AutoBone(server: VRServer) {
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun applyAndSaveConfig(humanPoseManager: HumanPoseManager? = this.humanPoseManager): Boolean {
|
||||
fun applyAndSaveConfig(humanPoseManager: HumanPoseManager? = this.server.humanPoseManager): Boolean {
|
||||
if (humanPoseManager == null) return false
|
||||
if (!applyConfig(humanPoseManager)) return false
|
||||
humanPoseManager.saveConfig()
|
||||
@@ -310,10 +306,13 @@ class AutoBone(server: VRServer) {
|
||||
return length
|
||||
}
|
||||
|
||||
fun getTargetHeight(frames: PoseFrames): Float {
|
||||
fun calcTargetHmdHeight(
|
||||
frames: PoseFrames,
|
||||
config: AutoBoneConfig = globalConfig,
|
||||
): Float {
|
||||
val targetHeight: Float
|
||||
// Get the current skeleton from the server
|
||||
val humanPoseManager = humanPoseManager
|
||||
val humanPoseManager = server.humanPoseManager
|
||||
if (config.useSkeletonHeight && humanPoseManager != null) {
|
||||
// If there is a skeleton available, calculate the target height
|
||||
// from its configs
|
||||
@@ -344,240 +343,289 @@ class AutoBone(server: VRServer) {
|
||||
@Throws(AutoBoneException::class)
|
||||
fun processFrames(
|
||||
frames: PoseFrames,
|
||||
calcInitError: Boolean = true,
|
||||
targetHeight: Float = -1f,
|
||||
epochCallback: Consumer<Epoch?>?,
|
||||
config: AutoBoneConfig = globalConfig,
|
||||
epochCallback: Consumer<Epoch?>? = null,
|
||||
): AutoBoneResults {
|
||||
var targetHeight = targetHeight
|
||||
val frameCount = frames.maxFrameCount
|
||||
val frames1 = TrackerFramesPlayer(frames)
|
||||
val frames2 = TrackerFramesPlayer(frames)
|
||||
val trackers1 = frames1.trackers.toList()
|
||||
val trackers2 = frames2.trackers.toList()
|
||||
|
||||
// Load current values for adjustable configs
|
||||
loadConfigValues()
|
||||
|
||||
val skeleton1 = HumanPoseManager(
|
||||
trackers1
|
||||
)
|
||||
val skeleton2 = HumanPoseManager(
|
||||
trackers2
|
||||
)
|
||||
|
||||
// Load server configs into the skeleton
|
||||
skeleton1.loadFromConfig(server.configManager)
|
||||
skeleton2.loadFromConfig(server.configManager)
|
||||
// Disable leg tweaks, this will mess with the resulting positions
|
||||
skeleton1.setLegTweaksEnabled(false)
|
||||
skeleton2.setLegTweaksEnabled(false)
|
||||
|
||||
val intermediateOffsets = EnumMap(offsets)
|
||||
// If target height isn't specified, auto-detect
|
||||
if (targetHeight < 0f) {
|
||||
targetHeight = getTargetHeight(frames)
|
||||
// Set the target heights either from config or calculate them
|
||||
val targetHmdHeight = if (config.targetHmdHeight > 0f) {
|
||||
config.targetHmdHeight
|
||||
} else {
|
||||
calcTargetHmdHeight(frames, config)
|
||||
}
|
||||
val trainingStep = AutoBoneTrainingStep(
|
||||
targetHeight,
|
||||
skeleton1,
|
||||
skeleton2,
|
||||
frames,
|
||||
intermediateOffsets
|
||||
val targetFullHeight = if (config.targetFullHeight > 0f) {
|
||||
config.targetFullHeight
|
||||
} else {
|
||||
targetHmdHeight * BodyProportionError.eyeHeightToHeightRatio
|
||||
}
|
||||
|
||||
// Set up the current state, making all required players and setting up the
|
||||
// skeletons appropriately
|
||||
val trainingStep = AutoBoneStep(
|
||||
config = config,
|
||||
targetHmdHeight = targetHmdHeight,
|
||||
targetFullHeight = targetFullHeight,
|
||||
frames = frames,
|
||||
intermediateOffsets = EnumMap(offsets),
|
||||
epochCallback = epochCallback,
|
||||
serverConfig = server.configManager
|
||||
)
|
||||
|
||||
val errorStats = StatsCalculator()
|
||||
// Initialize the frame order randomizer with a repeatable seed
|
||||
rand.setSeed(config.randSeed)
|
||||
|
||||
// Epoch loop, each epoch is one full iteration over the full dataset
|
||||
for (epoch in (if (calcInitError) -1 else 0) until config.numEpochs) {
|
||||
val adjustRate = decayFunc(config.initialAdjustRate, config.adjustRateDecay, epoch)
|
||||
for (epoch in (if (config.calcInitError) -1 else 0) until config.numEpochs) {
|
||||
// Set the current epoch to process
|
||||
trainingStep.curEpoch = epoch
|
||||
|
||||
var randomFrameIndices: IntArray? = null
|
||||
if (config.randomizeFrameOrder) {
|
||||
randomFrameIndices = IntArray(frameCount)
|
||||
var zeroPos = -1
|
||||
for (i in 0 until frameCount) {
|
||||
var index = rand.nextInt(frameCount)
|
||||
if (i > 0) {
|
||||
while (index == zeroPos || randomFrameIndices[index] > 0) {
|
||||
index = rand.nextInt(frameCount)
|
||||
}
|
||||
} else {
|
||||
zeroPos = index
|
||||
}
|
||||
randomFrameIndices[index] = i
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the frames using a cursor and an offset for comparing
|
||||
// frames a certain number of frames apart
|
||||
var cursorOffset = config.minDataDistance
|
||||
while (cursorOffset <= config.maxDataDistance &&
|
||||
cursorOffset < frameCount
|
||||
) {
|
||||
var frameCursor = 0
|
||||
while (frameCursor < frameCount - cursorOffset) {
|
||||
val frameCursor2 = frameCursor + cursorOffset
|
||||
applyConfig(skeleton1)
|
||||
applyConfig(skeleton2)
|
||||
if (config.randomizeFrameOrder && randomFrameIndices != null) {
|
||||
trainingStep
|
||||
.setCursors(
|
||||
randomFrameIndices[frameCursor],
|
||||
randomFrameIndices[frameCursor2]
|
||||
)
|
||||
} else {
|
||||
trainingStep.setCursors(frameCursor, frameCursor2)
|
||||
}
|
||||
frames1.setCursors(trainingStep.cursor1)
|
||||
frames2.setCursors(trainingStep.cursor2)
|
||||
skeleton1.update()
|
||||
skeleton2.update()
|
||||
val totalLength = getLengthSum(offsets)
|
||||
val curHeight = sumSelectConfigs(heightOffsets, offsets)
|
||||
trainingStep.currentHeight = curHeight
|
||||
val errorDeriv = getErrorDeriv(trainingStep)
|
||||
val error = errorFunc(errorDeriv)
|
||||
|
||||
// In case of fire
|
||||
if (java.lang.Float.isNaN(error) || java.lang.Float.isInfinite(error)) {
|
||||
// Extinguish
|
||||
LogManager
|
||||
.warning(
|
||||
"[AutoBone] Error value is invalid, resetting variables to recover"
|
||||
)
|
||||
// Reset adjustable config values
|
||||
loadConfigValues()
|
||||
|
||||
// Reset error sum values
|
||||
errorStats.reset()
|
||||
frameCursor += config.cursorIncrement
|
||||
|
||||
// Continue on new data
|
||||
continue
|
||||
}
|
||||
|
||||
// Store the error count for logging purposes
|
||||
errorStats.addValue(errorDeriv)
|
||||
val adjustVal = error * adjustRate
|
||||
|
||||
// If there is no adjustment whatsoever, skip this
|
||||
if (adjustVal == 0f) {
|
||||
frameCursor += config.cursorIncrement
|
||||
continue
|
||||
}
|
||||
val slideLeft = skeleton2
|
||||
.getComputedTracker(TrackerRole.LEFT_FOOT).position
|
||||
-skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
|
||||
|
||||
val slideRight = skeleton2
|
||||
.getComputedTracker(TrackerRole.RIGHT_FOOT).position
|
||||
-skeleton1.getComputedTracker(TrackerRole.RIGHT_FOOT).position
|
||||
intermediateOffsets.putAll(offsets)
|
||||
for (entry in offsets.entries) {
|
||||
// Skip adjustment if the epoch is before starting (for
|
||||
// logging only)
|
||||
if (epoch < 0) {
|
||||
break
|
||||
}
|
||||
val originalLength = entry.value
|
||||
val isHeightVar = heightOffsets.contains(entry.key)
|
||||
val leftDotProduct = getDotProductDiff(
|
||||
skeleton1,
|
||||
skeleton2,
|
||||
entry.key,
|
||||
false,
|
||||
slideLeft
|
||||
)
|
||||
val rightDotProduct = getDotProductDiff(
|
||||
skeleton1,
|
||||
skeleton2,
|
||||
entry.key,
|
||||
true,
|
||||
slideRight
|
||||
)
|
||||
val dotLength = (
|
||||
originalLength
|
||||
* ((leftDotProduct + rightDotProduct) / 2f)
|
||||
)
|
||||
|
||||
// Scale by the ratio for smooth adjustment and more
|
||||
// stable results
|
||||
val curAdjustVal = adjustVal * -dotLength / totalLength
|
||||
val newLength = originalLength + curAdjustVal
|
||||
|
||||
// No small or negative numbers!!! Bad algorithm!
|
||||
if (newLength < 0.01f) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Apply new offset length
|
||||
intermediateOffsets[entry.key] = newLength
|
||||
applyConfig(skeleton1, intermediateOffsets)
|
||||
applyConfig(skeleton2, intermediateOffsets)
|
||||
|
||||
// Update the skeleton poses for the new offset length
|
||||
skeleton1.update()
|
||||
skeleton2.update()
|
||||
val newHeight = if (isHeightVar) curHeight + curAdjustVal else curHeight
|
||||
trainingStep.currentHeight = newHeight
|
||||
val newErrorDeriv = getErrorDeriv(trainingStep)
|
||||
if (newErrorDeriv < errorDeriv) {
|
||||
entry.setValue(newLength)
|
||||
}
|
||||
|
||||
// Reset the length to minimize bias in other variables,
|
||||
// it's applied later
|
||||
intermediateOffsets[entry.key] = originalLength
|
||||
applyConfig(skeleton1, intermediateOffsets)
|
||||
applyConfig(skeleton2, intermediateOffsets)
|
||||
}
|
||||
if (config.scaleEachStep) {
|
||||
val stepHeight = sumSelectConfigs(heightOffsets, offsets)
|
||||
if (stepHeight > 0f) {
|
||||
val stepHeightDiff = targetHeight - stepHeight
|
||||
for (entry in offsets.entries) {
|
||||
// Only height variables
|
||||
if (entry.key == BoneType.NECK ||
|
||||
!heightOffsets.contains(entry.key)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
val length = entry.value
|
||||
|
||||
// Multiply the diff by the length to height
|
||||
// ratio
|
||||
val adjVal = stepHeightDiff * (length / stepHeight)
|
||||
|
||||
// Scale the length to fit the target height
|
||||
entry.setValue((length + adjVal / 2f).coerceAtLeast(0.01f))
|
||||
}
|
||||
}
|
||||
}
|
||||
frameCursor += config.cursorIncrement
|
||||
}
|
||||
cursorOffset++
|
||||
}
|
||||
|
||||
// Calculate average error over the epoch
|
||||
if (epoch <= 0 || epoch >= config.numEpochs - 1 || (epoch + 1) % config.printEveryNumEpochs == 0) {
|
||||
LogManager
|
||||
.info(
|
||||
"[AutoBone] Epoch: ${epoch + 1}, Mean error: ${errorStats.mean} (SD ${errorStats.standardDeviation}), Adjust rate: $adjustRate"
|
||||
)
|
||||
}
|
||||
applyConfig(legacyConfigs)
|
||||
epochCallback?.accept(Epoch(epoch + 1, config.numEpochs, errorStats, legacyConfigs))
|
||||
// Process the epoch
|
||||
internalEpoch(trainingStep)
|
||||
}
|
||||
|
||||
val finalHeight = sumSelectConfigs(heightOffsets, offsets)
|
||||
LogManager
|
||||
.info(
|
||||
"[AutoBone] Target height: $targetHeight, New height: $finalHeight"
|
||||
"[AutoBone] Target height: ${trainingStep.targetHmdHeight}, New height: $finalHeight"
|
||||
)
|
||||
return AutoBoneResults(finalHeight, targetHeight, errorStats, legacyConfigs)
|
||||
|
||||
return AutoBoneResults(
|
||||
finalHeight,
|
||||
trainingStep.targetHmdHeight,
|
||||
trainingStep.errorStats,
|
||||
legacyConfigs
|
||||
)
|
||||
}
|
||||
|
||||
private fun internalEpoch(trainingStep: AutoBoneStep) {
|
||||
// Pull frequently used variables out of trainingStep to reduce call length
|
||||
val config = trainingStep.config
|
||||
val frameCount = trainingStep.maxFrameCount
|
||||
val errorStats = trainingStep.errorStats
|
||||
val epoch = trainingStep.curEpoch
|
||||
|
||||
// Set the current adjust rate based on the current epoch
|
||||
trainingStep.curAdjustRate = decayFunc(config.initialAdjustRate, config.adjustRateDecay, epoch)
|
||||
|
||||
var randomFrameIndices: IntArray? = null
|
||||
if (config.randomizeFrameOrder) {
|
||||
randomFrameIndices = IntArray(frameCount)
|
||||
var zeroPos = -1
|
||||
for (i in 0 until frameCount) {
|
||||
var index = rand.nextInt(frameCount)
|
||||
if (i > 0) {
|
||||
while (index == zeroPos || randomFrameIndices[index] > 0) {
|
||||
index = rand.nextInt(frameCount)
|
||||
}
|
||||
} else {
|
||||
zeroPos = index
|
||||
}
|
||||
randomFrameIndices[index] = i
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the frames using a cursor and an offset for comparing
|
||||
// frames a certain number of frames apart
|
||||
var cursorOffset = config.minDataDistance
|
||||
while (cursorOffset <= config.maxDataDistance &&
|
||||
cursorOffset < frameCount
|
||||
) {
|
||||
var frameCursor = 0
|
||||
while (frameCursor < frameCount - cursorOffset) {
|
||||
val frameCursor2 = frameCursor + cursorOffset
|
||||
|
||||
// Apply the current adjusted config to both skeletons
|
||||
applyConfig(trainingStep.skeleton1)
|
||||
applyConfig(trainingStep.skeleton2)
|
||||
|
||||
// Then set the frame cursors and apply them to both skeletons
|
||||
if (config.randomizeFrameOrder && randomFrameIndices != null) {
|
||||
trainingStep
|
||||
.setCursors(
|
||||
randomFrameIndices[frameCursor],
|
||||
randomFrameIndices[frameCursor2],
|
||||
updatePlayerCursors = true
|
||||
)
|
||||
} else {
|
||||
trainingStep.setCursors(
|
||||
frameCursor,
|
||||
frameCursor2,
|
||||
updatePlayerCursors = true
|
||||
)
|
||||
}
|
||||
|
||||
// Process the iteration
|
||||
internalIter(trainingStep)
|
||||
|
||||
// Move on to the next iteration
|
||||
frameCursor += config.cursorIncrement
|
||||
}
|
||||
cursorOffset++
|
||||
}
|
||||
|
||||
// Calculate average error over the epoch
|
||||
if (epoch <= 0 || epoch >= config.numEpochs - 1 || (epoch + 1) % config.printEveryNumEpochs == 0) {
|
||||
LogManager
|
||||
.info(
|
||||
"[AutoBone] Epoch: ${epoch + 1}, Mean error: ${errorStats.mean} (SD ${errorStats.standardDeviation}), Adjust rate: ${trainingStep.curAdjustRate}"
|
||||
)
|
||||
}
|
||||
|
||||
// Convert current adjusted config to the legacy config format for the epoch
|
||||
// callback, then call it
|
||||
applyConfig(legacyConfigs)
|
||||
trainingStep.epochCallback?.accept(Epoch(epoch + 1, config.numEpochs, errorStats, legacyConfigs))
|
||||
}
|
||||
|
||||
private fun internalIter(trainingStep: AutoBoneStep) {
|
||||
// Pull frequently used variables out of trainingStep to reduce call length
|
||||
val skeleton1 = trainingStep.skeleton1
|
||||
val skeleton2 = trainingStep.skeleton2
|
||||
val intermediateOffsets = trainingStep.intermediateOffsets
|
||||
|
||||
val totalLength = getLengthSum(offsets)
|
||||
val curHeight = sumSelectConfigs(heightOffsets, offsets)
|
||||
trainingStep.currentHmdHeight = curHeight
|
||||
|
||||
val errorDeriv = getErrorDeriv(trainingStep)
|
||||
val error = errorFunc(errorDeriv)
|
||||
|
||||
// In case of fire
|
||||
if (java.lang.Float.isNaN(error) || java.lang.Float.isInfinite(error)) {
|
||||
// Extinguish
|
||||
LogManager
|
||||
.warning(
|
||||
"[AutoBone] Error value is invalid, resetting variables to recover"
|
||||
)
|
||||
// Reset adjustable config values
|
||||
loadConfigValues()
|
||||
|
||||
// Reset error sum values
|
||||
trainingStep.errorStats.reset()
|
||||
|
||||
// Continue on new data
|
||||
return
|
||||
}
|
||||
|
||||
// Store the error count for logging purposes
|
||||
trainingStep.errorStats.addValue(errorDeriv)
|
||||
val adjustVal = error * trainingStep.curAdjustRate
|
||||
|
||||
// If there is no adjustment whatsoever, skip this
|
||||
if (adjustVal == 0f) {
|
||||
return
|
||||
}
|
||||
|
||||
val slideLeft = skeleton2
|
||||
.getComputedTracker(TrackerRole.LEFT_FOOT).position -
|
||||
skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
|
||||
|
||||
val slideRight = skeleton2
|
||||
.getComputedTracker(TrackerRole.RIGHT_FOOT).position -
|
||||
skeleton1.getComputedTracker(TrackerRole.RIGHT_FOOT).position
|
||||
|
||||
// Load the current offsets into the working offsets holder
|
||||
intermediateOffsets.putAll(offsets)
|
||||
|
||||
for (entry in offsets.entries) {
|
||||
// Skip adjustment if the epoch is before starting (for
|
||||
// logging only)
|
||||
if (trainingStep.curEpoch < 0) {
|
||||
break
|
||||
}
|
||||
val originalLength = entry.value
|
||||
|
||||
val leftDotProduct = getDotProductDiff(
|
||||
skeleton1,
|
||||
skeleton2,
|
||||
entry.key,
|
||||
false,
|
||||
slideLeft
|
||||
)
|
||||
val rightDotProduct = getDotProductDiff(
|
||||
skeleton1,
|
||||
skeleton2,
|
||||
entry.key,
|
||||
true,
|
||||
slideRight
|
||||
)
|
||||
|
||||
// Calculate the total effect of the bone based on change in rotation
|
||||
val dotLength = (
|
||||
originalLength
|
||||
* ((leftDotProduct + rightDotProduct) / 2f)
|
||||
)
|
||||
|
||||
// Scale by the ratio for smooth adjustment and more
|
||||
// stable results
|
||||
val curAdjustVal = adjustVal * -dotLength / totalLength
|
||||
val newLength = originalLength + curAdjustVal
|
||||
|
||||
// No small or negative numbers!!! Bad algorithm!
|
||||
if (newLength < 0.01f) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Apply new offset length
|
||||
intermediateOffsets[entry.key] = newLength
|
||||
applyConfig(skeleton1, intermediateOffsets)
|
||||
applyConfig(skeleton2, intermediateOffsets)
|
||||
|
||||
// Update the skeleton poses for the new offset length
|
||||
skeleton1.update()
|
||||
skeleton2.update()
|
||||
|
||||
val newErrorDeriv = getErrorDeriv(trainingStep)
|
||||
if (newErrorDeriv < errorDeriv) {
|
||||
// Apply the adjusted length to the current adjusted offsets
|
||||
entry.setValue(newLength)
|
||||
}
|
||||
|
||||
// Reset the length to minimize bias in other variables,
|
||||
// it's applied later
|
||||
intermediateOffsets[entry.key] = originalLength
|
||||
applyConfig(skeleton1, intermediateOffsets)
|
||||
applyConfig(skeleton2, intermediateOffsets)
|
||||
}
|
||||
|
||||
if (trainingStep.config.scaleEachStep) {
|
||||
// Scale to the target height if requested by the config
|
||||
scaleToTargetHeight(trainingStep)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scaleToTargetHeight(trainingStep: AutoBoneStep) {
|
||||
val stepHeight = sumSelectConfigs(heightOffsets, offsets)
|
||||
if (stepHeight > 0f) {
|
||||
val stepHeightDiff = trainingStep.targetHmdHeight - stepHeight
|
||||
for (entry in offsets.entries) {
|
||||
// Only height variables
|
||||
if (entry.key == BoneType.NECK ||
|
||||
!heightOffsets.contains(entry.key)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
val length = entry.value
|
||||
|
||||
// Multiply the diff by the length to height
|
||||
// ratio
|
||||
val adjVal = stepHeightDiff * (length / stepHeight)
|
||||
|
||||
// Scale the length to fit the target height
|
||||
entry.setValue(
|
||||
(length + adjVal / 2f).coerceAtLeast(
|
||||
0.01f
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(AutoBoneException::class)
|
||||
protected fun getErrorDeriv(trainingStep: AutoBoneTrainingStep): Float {
|
||||
private fun getErrorDeriv(trainingStep: AutoBoneStep): Float {
|
||||
val config = trainingStep.config
|
||||
var sumError = 0f
|
||||
if (config.slideErrorFactor > 0f) {
|
||||
sumError += slideError.getStepError(trainingStep) * config.slideErrorFactor
|
||||
|
||||
@@ -85,9 +85,7 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
private fun processFrames(frames: PoseFrames): AutoBoneResults {
|
||||
return autoBone
|
||||
.processFrames(
|
||||
frames,
|
||||
autoBone.config.calcInitError,
|
||||
autoBone.config.targetHeight
|
||||
frames
|
||||
) { epoch: Epoch? -> listeners.forEach(Consumer { listener: AutoBoneListener -> listener.onAutoBoneEpoch(epoch!!) }) }
|
||||
}
|
||||
|
||||
@@ -120,8 +118,8 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
announceProcessStatus(AutoBoneProcessType.RECORD, "Recording...")
|
||||
|
||||
// 1000 samples at 20 ms per sample is 20 seconds
|
||||
val sampleCount = autoBone.config.sampleCount
|
||||
val sampleRate = autoBone.config.sampleRateMs
|
||||
val sampleCount = autoBone.globalConfig.sampleCount
|
||||
val sampleRate = autoBone.globalConfig.sampleRateMs
|
||||
val framesFuture = poseRecorder
|
||||
.startFrameRecording(
|
||||
sampleCount,
|
||||
@@ -140,7 +138,7 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
// Save a recurring recording for users to send as debug info
|
||||
announceProcessStatus(AutoBoneProcessType.RECORD, "Saving recording...")
|
||||
autoBone.saveRecording(frames, "LastABRecording.pfr")
|
||||
if (autoBone.config.saveRecordings) {
|
||||
if (autoBone.globalConfig.saveRecordings) {
|
||||
announceProcessStatus(
|
||||
AutoBoneProcessType.RECORD,
|
||||
"Saving recording (from config option)..."
|
||||
@@ -323,13 +321,15 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
skeletonConfigManagerBuffer.setOffsets(autoBoneResults.configValues)
|
||||
val neckLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.NECK)
|
||||
val upperChestLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.UPPER_CHEST)
|
||||
val chestLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.CHEST)
|
||||
val waistLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.WAIST)
|
||||
val hipLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.HIP)
|
||||
val torsoLength = chestLength + waistLength + hipLength
|
||||
val torsoLength = upperChestLength + chestLength + waistLength + hipLength
|
||||
val hipWidth = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.HIPS_WIDTH)
|
||||
val legLength = (
|
||||
@@ -341,7 +341,7 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
val lowerLegLength = skeletonConfigManagerBuffer
|
||||
.getOffset(SkeletonConfigOffsets.LOWER_LEG)
|
||||
val neckTorso = neckLength / torsoLength
|
||||
val chestTorso = chestLength / torsoLength
|
||||
val chestTorso = (upperChestLength + chestLength) / torsoLength
|
||||
val torsoWaist = hipWidth / torsoLength
|
||||
val legTorso = legLength / torsoLength
|
||||
val legBody = legLength / (torsoLength + neckLength)
|
||||
|
||||
67
server/src/main/java/dev/slimevr/autobone/AutoBoneStep.kt
Normal file
67
server/src/main/java/dev/slimevr/autobone/AutoBoneStep.kt
Normal file
@@ -0,0 +1,67 @@
|
||||
package dev.slimevr.autobone
|
||||
|
||||
import dev.slimevr.config.AutoBoneConfig
|
||||
import dev.slimevr.config.ConfigManager
|
||||
import dev.slimevr.poseframeformat.PoseFrames
|
||||
import dev.slimevr.poseframeformat.player.TrackerFramesPlayer
|
||||
import dev.slimevr.tracking.processor.BoneType
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
|
||||
class AutoBoneStep(
|
||||
val config: AutoBoneConfig,
|
||||
val targetHmdHeight: Float,
|
||||
val targetFullHeight: Float,
|
||||
val frames: PoseFrames,
|
||||
val intermediateOffsets: EnumMap<BoneType, Float>,
|
||||
val epochCallback: Consumer<AutoBone.Epoch?>?,
|
||||
serverConfig: ConfigManager,
|
||||
var curEpoch: Int = 0,
|
||||
var curAdjustRate: Float = 0f,
|
||||
var cursor1: Int = 0,
|
||||
var cursor2: Int = 0,
|
||||
var currentHmdHeight: Float = 0f,
|
||||
) {
|
||||
|
||||
val maxFrameCount = frames.maxFrameCount
|
||||
|
||||
val framePlayer1 = TrackerFramesPlayer(frames)
|
||||
val framePlayer2 = TrackerFramesPlayer(frames)
|
||||
|
||||
val trackers1 = framePlayer1.trackers.toList()
|
||||
val trackers2 = framePlayer2.trackers.toList()
|
||||
|
||||
val skeleton1 = HumanPoseManager(trackers1)
|
||||
val skeleton2 = HumanPoseManager(trackers2)
|
||||
|
||||
val errorStats = StatsCalculator()
|
||||
|
||||
init {
|
||||
// Load server configs into the skeleton
|
||||
skeleton1.loadFromConfig(serverConfig)
|
||||
skeleton2.loadFromConfig(serverConfig)
|
||||
// Disable leg tweaks, this will mess with the resulting positions
|
||||
skeleton1.setLegTweaksEnabled(false)
|
||||
skeleton2.setLegTweaksEnabled(false)
|
||||
}
|
||||
|
||||
fun setCursors(cursor1: Int, cursor2: Int, updatePlayerCursors: Boolean) {
|
||||
this.cursor1 = cursor1
|
||||
this.cursor2 = cursor2
|
||||
|
||||
if (updatePlayerCursors) {
|
||||
updatePlayerCursors()
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePlayerCursors() {
|
||||
framePlayer1.setCursors(cursor1)
|
||||
framePlayer2.setCursors(cursor2)
|
||||
skeleton1.update()
|
||||
skeleton2.update()
|
||||
}
|
||||
|
||||
val heightOffset: Float
|
||||
get() = targetHmdHeight - currentHmdHeight
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package dev.slimevr.autobone
|
||||
|
||||
import dev.slimevr.poseframeformat.PoseFrames
|
||||
import dev.slimevr.tracking.processor.BoneType
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
|
||||
class AutoBoneTrainingStep(
|
||||
val targetHeight: Float,
|
||||
val humanPoseManager1: HumanPoseManager,
|
||||
val humanPoseManager2: HumanPoseManager,
|
||||
val trainingFrames: PoseFrames,
|
||||
val intermediateOffsets: Map<BoneType, Float>,
|
||||
var cursor1: Int = 0,
|
||||
var cursor2: Int = 0,
|
||||
var currentHeight: Float = 0f,
|
||||
) {
|
||||
|
||||
fun setCursors(cursor1: Int, cursor2: Int) {
|
||||
this.cursor1 = cursor1
|
||||
this.cursor2 = cursor2
|
||||
}
|
||||
|
||||
val heightOffset: Float
|
||||
get() = targetHeight - currentHeight
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.autobone.errors.proportions.ProportionLimiter
|
||||
import dev.slimevr.autobone.errors.proportions.RangeProportionLimiter
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
@@ -10,10 +10,10 @@ import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets
|
||||
// The distance from average human proportions
|
||||
class BodyProportionError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
return getBodyProportionError(
|
||||
trainingStep.humanPoseManager1,
|
||||
trainingStep.currentHeight
|
||||
trainingStep.skeleton1,
|
||||
trainingStep.currentHmdHeight
|
||||
)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ class BodyProportionError : IAutoBoneError {
|
||||
// Full Height: 1.58 / 0.936 = 1.688034
|
||||
// Neck: 0.1 / 1.688034 = 0.059241
|
||||
// Torso: 0.56 / 1.688034 = 0.331747
|
||||
// Chest: 0.32 / 1.688034 = 0.18957
|
||||
// Upper Chest: 0.16 / 1.688034 = 0.094784
|
||||
// Chest: 0.16 / 1.688034 = 0.094784
|
||||
// Waist: (0.56 - 0.32 - 0.04) / 1.688034 = 0.118481
|
||||
// Hip: 0.04 / 1.688034 = 0.023696
|
||||
// Hip Width: 0.26 / 1.688034 = 0.154025
|
||||
@@ -63,12 +64,19 @@ class BodyProportionError : IAutoBoneError {
|
||||
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.NECK) },
|
||||
0.0015f
|
||||
),
|
||||
// Chest
|
||||
// Experimental: 0.189
|
||||
// Upper Chest
|
||||
// Experimental: 0.0945
|
||||
RangeProportionLimiter(
|
||||
0.189f,
|
||||
0.0945f,
|
||||
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.UPPER_CHEST) },
|
||||
0.01f
|
||||
),
|
||||
// Chest
|
||||
// Experimental: 0.0945
|
||||
RangeProportionLimiter(
|
||||
0.0945f,
|
||||
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.CHEST) },
|
||||
0.02f
|
||||
0.01f
|
||||
),
|
||||
// Waist
|
||||
// Experimental: 0.118
|
||||
@@ -114,12 +122,13 @@ class BodyProportionError : IAutoBoneError {
|
||||
return when (offset) {
|
||||
SkeletonConfigOffsets.HEAD -> proportionLimits[0]
|
||||
SkeletonConfigOffsets.NECK -> proportionLimits[1]
|
||||
SkeletonConfigOffsets.CHEST -> proportionLimits[2]
|
||||
SkeletonConfigOffsets.WAIST -> proportionLimits[3]
|
||||
SkeletonConfigOffsets.HIP -> proportionLimits[4]
|
||||
SkeletonConfigOffsets.HIPS_WIDTH -> proportionLimits[5]
|
||||
SkeletonConfigOffsets.UPPER_LEG -> proportionLimits[6]
|
||||
SkeletonConfigOffsets.LOWER_LEG -> proportionLimits[7]
|
||||
SkeletonConfigOffsets.UPPER_CHEST -> proportionLimits[2]
|
||||
SkeletonConfigOffsets.CHEST -> proportionLimits[3]
|
||||
SkeletonConfigOffsets.WAIST -> proportionLimits[4]
|
||||
SkeletonConfigOffsets.HIP -> proportionLimits[5]
|
||||
SkeletonConfigOffsets.HIPS_WIDTH -> proportionLimits[6]
|
||||
SkeletonConfigOffsets.UPPER_LEG -> proportionLimits[7]
|
||||
SkeletonConfigOffsets.LOWER_LEG -> proportionLimits[8]
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
@@ -9,10 +9,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
|
||||
// The offset between the height both feet at one instant and over time
|
||||
class FootHeightOffsetError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
return getSlideError(
|
||||
trainingStep.humanPoseManager1.skeleton,
|
||||
trainingStep.humanPoseManager2.skeleton
|
||||
trainingStep.skeleton1.skeleton,
|
||||
trainingStep.skeleton2.skeleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
|
||||
// The difference from the current height to the target height
|
||||
class HeightError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
return getHeightError(
|
||||
trainingStep.currentHeight,
|
||||
trainingStep.targetHeight
|
||||
trainingStep.currentHmdHeight,
|
||||
trainingStep.targetHmdHeight
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
|
||||
interface IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
fun getStepError(trainingStep: AutoBoneTrainingStep): Float
|
||||
fun getStepError(trainingStep: AutoBoneStep): Float
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
@@ -9,10 +9,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
|
||||
// The change in distance between both of the ankles over time
|
||||
class OffsetSlideError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
return getSlideError(
|
||||
trainingStep.humanPoseManager1.skeleton,
|
||||
trainingStep.humanPoseManager2.skeleton
|
||||
trainingStep.skeleton1.skeleton,
|
||||
trainingStep.skeleton2.skeleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.poseframeformat.trackerdata.TrackerFrames
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
|
||||
// The distance of any points to the corresponding absolute position
|
||||
class PositionError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
val trackers = trainingStep.trainingFrames.frameHolders
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
val trackers = trainingStep.frames.frameHolders
|
||||
return (
|
||||
(
|
||||
getPositionError(
|
||||
trackers,
|
||||
trainingStep.cursor1,
|
||||
trainingStep.humanPoseManager1.skeleton
|
||||
trainingStep.skeleton1.skeleton
|
||||
) +
|
||||
getPositionError(
|
||||
trackers,
|
||||
trainingStep.cursor2,
|
||||
trainingStep.humanPoseManager2.skeleton
|
||||
trainingStep.skeleton2.skeleton
|
||||
)
|
||||
) /
|
||||
2f
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.poseframeformat.trackerdata.TrackerFrames
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
|
||||
// The difference between offset of absolute position and the corresponding point over time
|
||||
class PositionOffsetError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
val trackers = trainingStep.trainingFrames.frameHolders
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
val trackers = trainingStep.frames.frameHolders
|
||||
return getPositionOffsetError(
|
||||
trackers,
|
||||
trainingStep.cursor1,
|
||||
trainingStep.cursor2,
|
||||
trainingStep.humanPoseManager1.skeleton,
|
||||
trainingStep.humanPoseManager2.skeleton
|
||||
trainingStep.skeleton1.skeleton,
|
||||
trainingStep.skeleton2.skeleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.slimevr.autobone.errors
|
||||
|
||||
import dev.slimevr.autobone.AutoBoneTrainingStep
|
||||
import dev.slimevr.autobone.AutoBoneStep
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
@@ -8,10 +8,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
|
||||
// The change in position of the ankle over time
|
||||
class SlideError : IAutoBoneError {
|
||||
@Throws(AutoBoneException::class)
|
||||
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
|
||||
override fun getStepError(trainingStep: AutoBoneStep): Float {
|
||||
return getSlideError(
|
||||
trainingStep.humanPoseManager1.skeleton,
|
||||
trainingStep.humanPoseManager2.skeleton
|
||||
trainingStep.skeleton1.skeleton,
|
||||
trainingStep.skeleton2.skeleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,13 @@ class AutoBoneConfig {
|
||||
var positionErrorFactor = 0.0f
|
||||
var positionOffsetErrorFactor = 0.0f
|
||||
var calcInitError = false
|
||||
var targetHeight = -1f
|
||||
var targetHmdHeight = -1f
|
||||
var targetFullHeight = -1f
|
||||
var randomizeFrameOrder = true
|
||||
var scaleEachStep = true
|
||||
var sampleCount = 1000
|
||||
var sampleRateMs: Long = 20
|
||||
var saveRecordings = false
|
||||
var useSkeletonHeight = false
|
||||
var randSeed = 4L
|
||||
}
|
||||
|
||||
@@ -224,6 +224,25 @@ public class CurrentVRConfigConverter implements VersionedModelConverter {
|
||||
tapDetectionNode.set("fullResetTaps", tapDetectionNode.get("resetTaps"));
|
||||
}
|
||||
}
|
||||
if (version < 9) {
|
||||
// split chest into 2 offsets
|
||||
ObjectNode skeletonNode = (ObjectNode) modelData.get("skeleton");
|
||||
if (skeletonNode != null) {
|
||||
ObjectNode offsetsNode = (ObjectNode) skeletonNode.get("offsets");
|
||||
if (offsetsNode != null) {
|
||||
JsonNode chestNode = offsetsNode.get("chestLength");
|
||||
if (chestNode != null) {
|
||||
offsetsNode
|
||||
.set("chestLength", new FloatNode(chestNode.floatValue() / 2f));
|
||||
offsetsNode
|
||||
.set(
|
||||
"upperChestLength",
|
||||
new FloatNode(chestNode.floatValue() / 2f)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogManager.severe("Error during config migration: " + e);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
|
||||
|
||||
@JsonVersionedModel(
|
||||
currentVersion = "8", defaultDeserializeToVersion = "8", toCurrentConverterClass = CurrentVRConfigConverter.class
|
||||
currentVersion = "9", defaultDeserializeToVersion = "9", toCurrentConverterClass = CurrentVRConfigConverter.class
|
||||
)
|
||||
public class VRConfig {
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ class UnityArmature(localRot: Boolean) {
|
||||
}
|
||||
|
||||
fun updateNodes() {
|
||||
// Set the upper chest's rotation to the chest's
|
||||
// Set the upper chest node's rotation to the chest's
|
||||
upperChestNode.localTransform.rotation = chestNode.localTransform.rotation
|
||||
// Update the root node
|
||||
hipNode.update()
|
||||
|
||||
@@ -11,7 +11,6 @@ import dev.slimevr.platform.SteamVRBridge;
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition;
|
||||
import dev.slimevr.tracking.trackers.TrackerRole;
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus;
|
||||
import io.eiren.util.collections.FastList;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
@@ -298,7 +297,7 @@ public class VRCOSCHandler implements OSCHandler {
|
||||
public void yawAlign() {
|
||||
if (oscSender != null && oscSender.isConnected()) {
|
||||
for (Tracker shareableTracker : computedTrackers) {
|
||||
if (shareableTracker.getTrackerPosition().getTrackerRole() == TrackerRole.HEAD) {
|
||||
if (shareableTracker.getTrackerPosition() == TrackerPosition.HEAD) {
|
||||
var hmdAngles = shareableTracker.getRotation().toEulerAngles(EulerOrder.XYZ);
|
||||
oscArgs.clear();
|
||||
oscArgs.add(0f);
|
||||
|
||||
@@ -4,13 +4,10 @@ import dev.slimevr.Main;
|
||||
import dev.slimevr.VRServer;
|
||||
import dev.slimevr.bridge.ProtobufBridge;
|
||||
import dev.slimevr.bridge.ProtobufMessages;
|
||||
import dev.slimevr.bridge.ProtobufMessages.*;
|
||||
import dev.slimevr.bridge.ProtobufMessages.Battery;
|
||||
import dev.slimevr.bridge.ProtobufMessages.ProtobufMessage;
|
||||
import dev.slimevr.config.BridgeConfig;
|
||||
import dev.slimevr.tracking.trackers.Device;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition;
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils;
|
||||
import dev.slimevr.tracking.trackers.TrackerRole;
|
||||
import dev.slimevr.tracking.trackers.*;
|
||||
import dev.slimevr.util.ann.VRServerThread;
|
||||
import solarxr_protocol.rpc.StatusData;
|
||||
import solarxr_protocol.rpc.StatusDataUnion;
|
||||
@@ -181,7 +178,7 @@ public abstract class SteamVRBridge extends ProtobufBridge implements Runnable {
|
||||
tertiaryTracker = TrackerUtils
|
||||
.getNonInternalTrackerForBodyPosition(
|
||||
allTrackers,
|
||||
TrackerPosition.CHEST
|
||||
TrackerPosition.UPPER_CHEST
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -59,7 +59,7 @@ class PlayerTracker(val trackerFrames: TrackerFrames, val tracker: Tracker, priv
|
||||
|
||||
val acceleration = frame.tryGetAcceleration()
|
||||
if (acceleration != null) {
|
||||
tracker.acceleration = acceleration
|
||||
tracker.setAcceleration(acceleration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ data class TrackerFrame(
|
||||
|
||||
val rotation: Quaternion? = if (tracker.hasRotation) tracker.getRotation() else null
|
||||
val position: Vector3? = if (tracker.hasPosition) tracker.position else null
|
||||
val acceleration: Vector3? = if (tracker.hasAcceleration) tracker.acceleration else null
|
||||
val acceleration: Vector3? = if (tracker.hasAcceleration) tracker.getAcceleration() else null
|
||||
|
||||
var rawRotation: Quaternion? = if (tracker.hasAdjustedRotation) tracker.getRawRotation() else null
|
||||
// If the rawRotation is the same as rotation, there's no point in saving it, set it back to null
|
||||
|
||||
@@ -164,6 +164,8 @@ public class ProvisioningHandler implements SerialListener {
|
||||
|
||||
@Override
|
||||
public void onNewSerialDevice(SerialPort port) {
|
||||
if (!isRunning)
|
||||
return;
|
||||
this.initSerial(this.preferredPort);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ public enum BoneType {
|
||||
HEAD(BodyPart.HEAD),
|
||||
HEAD_TRACKER(),
|
||||
NECK(BodyPart.NECK),
|
||||
CHEST(BodyPart.CHEST),
|
||||
UPPER_CHEST(BodyPart.UPPER_CHEST),
|
||||
CHEST_TRACKER,
|
||||
CHEST(BodyPart.CHEST),
|
||||
WAIST(BodyPart.WAIST),
|
||||
HIP(BodyPart.HIP),
|
||||
HIP_TRACKER,
|
||||
|
||||
@@ -144,7 +144,7 @@ public class HumanPoseManager {
|
||||
VRServer.getNextLocalTrackerId(),
|
||||
"human://CHEST",
|
||||
"Computed chest",
|
||||
TrackerPosition.CHEST,
|
||||
TrackerPosition.UPPER_CHEST,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
|
||||
@@ -125,6 +125,7 @@ public class SkeletonConfigManager {
|
||||
|
||||
private float calculateUserHeight() {
|
||||
return getOffset(SkeletonConfigOffsets.NECK)
|
||||
+ getOffset(SkeletonConfigOffsets.UPPER_CHEST)
|
||||
+ getOffset(SkeletonConfigOffsets.CHEST)
|
||||
+ getOffset(SkeletonConfigOffsets.WAIST)
|
||||
+ getOffset(SkeletonConfigOffsets.HIP)
|
||||
@@ -208,14 +209,21 @@ public class SkeletonConfigManager {
|
||||
switch (nodeOffset) {
|
||||
case HEAD -> setNodeOffset(nodeOffset, 0, 0, getOffset(SkeletonConfigOffsets.HEAD));
|
||||
case NECK -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.NECK), 0);
|
||||
case CHEST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.CHEST), 0);
|
||||
case UPPER_CHEST -> setNodeOffset(
|
||||
nodeOffset,
|
||||
0,
|
||||
-getOffset(SkeletonConfigOffsets.UPPER_CHEST),
|
||||
0
|
||||
);
|
||||
case CHEST_TRACKER -> setNodeOffset(
|
||||
nodeOffset,
|
||||
0,
|
||||
-getOffset(SkeletonConfigOffsets.CHEST_OFFSET)
|
||||
- getOffset(SkeletonConfigOffsets.UPPER_CHEST)
|
||||
- getOffset(SkeletonConfigOffsets.CHEST),
|
||||
-getOffset(SkeletonConfigOffsets.SKELETON_OFFSET)
|
||||
);
|
||||
case CHEST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.CHEST), 0);
|
||||
case WAIST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.WAIST), 0);
|
||||
case HIP -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.HIP), 0);
|
||||
case HIP_TRACKER -> setNodeOffset(
|
||||
@@ -423,7 +431,7 @@ public class SkeletonConfigManager {
|
||||
}
|
||||
|
||||
switch (config) {
|
||||
case CHEST, WAIST, HIP, UPPER_LEG, LOWER_LEG -> {
|
||||
case UPPER_CHEST, CHEST, WAIST, HIP, UPPER_LEG, LOWER_LEG -> {
|
||||
float height = humanPoseManager.getHmdHeight()
|
||||
/ BodyProportionError.eyeHeightToHeightRatio;
|
||||
if (height > 0.5f) { // Reset only if floor level seems right,
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
|
||||
|
||||
public enum SkeletonConfigOffsets {
|
||||
// Note: GUI uses this order to sort (not ID)
|
||||
HEAD(
|
||||
1,
|
||||
"headShift",
|
||||
@@ -19,10 +20,16 @@ public enum SkeletonConfigOffsets {
|
||||
0.1f,
|
||||
new BoneType[] { BoneType.NECK }
|
||||
),
|
||||
UPPER_CHEST(
|
||||
21,
|
||||
"upperChestLength",
|
||||
0.16f,
|
||||
new BoneType[] { BoneType.UPPER_CHEST, BoneType.CHEST_TRACKER }
|
||||
),
|
||||
CHEST(
|
||||
3,
|
||||
"chestLength",
|
||||
0.32f,
|
||||
0.16f,
|
||||
new BoneType[] { BoneType.CHEST, BoneType.CHEST_TRACKER }
|
||||
),
|
||||
CHEST_OFFSET(
|
||||
@@ -150,7 +157,7 @@ public enum SkeletonConfigOffsets {
|
||||
float defaultValue,
|
||||
BoneType[] affectedOffsets
|
||||
) {
|
||||
this.id = id;
|
||||
this.id = id; // id of SkeletonBone in solarxr
|
||||
this.configKey = configKey;
|
||||
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
@@ -34,8 +34,9 @@ public class HumanSkeleton {
|
||||
protected final TransformNode headNode = new TransformNode(BoneType.HEAD, false);
|
||||
protected final TransformNode trackerHeadNode = new TransformNode(BoneType.HEAD_TRACKER, false);
|
||||
protected final TransformNode neckNode = new TransformNode(BoneType.NECK, false);
|
||||
protected final TransformNode chestNode = new TransformNode(BoneType.CHEST, false);
|
||||
protected final TransformNode upperChestNode = new TransformNode(BoneType.CHEST, false);
|
||||
protected final TransformNode trackerChestNode = new TransformNode(BoneType.CHEST_TRACKER, false);
|
||||
protected final TransformNode chestNode = new TransformNode(BoneType.CHEST, false);
|
||||
protected final TransformNode waistNode = new TransformNode(BoneType.WAIST, false);
|
||||
protected final TransformNode hipNode = new TransformNode(BoneType.HIP, false);
|
||||
protected final TransformNode trackerHipNode = new TransformNode(BoneType.HIP_TRACKER, false);
|
||||
@@ -89,6 +90,7 @@ public class HumanSkeleton {
|
||||
// #region Tracker Input
|
||||
protected Tracker headTracker;
|
||||
protected Tracker neckTracker;
|
||||
protected Tracker upperChestTracker;
|
||||
protected Tracker chestTracker;
|
||||
protected Tracker waistTracker;
|
||||
protected Tracker hipTracker;
|
||||
@@ -201,7 +203,8 @@ public class HumanSkeleton {
|
||||
// #region Assemble skeleton from head to hip
|
||||
hmdNode.attachChild(headNode);
|
||||
headNode.attachChild(neckNode);
|
||||
neckNode.attachChild(chestNode);
|
||||
neckNode.attachChild(upperChestNode);
|
||||
upperChestNode.attachChild(chestNode);
|
||||
chestNode.attachChild(waistNode);
|
||||
waistNode.attachChild(hipNode);
|
||||
// #endregion
|
||||
@@ -386,6 +389,11 @@ public class HumanSkeleton {
|
||||
trackers,
|
||||
TrackerPosition.NECK
|
||||
);
|
||||
upperChestTracker = TrackerUtils
|
||||
.getNonInternalTrackerForBodyPosition(
|
||||
trackers,
|
||||
TrackerPosition.UPPER_CHEST
|
||||
);
|
||||
chestTracker = TrackerUtils
|
||||
.getNonInternalTrackerForBodyPosition(
|
||||
trackers,
|
||||
@@ -473,7 +481,10 @@ public class HumanSkeleton {
|
||||
);
|
||||
|
||||
// Check for specific conditions and store them in booleans.
|
||||
hasSpineTracker = chestTracker != null || waistTracker != null || hipTracker != null;
|
||||
hasSpineTracker = upperChestTracker != null
|
||||
|| chestTracker != null
|
||||
|| waistTracker != null
|
||||
|| hipTracker != null;
|
||||
hasKneeTrackers = leftUpperLegTracker != null && rightUpperLegTracker != null;
|
||||
hasLeftLegTracker = leftUpperLegTracker != null
|
||||
|| leftLowerLegTracker != null
|
||||
@@ -497,7 +508,7 @@ public class HumanSkeleton {
|
||||
protected void setComputedTracker(Tracker tracker) {
|
||||
switch (tracker.getTrackerPosition()) {
|
||||
case HEAD -> computedHeadTracker = tracker;
|
||||
case CHEST -> computedChestTracker = tracker;
|
||||
case UPPER_CHEST -> computedChestTracker = tracker;
|
||||
case HIP -> computedHipTracker = tracker;
|
||||
case LEFT_UPPER_LEG -> computedLeftKneeTracker = tracker;
|
||||
case LEFT_FOOT -> computedLeftFootTracker = tracker;
|
||||
@@ -591,7 +602,12 @@ public class HumanSkeleton {
|
||||
headRot = neckTracker.getRotation();
|
||||
} else if (hasSpineTracker) {
|
||||
headRot = TrackerUtils
|
||||
.getFirstAvailableTracker(chestTracker, waistTracker, hipTracker)
|
||||
.getFirstAvailableTracker(
|
||||
upperChestTracker,
|
||||
chestTracker,
|
||||
waistTracker,
|
||||
hipTracker
|
||||
)
|
||||
.getRotation();
|
||||
}
|
||||
|
||||
@@ -607,19 +623,28 @@ public class HumanSkeleton {
|
||||
|
||||
// Spine
|
||||
if (hasSpineTracker) {
|
||||
// Upper chest
|
||||
Quaternion torsoRot = TrackerUtils
|
||||
.getFirstAvailableTracker(chestTracker, waistTracker, hipTracker)
|
||||
.getFirstAvailableTracker(upperChestTracker, chestTracker, waistTracker, hipTracker)
|
||||
.getRotation();
|
||||
neckNode.getLocalTransform().setRotation(torsoRot);
|
||||
trackerChestNode.getLocalTransform().setRotation(torsoRot);
|
||||
|
||||
// Chest
|
||||
torsoRot = TrackerUtils
|
||||
.getFirstAvailableTracker(waistTracker, chestTracker, hipTracker)
|
||||
.getFirstAvailableTracker(chestTracker, upperChestTracker, waistTracker, hipTracker)
|
||||
.getRotation();
|
||||
upperChestNode.getLocalTransform().setRotation(torsoRot);
|
||||
|
||||
// Waist
|
||||
torsoRot = TrackerUtils
|
||||
.getFirstAvailableTracker(waistTracker, chestTracker, upperChestTracker, hipTracker)
|
||||
.getRotation();
|
||||
chestNode.getLocalTransform().setRotation(torsoRot);
|
||||
|
||||
// Hip
|
||||
torsoRot = TrackerUtils
|
||||
.getFirstAvailableTracker(hipTracker, waistTracker, chestTracker)
|
||||
.getFirstAvailableTracker(hipTracker, waistTracker, chestTracker, upperChestTracker)
|
||||
.getRotation();
|
||||
waistNode.getLocalTransform().setRotation(torsoRot);
|
||||
hipNode.getLocalTransform().setRotation(torsoRot);
|
||||
@@ -630,6 +655,7 @@ public class HumanSkeleton {
|
||||
|
||||
neckNode.getLocalTransform().setRotation(yawQuat);
|
||||
trackerChestNode.getLocalTransform().setRotation(yawQuat);
|
||||
upperChestNode.getLocalTransform().setRotation(yawQuat);
|
||||
chestNode.getLocalTransform().setRotation(yawQuat);
|
||||
waistNode.getLocalTransform().setRotation(yawQuat);
|
||||
hipNode.getLocalTransform().setRotation(yawQuat);
|
||||
@@ -730,10 +756,12 @@ public class HumanSkeleton {
|
||||
// Tries to guess missing lower spine trackers by interpolating
|
||||
// rotations
|
||||
if (waistTracker == null) {
|
||||
if (chestTracker != null && hipTracker != null) {
|
||||
if ((upperChestTracker != null || chestTracker != null) && hipTracker != null) {
|
||||
// Calculates waist from chest + hip
|
||||
var hipRot = hipTracker.getRotation();
|
||||
var chestRot = chestTracker.getRotation();
|
||||
var chestRot = TrackerUtils
|
||||
.getFirstAvailableTracker(upperChestTracker, chestTracker)
|
||||
.getRotation();
|
||||
|
||||
// Get the rotation relative to where we expect the hip to
|
||||
// be
|
||||
@@ -745,11 +773,13 @@ public class HumanSkeleton {
|
||||
chestRot = chestRot.interpQ(hipRot, waistFromChestHipAveraging);
|
||||
|
||||
chestNode.getLocalTransform().setRotation(chestRot);
|
||||
} else if (chestTracker != null && hasKneeTrackers) {
|
||||
} else if ((upperChestTracker != null || chestTracker != null) && hasKneeTrackers) {
|
||||
// Calculates waist from chest + legs
|
||||
var leftHipRot = leftHipNode.getLocalTransform().getRotation();
|
||||
var rightHipRot = rightHipNode.getLocalTransform().getRotation();
|
||||
var chestRot = chestTracker.getRotation();
|
||||
var chestRot = TrackerUtils
|
||||
.getFirstAvailableTracker(upperChestTracker, chestTracker)
|
||||
.getRotation();
|
||||
|
||||
// Get the rotation relative to where we expect the
|
||||
// upper legs to be
|
||||
@@ -796,11 +826,13 @@ public class HumanSkeleton {
|
||||
waistNode.getLocalTransform().setRotation(waistRot);
|
||||
hipNode.getLocalTransform().setRotation(waistRot);
|
||||
trackerHipNode.getLocalTransform().setRotation(waistRot);
|
||||
} else if (chestTracker != null) {
|
||||
} else if (upperChestTracker != null || chestTracker != null) {
|
||||
// Calculates hip from chest + legs
|
||||
var leftHipRot = leftHipNode.getLocalTransform().getRotation();
|
||||
var rightHipRot = rightHipNode.getLocalTransform().getRotation();
|
||||
var chestRot = chestTracker.getRotation();
|
||||
var chestRot = TrackerUtils
|
||||
.getFirstAvailableTracker(upperChestTracker, chestTracker)
|
||||
.getRotation();
|
||||
|
||||
// Get the rotation relative to where we expect the
|
||||
// upper legs to be
|
||||
@@ -1118,8 +1150,9 @@ public class HumanSkeleton {
|
||||
}
|
||||
}
|
||||
case NECK -> neckNode.getLocalTransform().setTranslation(offset);
|
||||
case CHEST -> chestNode.getLocalTransform().setTranslation(offset);
|
||||
case UPPER_CHEST -> upperChestNode.getLocalTransform().setTranslation(offset);
|
||||
case CHEST_TRACKER -> trackerChestNode.getLocalTransform().setTranslation(offset);
|
||||
case CHEST -> chestNode.getLocalTransform().setTranslation(offset);
|
||||
case WAIST -> waistNode.getLocalTransform().setTranslation(offset);
|
||||
case HIP -> hipNode.getLocalTransform().setTranslation(offset);
|
||||
case HIP_TRACKER -> trackerHipNode.getLocalTransform().setTranslation(offset);
|
||||
@@ -1216,8 +1249,9 @@ public class HumanSkeleton {
|
||||
case HMD, HEAD -> headNode;
|
||||
case HEAD_TRACKER -> trackerHeadNode;
|
||||
case NECK -> neckNode;
|
||||
case CHEST -> chestNode;
|
||||
case UPPER_CHEST -> upperChestNode;
|
||||
case CHEST_TRACKER -> trackerChestNode;
|
||||
case CHEST -> chestNode;
|
||||
case WAIST -> waistNode;
|
||||
case HIP -> hipNode;
|
||||
case HIP_TRACKER -> trackerHipNode;
|
||||
@@ -1276,8 +1310,9 @@ public class HumanSkeleton {
|
||||
headNode,
|
||||
trackerHeadNode,
|
||||
neckNode,
|
||||
chestNode,
|
||||
upperChestNode,
|
||||
trackerChestNode,
|
||||
chestNode,
|
||||
waistNode,
|
||||
hipNode,
|
||||
trackerHipNode,
|
||||
|
||||
@@ -83,10 +83,9 @@ public class LegTweakBuffer {
|
||||
// hyperparameters
|
||||
public static final float SKATING_DISTANCE_CUTOFF = 0.5f;
|
||||
static float SKATING_VELOCITY_THRESHOLD = 2.4f;
|
||||
static float SKATING_ACCELERATION_THRESHOLD = 0.7f;
|
||||
static float SKATING_ACCELERATION_THRESHOLD = 0.8f;
|
||||
private static final float SKATING_ROTVELOCITY_THRESHOLD = 4.5f;
|
||||
private static final float SKATING_LOCK_ENGAGE_PERCENT = 0.85f;
|
||||
private static final float SKATING_ACCELERATION_Y_USE_PERCENT = 0.25f;
|
||||
private static final float FLOOR_DISTANCE_CUTOFF = 0.125f;
|
||||
private static final float SIX_TRACKER_TOLERANCE = -0.10f;
|
||||
private static final Vector3 FORCE_VECTOR_TO_PRESSURE = new Vector3(0.25f, 1.0f, 0.25f);
|
||||
@@ -523,17 +522,9 @@ public class LegTweakBuffer {
|
||||
// compute the acceleration magnitude of the feet from the acceleration
|
||||
// given by the imus (exclude y)
|
||||
private void computeAccelerationMagnitude() {
|
||||
leftFootAccelerationMagnitude = new Vector3(
|
||||
leftFootAcceleration.getX(),
|
||||
leftFootAcceleration.getY() * SKATING_ACCELERATION_Y_USE_PERCENT,
|
||||
leftFootAcceleration.getZ()
|
||||
).len();
|
||||
leftFootAccelerationMagnitude = leftFootAcceleration.len();
|
||||
|
||||
rightFootAccelerationMagnitude = new Vector3(
|
||||
rightFootAcceleration.getX(),
|
||||
rightFootAcceleration.getY() * SKATING_ACCELERATION_Y_USE_PERCENT,
|
||||
rightFootAcceleration.getZ()
|
||||
).len();
|
||||
rightFootAccelerationMagnitude = rightFootAcceleration.len();
|
||||
}
|
||||
|
||||
// compute the velocity and acceleration of the center of mass
|
||||
|
||||
@@ -47,13 +47,14 @@ public class LegTweaks {
|
||||
|
||||
// hyperparameters (COM calculation)
|
||||
// mass percentages of the body
|
||||
private static final float HEAD_MASS = 0.082f;
|
||||
private static final float CHEST_MASS = 0.25f;
|
||||
private static final float WAIST_MASS = 0.209f;
|
||||
private static final float THIGH_MASS = 0.128f;
|
||||
private static final float CALF_MASS = 0.0535f;
|
||||
private static final float UPPER_ARM_MASS = 0.031f;
|
||||
private static final float FOREARM_MASS = 0.017f;
|
||||
private static final float HEAD_MASS = 0.0827f;
|
||||
private static final float THORAX_MASS = 0.1870f;
|
||||
private static final float ABDOMEN_MASS = 0.1320f;
|
||||
private static final float PELVIS_MASS = 0.1530f;
|
||||
private static final float THIGH_MASS = 0.1122f;
|
||||
private static final float LEG_AND_FOOT_MASS = 0.0620f;
|
||||
private static final float UPPER_ARM_MASS = 0.0263f;
|
||||
private static final float FOREARM_AND_HAND_MASS = 0.0224f;
|
||||
|
||||
// hyperparameters (rotation correction)
|
||||
private static final float ROTATION_CORRECTION_VERTICAL = 0.1f;
|
||||
@@ -1096,17 +1097,19 @@ public class LegTweaks {
|
||||
// compute the center of mass of smaller body parts and then sum them up
|
||||
// with their respective weights
|
||||
Vector3 head = skeleton.headNode.getWorldTransform().getTranslation();
|
||||
Vector3 chest = skeleton.chestNode.getWorldTransform().getTranslation();
|
||||
Vector3 hip = skeleton.hipNode.getWorldTransform().getTranslation();
|
||||
Vector3 thorax = getCenterOfJoint(skeleton.chestNode, skeleton.upperChestNode);
|
||||
Vector3 abdomen = skeleton.waistNode.getWorldTransform().getTranslation();
|
||||
Vector3 pelvis = skeleton.hipNode.getWorldTransform().getTranslation();
|
||||
Vector3 leftCalf = getCenterOfJoint(skeleton.leftAnkleNode, skeleton.leftKneeNode);
|
||||
Vector3 rightCalf = getCenterOfJoint(skeleton.rightAnkleNode, skeleton.rightKneeNode);
|
||||
Vector3 leftThigh = getCenterOfJoint(skeleton.leftKneeNode, skeleton.leftHipNode);
|
||||
Vector3 rightThigh = getCenterOfJoint(skeleton.rightKneeNode, skeleton.rightHipNode);
|
||||
centerOfMass = centerOfMass.plus(head.times(HEAD_MASS));
|
||||
centerOfMass = centerOfMass.plus(chest.times(CHEST_MASS));
|
||||
centerOfMass = centerOfMass.plus(hip.times(WAIST_MASS));
|
||||
centerOfMass = centerOfMass.plus(leftCalf.times(CALF_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightCalf.times(CALF_MASS));
|
||||
centerOfMass = centerOfMass.plus(thorax.times(THORAX_MASS));
|
||||
centerOfMass = centerOfMass.plus(abdomen.times(ABDOMEN_MASS));
|
||||
centerOfMass = centerOfMass.plus(pelvis.times(PELVIS_MASS));
|
||||
centerOfMass = centerOfMass.plus(leftCalf.times(LEG_AND_FOOT_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightCalf.times(LEG_AND_FOOT_MASS));
|
||||
centerOfMass = centerOfMass.plus(leftThigh.times(THIGH_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightThigh.times(THIGH_MASS));
|
||||
|
||||
@@ -1126,17 +1129,17 @@ public class LegTweaks {
|
||||
);
|
||||
centerOfMass = centerOfMass.plus(leftUpperArm.times(UPPER_ARM_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightUpperArm.times(UPPER_ARM_MASS));
|
||||
centerOfMass = centerOfMass.plus(leftForearm.times(FOREARM_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightForearm.times(FOREARM_MASS));
|
||||
centerOfMass = centerOfMass.plus(leftForearm.times(FOREARM_AND_HAND_MASS));
|
||||
centerOfMass = centerOfMass.plus(rightForearm.times(FOREARM_AND_HAND_MASS));
|
||||
} else {
|
||||
// if the arms are not available put them slightly in front
|
||||
// of the chest.
|
||||
// of the upper chest.
|
||||
Vector3 chestUnitVector = computeUnitVector(
|
||||
skeleton.chestNode.getWorldTransform().getRotation()
|
||||
skeleton.upperChestNode.getWorldTransform().getRotation()
|
||||
);
|
||||
Vector3 armLocation = chest.plus(chestUnitVector.times(DEFAULT_ARM_DISTANCE));
|
||||
Vector3 armLocation = abdomen.plus(chestUnitVector.times(DEFAULT_ARM_DISTANCE));
|
||||
centerOfMass = centerOfMass.plus(armLocation.times(UPPER_ARM_MASS * 2.0f));
|
||||
centerOfMass = centerOfMass.plus(armLocation.times(FOREARM_MASS * 2.0f));
|
||||
centerOfMass = centerOfMass.plus(armLocation.times(FOREARM_AND_HAND_MASS * 2.0f));
|
||||
}
|
||||
|
||||
// finally translate in to tracker space
|
||||
|
||||
@@ -5,7 +5,7 @@ import dev.slimevr.tracking.trackers.Tracker;
|
||||
import java.util.LinkedList;
|
||||
|
||||
|
||||
// class that monitors the acceleration of the waist, hip, or chest trackers to detect taps
|
||||
// class that monitors the acceleration of the waist, hip, chest or upper chest trackers to detect taps
|
||||
// and use this to trigger a varaity of resets (if your wondering why no single tap class exists, it's because
|
||||
// to many false positives)
|
||||
public class TapDetection {
|
||||
@@ -175,6 +175,13 @@ public class TapDetection {
|
||||
// you need two or more trackers for this feature to be reliable)
|
||||
private boolean isUserStatic(Tracker trackerToExclude) {
|
||||
int num = 0;
|
||||
if (
|
||||
skeleton.upperChestTracker != null
|
||||
&& !skeleton.upperChestTracker.equals(trackerToExclude)
|
||||
) {
|
||||
if (skeleton.upperChestTracker.getAcceleration().lenSq() > ALLOWED_BODY_ACCEL_SQUARED)
|
||||
num++;
|
||||
}
|
||||
if (skeleton.chestTracker != null && !skeleton.chestTracker.equals(trackerToExclude)) {
|
||||
if (skeleton.chestTracker.getAcceleration().lenSq() > ALLOWED_BODY_ACCEL_SQUARED)
|
||||
num++;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package dev.slimevr.tracking.processor.skeleton;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dev.slimevr.config.TapDetectionConfig;
|
||||
import dev.slimevr.reset.ResetHandler;
|
||||
import dev.slimevr.setup.TapSetupHandler;
|
||||
@@ -11,6 +8,9 @@ import dev.slimevr.tracking.processor.HumanPoseManager;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import solarxr_protocol.rpc.ResetType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Handles tap detection for reset
|
||||
@@ -211,7 +211,9 @@ public class TapDetectionManager {
|
||||
// on which one is available
|
||||
// if none are available, returns null
|
||||
private Tracker getTrackerToWatchYawReset() {
|
||||
if (skeleton.chestTracker != null)
|
||||
if (skeleton.upperChestTracker != null)
|
||||
return skeleton.upperChestTracker;
|
||||
else if (skeleton.chestTracker != null)
|
||||
return skeleton.chestTracker;
|
||||
else if (skeleton.hipTracker != null)
|
||||
return skeleton.hipTracker;
|
||||
|
||||
@@ -13,6 +13,7 @@ import solarxr_protocol.rpc.StatusData
|
||||
import solarxr_protocol.rpc.StatusDataUnion
|
||||
import solarxr_protocol.rpc.StatusTrackerErrorT
|
||||
import solarxr_protocol.rpc.StatusTrackerResetT
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
const val TIMEOUT_MS = 2000L
|
||||
|
||||
@@ -45,8 +46,8 @@ class Tracker @JvmOverloads constructor(
|
||||
private val timer = BufferedTimer(1f)
|
||||
private var timeAtLastUpdate: Long = 0
|
||||
private var rotation = Quaternion.IDENTITY
|
||||
private var acceleration = Vector3.NULL
|
||||
var position = Vector3.NULL
|
||||
var acceleration = Vector3.NULL
|
||||
val resetsHandler: TrackerResetsHandler = TrackerResetsHandler(this)
|
||||
val filteringHandler: TrackerFilteringHandler = TrackerFilteringHandler()
|
||||
var batteryVoltage: Float? = null
|
||||
@@ -59,40 +60,37 @@ class Tracker @JvmOverloads constructor(
|
||||
/**
|
||||
* If the tracker has gotten disconnected after it was initialized first time
|
||||
*/
|
||||
var disconnectedRecently = false
|
||||
var statusResetRecently = false
|
||||
private var alreadyInitialized = false
|
||||
var status = TrackerStatus.DISCONNECTED
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
var status: TrackerStatus by Delegates.observable(TrackerStatus.DISCONNECTED) {
|
||||
_, old, new ->
|
||||
if (old == new) return@observable
|
||||
|
||||
field = value
|
||||
|
||||
if (field == TrackerStatus.DISCONNECTED && alreadyInitialized) {
|
||||
disconnectedRecently = true
|
||||
}
|
||||
if (field.sendData) {
|
||||
alreadyInitialized = true
|
||||
}
|
||||
if (!isInternal) {
|
||||
// If the status of a non-internal tracker has changed, inform
|
||||
// the VRServer to recreate the skeleton, as it may need to
|
||||
// assign or un-assign the tracker to a body part
|
||||
vrServer.updateSkeletonModel()
|
||||
|
||||
checkReportErrorStatus()
|
||||
checkReportRequireReset()
|
||||
if (!new.reset) {
|
||||
if (alreadyInitialized) {
|
||||
statusResetRecently = true
|
||||
}
|
||||
alreadyInitialized = true
|
||||
}
|
||||
if (!isInternal) {
|
||||
// If the status of a non-internal tracker has changed, inform
|
||||
// the VRServer to recreate the skeleton, as it may need to
|
||||
// assign or un-assign the tracker to a body part
|
||||
vrServer.updateSkeletonModel()
|
||||
|
||||
var trackerPosition = trackerPosition
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
field = value
|
||||
|
||||
if (!isInternal) {
|
||||
checkReportRequireReset()
|
||||
}
|
||||
checkReportErrorStatus()
|
||||
checkReportRequireReset()
|
||||
}
|
||||
}
|
||||
|
||||
var trackerPosition: TrackerPosition? by Delegates.observable(trackerPosition) {
|
||||
_, old, new ->
|
||||
if (old == new) return@observable
|
||||
|
||||
if (!isInternal) {
|
||||
checkReportRequireReset()
|
||||
}
|
||||
}
|
||||
|
||||
// Computed value to simplify availability checks
|
||||
val hasAdjustedRotation = hasRotation && (allowFiltering || needsReset)
|
||||
@@ -116,9 +114,11 @@ class Tracker @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun checkReportRequireReset() {
|
||||
if (needsReset && trackerPosition != null && lastResetStatus == 0u && status.sendData) {
|
||||
if (needsReset && trackerPosition != null && lastResetStatus == 0u &&
|
||||
!status.reset && (isImu() || !statusResetRecently)
|
||||
) {
|
||||
reportRequireReset()
|
||||
} else if (lastResetStatus != 0u && (trackerPosition == null || !status.sendData)) {
|
||||
} else if (lastResetStatus != 0u && (trackerPosition == null || status.reset)) {
|
||||
vrServer.statusSystem.removeStatus(lastResetStatus)
|
||||
lastResetStatus = 0u
|
||||
}
|
||||
@@ -203,6 +203,11 @@ class Tracker @JvmOverloads constructor(
|
||||
resetsHandler.allowDriftCompensation = it
|
||||
}
|
||||
}
|
||||
if (!isInternal &&
|
||||
!(!isImu() && (trackerPosition == TrackerPosition.LEFT_HAND || trackerPosition == TrackerPosition.RIGHT_HAND))
|
||||
) {
|
||||
checkReportRequireReset()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,6 +269,18 @@ class Tracker @JvmOverloads constructor(
|
||||
return rot
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the adjusted tracker acceleration after mounting corrections.
|
||||
*/
|
||||
fun getAcceleration(): Vector3 {
|
||||
var vec = acceleration
|
||||
if (needsReset) {
|
||||
vec = resetsHandler.getMountingAdjustedRotationFrom(rotation).sandwich(vec)
|
||||
}
|
||||
|
||||
return vec
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identity-adjusted tracker rotation after some corrections
|
||||
* (filtering, identity reset and identity mounting).
|
||||
@@ -301,6 +318,13 @@ class Tracker @JvmOverloads constructor(
|
||||
this.rotation = rotation
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw (unadjusted) acceleration of the tracker.
|
||||
*/
|
||||
fun setAcceleration(vec: Vector3) {
|
||||
this.acceleration = vec
|
||||
}
|
||||
|
||||
fun isImu(): Boolean {
|
||||
return imuType != null
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ enum class TrackerPosition(
|
||||
) {
|
||||
HEAD("body:head", TrackerRole.HMD, BodyPart.HEAD),
|
||||
NECK("body:neck", TrackerRole.NECK, BodyPart.NECK),
|
||||
CHEST("body:chest", TrackerRole.CHEST, BodyPart.CHEST),
|
||||
UPPER_CHEST("body:upper_chest", TrackerRole.CHEST, BodyPart.UPPER_CHEST),
|
||||
CHEST("body:chest", null, BodyPart.CHEST),
|
||||
WAIST("body:waist", null, BodyPart.WAIST),
|
||||
HIP("body:hip", TrackerRole.WAIST, BodyPart.HIP),
|
||||
LEFT_UPPER_LEG("body:left_upper_leg", TrackerRole.LEFT_KNEE, BodyPart.LEFT_UPPER_LEG),
|
||||
|
||||
@@ -96,6 +96,13 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
return adjustToIdentity(rotation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a rotation and adjusts it to mounting
|
||||
*/
|
||||
fun getMountingAdjustedRotationFrom(rotation: Quaternion): Quaternion {
|
||||
return rotation * mountingOrientation * mountRotFix
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts raw or filtered rotation into reference- and
|
||||
* mounting-reset-adjusted by applying quaternions produced after
|
||||
@@ -185,9 +192,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
|
||||
// Let's just remove the status if you do yaw reset if the tracker was
|
||||
// disconnected and then connected back
|
||||
if (this.tracker.lastResetStatus != 0u && this.tracker.disconnectedRecently) {
|
||||
if (this.tracker.lastResetStatus != 0u && this.tracker.statusResetRecently) {
|
||||
vrServer.statusSystem.removeStatus(this.tracker.lastResetStatus)
|
||||
this.tracker.disconnectedRecently = false
|
||||
this.tracker.statusResetRecently = false
|
||||
this.tracker.lastResetStatus = 0u
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package dev.slimevr.tracking.trackers
|
||||
|
||||
enum class TrackerStatus(val id: Int, val sendData: Boolean) {
|
||||
enum class TrackerStatus(val id: Int, val sendData: Boolean, val reset: Boolean) {
|
||||
|
||||
DISCONNECTED(0, false),
|
||||
OK(1, true),
|
||||
BUSY(2, true),
|
||||
ERROR(3, false),
|
||||
OCCLUDED(4, false),
|
||||
DISCONNECTED(0, false, true),
|
||||
OK(1, true, false),
|
||||
BUSY(2, true, false),
|
||||
ERROR(3, false, true),
|
||||
OCCLUDED(4, false, false),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -43,4 +43,18 @@ object TrackerUtils {
|
||||
secondTracker: Tracker?,
|
||||
thirdTracker: Tracker?,
|
||||
): Tracker? = firstTracker ?: (secondTracker ?: thirdTracker)
|
||||
|
||||
/**
|
||||
* Returns the first tracker that isn't null out of the 4 trackers passed as
|
||||
* arguments.
|
||||
*
|
||||
* @return The first non-null tracker or null
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getFirstAvailableTracker(
|
||||
firstTracker: Tracker?,
|
||||
secondTracker: Tracker?,
|
||||
thirdTracker: Tracker?,
|
||||
fourthTracker: Tracker?,
|
||||
): Tracker? = firstTracker ?: (secondTracker ?: (thirdTracker ?: fourthTracker))
|
||||
}
|
||||
|
||||
@@ -293,8 +293,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
is UDPPacket4Acceleration -> {
|
||||
tracker = connection?.getTracker(packet.sensorId)
|
||||
if (tracker == null) return
|
||||
val acceleration = tracker.getRotation().sandwich(packet.acceleration)
|
||||
tracker.acceleration = acceleration
|
||||
tracker.setAcceleration(packet.acceleration)
|
||||
}
|
||||
is UDPPacket10PingPong -> {
|
||||
if (connection == null) return
|
||||
|
||||
Submodule solarxr-protocol updated: d0c1dc0118...56658d0d9a
Reference in New Issue
Block a user