Skip to content

Commit

Permalink
allow editing weights
Browse files Browse the repository at this point in the history
  • Loading branch information
jakmeier committed Oct 11, 2024
1 parent 4b5ac05 commit 36ba030
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 28 deletions.
116 changes: 94 additions & 22 deletions bouncy_frontend/src/lib/components/editor/PoseEditForm.svelte
Original file line number Diff line number Diff line change
@@ -1,54 +1,126 @@
<script>
import { GRAY_COLORING } from '$lib/constants';
import { t } from '$lib/i18n';
import {
PoseWrapper,
Skeleton,
SkeletonField,
} from '$lib/instructor/bouncy_instructor';
import Svg from '../avatar/Svg.svelte';
import SvgAvatar from '../avatar/SvgAvatar.svelte';
/** @type {PoseWrapper} */
export let pose;
/** @type {PoseWrapper | undefined} */
let pose;
/** @type {Skeleton | undefined} */
let skeleton;
/** @type {WeightedPoseLimb[]} */
let weightedLimbs = [];
/** @param {PoseWrapper} newPose */
export function loadPose(newPose) {
// using an indirect setting, rather than a property, to better control when
// the updates happen
pose = newPose;
onPoseUpdated(pose);
}
/** @param {PoseWrapper} newPose */
function onPoseUpdated(newPose) {
skeleton = newPose.skeleton();
weightedLimbs = extractWeightedLimbs(newPose);
}
$: skeleton = pose.skeleton();
$: weightedLimbs = extractWeightedLimbs(pose);
$: markedLimbIndices = weightedLimbs.map((limb) => limb.index);
/**
* @param {PoseWrapper} pose
* @returns {WeightedPoseLimb[]}
*/
function extractWeightedLimbs(pose) {
return allSkeletonFields()
.map((limb) => ({
name: SkeletonField[limb],
index: limb,
weight: pose.getWeight(limb),
}))
.filter((limb) => limb.weight > 0.0);
}
/** @param {number} weightedLimbIndex */
function updateAvatar(weightedLimbIndex) {
let limb = weightedLimbs[weightedLimbIndex];
if (pose && limb) {
pose.setWeight(limb.index, limb.weight);
onPoseUpdated(pose);
} else {
console.log('Pose or limb not available');
}
}
function allSkeletonFields() {
// using reduce as a map + filter to avoid the extra array
return Object.keys(SkeletonField).reduce(
/** @type {(acc: WeightedPoseLimb[], field: string) => WeightedPoseLimb[]} */
/** @type {(acc: number[], field: string) => number[]} */
(acc, field) => {
// enum has static values for indices and names, we want to filter only the numbers
let limb = SkeletonField[field];
if (typeof limb === 'number') {
let weight = pose.getWeight(limb);
if (weight > 0.0) {
acc.push({ name: SkeletonField[limb], index: limb, weight });
}
acc.push(limb);
}
return acc;
},
/** @type {WeightedPoseLimb[]}*/
/** @type {number[]}*/
[]
);
}
</script>

<div class="avatar">
<Svg width={300} height={300} orderByZ>
{#if skeleton}
<SvgAvatar
{skeleton}
width={300}
height={300}
style={GRAY_COLORING}
{markedLimbIndices}
></SvgAvatar>
{/if}
</Svg>
<div class="container">
<div class="avatar">
<Svg width={300} height={300} orderByZ>
{#if skeleton}
<SvgAvatar
{skeleton}
width={300}
height={300}
style={GRAY_COLORING}
{markedLimbIndices}
></SvgAvatar>
{/if}
</Svg>
</div>

<div class="weights">
{#each weightedLimbs as limb, index}
<div class="body-part">
<label for={limb.name}>{$t(limb.name)}</label>
<input
name={limb.name}
type="number"
class="weight-input"
bind:value={limb.weight}
on:input={() => updateAvatar(index)}
placeholder="Weight"
/>
</div>
{/each}
</div>
</div>

<style>
.container {
display: grid;
grid-template-columns: 2fr 1fr;
}
.body-part {
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
margin: auto;
}
.weight-input {
width: 100%;
text-align: center;
}
</style>
13 changes: 7 additions & 6 deletions bouncy_frontend/src/routes/dev/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
let liveSkeleton;
/** @type {import("$lib/instructor/bouncy_instructor").SkeletonWrapper | undefined} */
let poseSkeleton;
/** @type {import("$lib/instructor/bouncy_instructor").PoseWrapper | undefined} */
let editorPose;
let dataListener;
/** @type {(skeleton: import("$lib/instructor/bouncy_instructor").SkeletonWrapper)=>void} */
let loadSkeleton;
/** @type {()=>import("$lib/instructor/bouncy_instructor").PoseWrapper} */
let poseFromForm;
/** @type {(skeleton: import("$lib/instructor/bouncy_instructor").PoseWrapper)=>void} */
let loadPose;
let tracker = new Tracker();
registerTracker(tracker);
const poseCtx = getContext('pose');
Expand Down Expand Up @@ -139,7 +139,10 @@
}
function copyPose() {
editorPose = poseFromForm();
let pose = poseFromForm();
if (pose) {
loadPose(pose);
}
}
</script>

Expand Down Expand Up @@ -182,9 +185,7 @@

<button class="light full-width short" on:click={copyPose}> ↓ </button>

{#if editorPose}
<PoseEditForm pose={editorPose}></PoseEditForm>
{/if}
<PoseEditForm bind:loadPose></PoseEditForm>

<button on:click={downloadFrame}> Download Keypoints of Frame </button>
<button on:click={downloadKeypoints}> Download Keypoints of Video </button>
Expand Down

0 comments on commit 36ba030

Please sign in to comment.