Skip to content

Commit

Permalink
poc: step editor
Browse files Browse the repository at this point in the history
Just getting things loaded without panics was a bit of a challenge.
For now, I'm committing these changes and will complete the actual
editor in other commits.

In the medium term, I really need to fix how steps and poses are loaded.
  • Loading branch information
jakmeier committed Oct 20, 2024
1 parent a4ec9c1 commit 0a33357
Show file tree
Hide file tree
Showing 23 changed files with 614 additions and 110 deletions.
18 changes: 18 additions & 0 deletions bouncy_frontend/src/lib/components/Pose.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script>
import { LEFT_RIGHT_COLORING_LIGHT } from '$lib/constants';
import { PoseWrapper } from '$lib/instructor/bouncy_instructor';
import Svg from './avatar/Svg.svelte';
import SvgAvatar from './avatar/SvgAvatar.svelte';
/** @type {PoseWrapper} */
export let pose;
export let size = 200;
export let style = LEFT_RIGHT_COLORING_LIGHT;
</script>

<div class="avatar">
<Svg width={size} height={size} orderByZ>
<SvgAvatar skeleton={pose.skeleton()} width={size} height={size} {style}
></SvgAvatar>
</Svg>
</div>
78 changes: 78 additions & 0 deletions bouncy_frontend/src/lib/components/editor/StepEditForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script>
import { t } from '$lib/i18n';
import {
PoseWrapper,
StepPositionBuilder,
StepWrapper,
} from '$lib/instructor/bouncy_instructor';
import { getContext } from 'svelte';
import DraggableList from '../ui/DraggableList.svelte';
import UiBox from '../ui/UiBox.svelte';
import Pose from '../Pose.svelte';
const localCollectionCtx = getContext('localCollection');
const availablePoses = localCollectionCtx.poses;
/** @param {StepWrapper} loadedStep */
export function loadStep(loadedStep) {
step = loadedStep;
stepName = step.name;
}
/** @type {StepWrapper} */
let step = newStep();
let stepName = $t('editor.name-input-placeholder');
$: stepPositionBuilders = step.positions();
let showAddNewItem = false;
function newStep() {
const idNum = Math.random().toFixed(6).substring(2);
const id = `step-${idNum}`;
const name = `Step ${idNum} Name`;
const step = new StepWrapper(id, name, 'lab');
localCollectionCtx.addStep(step);
return step;
}
/** @param {PoseWrapper} pose */
function addPose(pose) {
step.addPosition(new StepPositionBuilder(pose));
step = step;
}
</script>

<input id="name" name="name" bind:value={stepName} />

<DraggableList items={stepPositionBuilders} bind:showAddNewItem>
<slot slot="main" let:item={position}>
<div class="pose">
<Pose pose={position.pose()}></Pose>
</div>
</slot>
<slot slot="name" let:item={position}>{position.pose().name('en')}</slot>
</DraggableList>

{#if showAddNewItem}
<UiBox title="editor.pick-pose-prompt">
<div>
{#each $availablePoses as pose}
<div
on:click={() => addPose(pose)}
role="button"
tabindex={0}
on:keydown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
addPose(pose);
}
}}
>
<div class="pose">
<p>{pose.name('en')}</p>
<Pose {pose}></Pose>
</div>
</div>
{/each}
</div>
</UiBox>
{/if}
2 changes: 2 additions & 0 deletions bouncy_frontend/src/lib/i18n/de-CH.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@
"delete-dance-confirmation": "Tanz wirklich löschen?",
"edit-button": "Tanz bearbeiten",
"edit-copy-button": "Kopieren und bearbeiten",
"pick-pose-prompt": "Wähle eine Pose",
"pick-step-prompt": "Wähle einen Schritt",
"saving-prompt": "Wie soll der Tanz gespeichert werden?",
"save-copy-button": "Kopie speichern",
"overwrite-existing-button": "Überschreiben",
"direction": "Richtung",
"speed": "Tempo",
"name-input-placeholder": "Name",
"pose": {
"title": "Labor: Posen",
"list": "Meine Posen",
Expand Down
2 changes: 2 additions & 0 deletions bouncy_frontend/src/lib/i18n/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@
"delete-dance-confirmation": "Delete the dance?",
"edit-button": "Edit dance",
"edit-copy-button": "Copy and edit",
"pick-pose-prompt": "Pick a pose",
"pick-step-prompt": "Pick a step",
"saving-prompt": "How to save the dance?",
"save-copy-button": "Save a copy",
"overwrite-existing-button": "Overwrite",
"direction": "Direction",
"speed": "Speed",
"name-input-placeholder": "Name",
"pose": {
"title": "Lab: Poses",
"list": "My poses",
Expand Down
145 changes: 104 additions & 41 deletions bouncy_frontend/src/lib/instructor/bouncy_instructor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,30 +74,13 @@ export function dances(): (DanceWrapper)[];
*/
export function danceBuilderFromDance(dance_id: string): DanceBuilder;
/**
* @param {(PoseWrapper)[]} poses
*/
export enum DetectionState {
/**
* Neutral state, not detecting anything.
*/
Init = 1,
export function addLocalPoses(poses: (PoseWrapper)[]): void;
/**
* Dance is positioning themselves, detecting the idle position.
* @param {(StepWrapper)[]} steps
*/
Positioning = 2,
/**
* About to go over to live tracking, playing a countdown audio.
*/
CountDown = 3,
/**
* Tracking current movements.
*/
LiveTracking = 4,
/**
* No longer tracking but the results of the previous tracking are
* available.
*/
TrackingDone = 5,
}
export function loadLocalSteps(steps: (StepWrapper)[]): void;
/**
*/
export enum DetectionFailureReason {
Expand Down Expand Up @@ -149,6 +132,31 @@ export enum PoseHint {
ZOrder = 2,
WrongDirection = 3,
}
/**
*/
export enum DetectionState {
/**
* Neutral state, not detecting anything.
*/
Init = 1,
/**
* Dance is positioning themselves, detecting the idle position.
*/
Positioning = 2,
/**
* About to go over to live tracking, playing a countdown audio.
*/
CountDown = 3,
/**
* Tracking current movements.
*/
LiveTracking = 4,
/**
* No longer tracking but the results of the previous tracking are
* available.
*/
TrackingDone = 5,
}

import type { Readable } from "svelte/store";

Expand Down Expand Up @@ -745,6 +753,17 @@ export class Segment {
export class Skeleton {
free(): void;
/**
* Compute 2d coordinates for the skeleton for rendering.
*
* The skeleton will be rendered assuming hard-coded values for body part
* proportional lengths, multiplied with the size parameter. The hip
* segment will have its center at the given position.
* @param {Cartesian2d} hip_center
* @param {number} size
* @returns {SkeletonV2}
*/
render(hip_center: Cartesian2d, size: number): SkeletonV2;
/**
* @param {boolean} sideway
* @returns {Skeleton}
*/
Expand All @@ -758,17 +777,6 @@ export class Skeleton {
*/
debugString(): string;
/**
* Compute 2d coordinates for the skeleton for rendering.
*
* The skeleton will be rendered assuming hard-coded values for body part
* proportional lengths, multiplied with the size parameter. The hip
* segment will have its center at the given position.
* @param {Cartesian2d} hip_center
* @param {number} size
* @returns {SkeletonV2}
*/
render(hip_center: Cartesian2d, size: number): SkeletonV2;
/**
* Does the dancer face away more than they face the camera?
*/
backwards: boolean;
Expand Down Expand Up @@ -902,6 +910,10 @@ export class StepFileWrapper {
*/
constructor();
/**
* FIXME: This adds steps as lab steps and then calls a warm up. This is to
* avoid the problem where a step wrapper can only be created for steps
* that are already registered in global state. A proper refactoring should
* solve this.
* @param {string} text
* @returns {StepFileWrapper}
*/
Expand All @@ -928,10 +940,34 @@ export class StepFileWrapper {
buildRon(): string;
}
/**
* Used in the editor to add and edit poses of a step.
*/
export class StepPositionBuilder {
free(): void;
/**
* @param {PoseWrapper} pose
*/
constructor(pose: PoseWrapper);
/**
* @returns {PoseWrapper}
*/
pose(): PoseWrapper;
/**
* @param {number} height
*/
setJumpHeight(height: number): void;
}
/**
*/
export class StepWrapper {
free(): void;
/**
* @param {string} id
* @param {string} name
* @param {string} source
*/
constructor(id: string, name: string, source: string);
/**
* @param {number} beat
* @returns {Skeleton}
*/
Expand All @@ -955,6 +991,33 @@ export class StepWrapper {
*/
jumpHeight(beat: number): number | undefined;
/**
* Look up poses from the global collection, do not use for courses that
* require a custom collection.
* @returns {(PoseWrapper)[]}
*/
poses(): (PoseWrapper)[];
/**
* Positions with poses from the global collection, do not use for courses
* that require a custom collection.
* @returns {(StepPositionBuilder)[]}
*/
positions(): (StepPositionBuilder)[];
/**
* Add poses from the global collection, do not use for courses that
* require a custom collection.
* @param {StepPositionBuilder} position
*/
addPosition(position: StepPositionBuilder): void;
/**
* @param {number} index
*/
removePosition(index: number): void;
/**
* @param {number} index
* @param {StepPositionBuilder} position
*/
insertPosition(index: number, position: StepPositionBuilder): void;
/**
* The number of beats the step takes for one repetition.
*/
readonly beats: number;
Expand Down Expand Up @@ -983,15 +1046,6 @@ export class StepWrapper {
export class Tracker {
free(): void;
/**
* @param {number} timestamp
* @returns {ExportedFrame}
*/
exportFrame(timestamp: number): ExportedFrame;
/**
* @returns {string}
*/
exportKeypoints(): string;
/**
* Create a tracker for all known steps.
*/
constructor();
Expand Down Expand Up @@ -1150,6 +1204,15 @@ export class Tracker {
*/
renderedKeypointsAt(timestamp: number, width: number, height: number): SkeletonV2 | undefined;
/**
* @param {number} timestamp
* @returns {ExportedFrame}
*/
exportFrame(timestamp: number): ExportedFrame;
/**
* @returns {string}
*/
exportKeypoints(): string;
/**
*/
readonly detectionState: ReadableDetectionState;
/**
Expand Down
12 changes: 12 additions & 0 deletions bouncy_frontend/src/lib/instructor/bouncy_instructor_bg.wasm.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const __wbindgen_export_2: WebAssembly.Table;
export function addLocalPoses(a: number, b: number): void;
export function audioeffect_soundId(a: number, b: number): void;
export function cartesian2d_add(a: number, b: number): number;
export function cartesian2d_new(a: number, b: number): number;
Expand Down Expand Up @@ -62,6 +63,7 @@ export function limberror_name(a: number, b: number): void;
export function limberror_render(a: number, b: number, c: number): void;
export function loadDanceFile(a: number, b: number): number;
export function loadDanceString(a: number, b: number, c: number): void;
export function loadLocalSteps(a: number, b: number): void;
export function loadPoseFile(a: number, b: number): number;
export function loadPoseString(a: number, b: number, c: number): void;
export function loadStepFile(a: number, b: number, c: number, d: number): number;
Expand Down Expand Up @@ -106,14 +108,23 @@ export function stepfilewrapper_new_empty(): number;
export function stepfilewrapper_overwriteStep(a: number, b: number, c: number): void;
export function stepfilewrapper_removeStep(a: number, b: number, c: number, d: number): void;
export function stepfilewrapper_steps(a: number, b: number): void;
export function steppositionbuilder_new(a: number): number;
export function steppositionbuilder_pose(a: number): number;
export function steppositionbuilder_setJumpHeight(a: number, b: number): void;
export function steps(a: number): void;
export function stepsByName(a: number, b: number, c: number): void;
export function stepsBySource(a: number, b: number, c: number): void;
export function stepwrapper_addPosition(a: number, b: number): void;
export function stepwrapper_beats(a: number): number;
export function stepwrapper_bodyShift(a: number, b: number): number;
export function stepwrapper_id(a: number, b: number): void;
export function stepwrapper_insertPosition(a: number, b: number, c: number, d: number): void;
export function stepwrapper_jumpHeight(a: number, b: number, c: number): void;
export function stepwrapper_name(a: number, b: number): void;
export function stepwrapper_new_empty(a: number, b: number, c: number, d: number, e: number, f: number): number;
export function stepwrapper_poses(a: number, b: number): void;
export function stepwrapper_positions(a: number, b: number): void;
export function stepwrapper_removePosition(a: number, b: number, c: number): void;
export function stepwrapper_rotatedSkeleton(a: number, b: number, c: number): number;
export function stepwrapper_skeleton(a: number, b: number): number;
export function stepwrapper_variation(a: number, b: number): void;
Expand Down Expand Up @@ -299,6 +310,7 @@ export function __wbg_skeletonsidev2_free(a: number, b: number): void;
export function __wbg_skeletonv2_free(a: number, b: number): void;
export function __wbg_skeletonwrapper_free(a: number, b: number): void;
export function __wbg_stepfilewrapper_free(a: number, b: number): void;
export function __wbg_steppositionbuilder_free(a: number, b: number): void;
export function __wbg_stepwrapper_free(a: number, b: number): void;
export function __wbg_tracker_free(a: number, b: number): void;
export function __wbg_zerror_free(a: number, b: number): void;
Expand Down
Loading

0 comments on commit 0a33357

Please sign in to comment.