Last active
November 5, 2021 06:28
-
-
Save yllan/42d296f72796cbebb9d9529cee452d57 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const IDX = { | |
VHS: { P2: 6, P3: 7, P4: 8, P5: 9, P6: 10 } | |
} | |
const nudgePoints = (elementIndexes) => assign((ctx, evt) => { | |
const [x, y] = [evt.clientX, evt.clientY] | |
const [sx, sy] = ctx.lastClientPoint | |
const [dx, dy] = [x - sx, y - sy] | |
const paths = elementIndexes.map(idx => [0, ctx.groupIndex, idx]) | |
ctx.editorMethods.nudgeElementsAtPath(dx, dy, paths) | |
return { ...ctx, lastClientPoint: [x, y] } | |
}) | |
const setPoints = (elementIndexes) => assign((ctx, evt) => { | |
const [vx, vy] = [evt.offsetX, evt.offsetY] // viewport | |
const [x, y] = multiply(ctx.invMatrix, [vx, vy, 1]) // dataspace | |
const paths = elementIndexes.map(idx => [0, ctx.groupIndex, idx]) | |
ctx.editorMethods.moveCirclesAtPathTo(x, y, paths) | |
return { ...ctx, lastClientPoint: [evt.clientX, evt.clientY] } | |
}) | |
/* Finite State Machine definition */ | |
// https://xstate.js.org/viz/?gist=42d296f72796cbebb9d9529cee452d57 | |
const editMachine = Machine({ | |
id: 'roiEdit', | |
initial: 'idle', | |
context: { | |
// dependencies | |
mode: 'readonly', | |
invMatrix: [], | |
editorMethods: {}, | |
groupsBehaviors: {}, | |
onUpdateCallback: () => { }, | |
// variables | |
lastClientPoint: [0, 0], | |
groupIndex: 0, | |
colorIndex: 0, | |
colors: [ | |
'#01f601', // green | |
'#ffb80a', // yellow | |
'#f35252', // red | |
'#0086ff' // blue | |
] | |
}, | |
states: { | |
idle: { | |
on: { | |
mousemove: [ | |
{ | |
actions: 'highlightElement', | |
cond: 'hitElement' | |
}, | |
{ | |
actions: 'clearHighlights' | |
} | |
], | |
mousedown: [ | |
{ | |
actions: ['selectElement', 'setLastClientPoint'], | |
target: 'selection', | |
cond: 'hitElement' | |
}, | |
{ | |
actions: ['createSimpleShape', 'setLastClientPoint'], | |
target: 'simple_shape_creation', | |
cond: 'isSimpleShape' | |
}, | |
{ | |
actions: ['createAngle', 'setLastClientPoint'], | |
target: 'angle_creation', | |
cond: 'isAngle' | |
}, | |
{ | |
actions: ['createVHS', 'setLastClientPoint'], | |
target: 'vhs_creation', | |
cond: 'isVHS' | |
}, | |
{ | |
actions: ['createText', 'setLastClientPoint'], | |
target: 'decide_text_position', | |
cond: 'isText' | |
} | |
], | |
dblclick: [{ | |
actions: ['selectElement', 'setGroupIndex', 'prepareTextInput'], | |
target: 'edit_text', | |
cond: 'targetIsText' | |
}], | |
delete: { | |
actions: 'deleteSelectedROIs' | |
} | |
} | |
}, | |
selection: { | |
on: { | |
mousemove: { // nudge element | |
actions: 'nudgeSelection', | |
target: 'selection_dragged' | |
}, | |
mouseup: { // just click, not dragged | |
target: 'idle' | |
} | |
} | |
}, | |
selection_dragged: { | |
on: { | |
mousemove: { | |
actions: 'nudgeSelection' | |
}, | |
mouseup: { | |
actions: 'saveROI', | |
target: 'idle' | |
} | |
} | |
}, | |
vhs_creation: { | |
initial: 'p1_commited', | |
states: { | |
p1_commited: { // 第一個點決定好位置 | |
on: { | |
mousemove: { | |
actions: 'nudgeVHSP2', | |
target: 'p2_moving' | |
} | |
} | |
}, | |
p2_moving: { // 移動第二個點 | |
on: { | |
mousemove: { actions: 'nudgeVHSP2' }, | |
mouseup: { target: 'p2_commited' } | |
} | |
}, | |
p2_commited: { | |
on: { | |
mousedown: { | |
actions: 'setVHSP3P4', | |
target: 'p3_commited' | |
} | |
} | |
}, | |
p3_commited: { | |
on: { | |
mousemove: { | |
actions: 'nudgeVHSP4', | |
target: 'p4_moving' | |
} | |
} | |
}, | |
p4_moving: { | |
on: { | |
mousemove: { actions: 'nudgeVHSP4' }, | |
mouseup: { target: 'p4_commited' } | |
} | |
}, | |
p4_commited: { | |
on: { | |
mousedown: { | |
actions: 'setVHSP5P6', | |
target: 'p5_commited' | |
} | |
} | |
}, | |
p5_commited: { | |
on: { | |
mousemove: { | |
actions: 'nudgeVHSP6', | |
target: 'p6_moving' | |
} | |
} | |
}, | |
p6_moving: { | |
on: { | |
mousemove: { actions: 'nudgeVHSP6' }, | |
mouseup: { // 結束編輯, 儲存 | |
actions: ['saveROI', 'clearSelection', send('saved')] | |
} | |
} | |
} | |
}, | |
on: { | |
saved: { target: 'idle' } | |
} | |
}, | |
angle_creation: { | |
initial: 'p1_commited', | |
states: { | |
p1_commited: { | |
on: { | |
mousemove: { | |
actions: 'nudgeAngleP2P3', | |
target: 'p2_moving' | |
} | |
} | |
}, | |
p2_moving: { | |
on: { | |
mousemove: { actions: 'nudgeAngleP2P3' }, | |
mouseup: { target: 'p2_commited' } | |
} | |
}, | |
p2_commited: { | |
on: { | |
mousemove: { actions: 'nudgeAngleP3' }, | |
mouseup: { actions: ['saveROI', 'clearSelection', send('saved')] } | |
} | |
} | |
}, | |
on: { | |
saved: { target: 'idle' } | |
} | |
}, | |
simple_shape_creation: { | |
initial: 'p1_commited', | |
states: { | |
p1_commited: { | |
on: { | |
mousemove: { | |
actions: 'nudgeSelection', | |
target: 'p2_moving' | |
}, | |
mouseup: { | |
actions: ['saveROI', 'clearSelection', send('saved')], | |
cond: 'isLabel' | |
} | |
} | |
}, | |
p2_moving: { | |
on: { | |
mousemove: { actions: 'nudgeSelection' }, | |
mouseup: { actions: ['saveROI', 'clearSelection', send('saved')] } | |
} | |
} | |
}, | |
on: { | |
saved: { target: 'idle' } | |
} | |
}, | |
decide_text_position: { | |
on: { | |
mousemove: { actions: 'nudgeSelection' }, | |
mouseup: { actions: 'prepareTextInput', target: 'edit_text' } | |
} | |
}, | |
edit_text: { | |
initial: 'normal_mode', | |
states: { | |
normal_mode: { | |
on: { | |
input: { actions: 'handleInput' }, | |
close: [ | |
{ | |
actions: ['discardROI', 'clearSelection', send('discard')], | |
cond: 'emptyText' | |
}, | |
{ | |
actions: ['saveROI', 'clearSelection', send('saved')] | |
} | |
], | |
begin_ime: { target: 'ime_mode' } | |
} | |
}, | |
ime_mode: { | |
on: { | |
input: { actions: 'handleInput' }, | |
end_ime: { target: 'normal_mode' } | |
} | |
} | |
}, | |
on: { | |
saved: { target: 'idle' }, | |
discard: { target: 'idle' } | |
} | |
} | |
}, | |
on: { | |
// 更新 props 時傳進來 machine | |
UPDATE: { actions: assign((ctx, evt) => ({ ...ctx, ...evt.content })) } | |
} | |
}, { | |
actions: { | |
setLastClientPoint: (ctx, evt) => { /*STUB*/ }, | |
selectElement: (ctx, evt) => { /*STUB*/ }, | |
setGroupIndex: (ctx, evt) => { /*STUB*/ }, | |
clearHighlights: (ctx) => { /*STUB*/ }, | |
highlightElement: (ctx, evt) => { /*STUB*/ }, | |
nudgeSelection: (ctx, evt) => { /*STUB*/ }, | |
createSimpleShape: (ctx, evt) => { /*STUB*/ }, | |
createText: (ctx, evt) => { /*STUB*/ }, | |
prepareTextInput: (ctx) => { /*STUB*/ }, | |
handleInput: (ctx, evt) => { /*STUB*/ }, | |
clearSelection: (ctx) => { /*STUB*/ }, | |
deleteSelectedROIs: (ctx) => { /*STUB*/ }, | |
saveROI: (ctx) => { /*STUB*/ }, | |
discardROI: (ctx) => { /*STUB*/ }, | |
createAngle: (ctx, evt) => { /*STUB*/ }, | |
nudgeVHSP2: nudgePoints([IDX.VHS.P2]), | |
nudgeVHSP4: nudgePoints([IDX.VHS.P4]), | |
nudgeVHSP6: nudgePoints([IDX.VHS.P6]), | |
setVHSP3P4: setPoints([IDX.VHS.P3, IDX.VHS.P4]), | |
setVHSP5P6: setPoints([IDX.VHS.P5, IDX.VHS.P6]), | |
nudgeAngleP2P3: nudgePoints([3, 4]), | |
nudgeAngleP3: nudgePoints([4]) | |
}, | |
guards: { | |
hitElement: (ctx, evt) => true, | |
isVHS: (ctx) => ctx.mode === 'vhs', | |
isAngle: (ctx) => ctx.mode === 'angle', | |
isText: (ctx) => ctx.mode === 'text', | |
isLabel: (ctx) => ctx.mode === 'label', | |
isSimpleShape: (ctx) => new Set(['line', 'arrow', 'ellipse', 'label']).has(ctx.mode), | |
emptyText: (ctx) => ctx.textValue === '', | |
targetIsText: (ctx, evt) => false | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment