Skip to content

Commit

Permalink
feat: [project-sequencer-statemachine] 初期ステートの設定周りを変更 (#2518)
Browse files Browse the repository at this point in the history
Co-authored-by: Hiroshiba <[email protected]>
  • Loading branch information
sigprogramming and Hiroshiba authored Feb 3, 2025
1 parent fce6ce8 commit 4205802
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 12 deletions.
19 changes: 13 additions & 6 deletions src/composables/useSequencerStateMachine.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { computed, ref } from "vue";
import {
ComputedRefs,
IdleStateId,
PartialStore,
Refs,
} from "@/sing/sequencerStateMachine/common";
import { getNoteDuration } from "@/sing/domain";
import { createSequencerStateMachine } from "@/sing/sequencerStateMachine";

export const useSequencerStateMachine = (store: PartialStore) => {
export const useSequencerStateMachine = (
store: PartialStore,
initialStateId: IdleStateId,
) => {
const computedRefs: ComputedRefs = {
snapTicks: computed(() =>
getNoteDuration(store.state.sequencerSnapType, store.state.tpqn),
Expand All @@ -25,11 +29,14 @@ export const useSequencerStateMachine = (store: PartialStore) => {
previewPitchEdit: ref(undefined),
guideLineTicks: ref(0),
};
const stateMachine = createSequencerStateMachine({
...computedRefs,
...refs,
store,
});
const stateMachine = createSequencerStateMachine(
{
...computedRefs,
...refs,
store,
},
initialStateId,
);
return {
stateMachine,
nowPreviewing: computed(() => refs.nowPreviewing.value),
Expand Down
8 changes: 6 additions & 2 deletions src/sing/sequencerStateMachine/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Context,
IdleStateId,
Input,
SequencerStateDefinitions,
} from "@/sing/sequencerStateMachine/common";
Expand All @@ -17,7 +18,10 @@ import { SelectNotesWithRectState } from "@/sing/sequencerStateMachine/states/se
import { DrawPitchState } from "@/sing/sequencerStateMachine/states/drawPitchState";
import { ErasePitchState } from "@/sing/sequencerStateMachine/states/erasePitchState";

export const createSequencerStateMachine = (context: Context) => {
export const createSequencerStateMachine = (
context: Context,
initialStateId: IdleStateId,
) => {
return new StateMachine<SequencerStateDefinitions, Input, Context>(
{
selectNotesToolIdle: () => new SelectNotesToolIdleState(),
Expand All @@ -32,7 +36,7 @@ export const createSequencerStateMachine = (context: Context) => {
drawPitch: (args) => new DrawPitchState(args),
erasePitch: (args) => new ErasePitchState(args),
},
new SelectNotesToolIdleState(),
context,
initialStateId,
);
};
52 changes: 48 additions & 4 deletions src/sing/stateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ type StateFactories<T extends StateDefinition[], Input, Context> = {
) => State<T, Input, Context> & { readonly id: U };
};

/**
* 初期ステートとして設定可能なステートのIDを表す型。
*/
type InitialStateId<T extends StateDefinition[]> = T[number] extends infer U
? U extends { id: string; factoryArgs: undefined }
? U["id"]
: never
: never;

/**
* ステートマシンを表すクラス。
*
Expand All @@ -111,6 +120,7 @@ export class StateMachine<
private readonly context: Context;

private currentState: State<StateDefinitions, Input, Context>;
private isDisposed = false;

/**
* ステートマシンの現在のステートのID。
Expand All @@ -120,19 +130,36 @@ export class StateMachine<
}

/**
* @param initialState ステートマシンの初期ステート。
* @param context ステート間で共有されるコンテキスト。
* @param stateFactories ステートのファクトリー関数。
* @param context ステートマシンのコンテキスト。
* @param initialStateId ステートマシンの初期ステートのID。
*/
constructor(
stateFactories: StateFactories<StateDefinitions, Input, Context>,
initialState: State<StateDefinitions, Input, Context>,
context: Context,
initialStateId: InitialStateId<StateDefinitions>,
) {
this.stateFactories = stateFactories;
this.context = context;

this.currentState = initialState;
this.currentState = stateFactories[initialStateId](undefined);
}

/**
* ステートを遷移し、ライフサイクルイベントを実行する。
*
* @param id 遷移先のステートのID。
* @param factoryArgs 遷移先のステートのファクトリー関数の引数。
*/
transitionTo<T extends StateId<StateDefinitions>>(
id: T,
factoryArgs: FactoryArgs<StateDefinitions, T>,
) {
if (this.isDisposed) {
throw new Error("This state machine is already disposed.");
}
this.currentState.onExit(this.context);
this.currentState = this.stateFactories[id](factoryArgs);
this.currentState.onEnter(this.context);
}

Expand All @@ -142,19 +169,36 @@ export class StateMachine<
* @param input 処理する入力。
*/
process(input: Input) {
if (this.isDisposed) {
throw new Error("This state machine is already disposed.");
}

let nextState: State<StateDefinitions, Input, Context> | undefined =
undefined;

this.currentState.process({
input,
context: this.context,
setNextState: (id, factoryArgs) => {
nextState = this.stateFactories[id](factoryArgs);
},
});

if (nextState != undefined) {
this.currentState.onExit(this.context);
this.currentState = nextState;
this.currentState.onEnter(this.context);
}
}

/**
* ステートマシンを破棄する。
*/
dispose() {
if (this.isDisposed) {
throw new Error("Already disposed.");
}
this.isDisposed = true;
this.currentState.onExit(this.context);
}
}

0 comments on commit 4205802

Please sign in to comment.