Files
Aislo/B07_DesignDetail/openwebcad/src/tools/pedit-tool.ts
T

139 lines
3.8 KiB
TypeScript

import {toast} from 'react-toastify';
import {assign, createMachine, sendTo} from 'xstate';
import {PolyLineEntity} from '../entities/PolyLineEntity.ts';
import {
getActiveLayerId,
getNotSelectedEntities,
getSelectedEntities,
getSelectedEntityIds,
setAngleGuideOriginPoint,
setEntities,
setGhostHelperEntities,
setSelectedEntityIds,
setShouldDrawHelpers,
} from '../state';
import {Tool} from '../tools';
import {selectToolStateMachine} from './select-tool';
import type {StateEvent, ToolContext} from './tool.types';
export interface PeditContext extends ToolContext {}
export enum PeditState {
INIT = 'INIT',
CHECK_SELECTION = 'CHECK_SELECTION',
WAITING_FOR_SELECTION = 'WAITING_FOR_SELECTION',
CONVERT_SELECTION_TO_POLYLINE = 'CONVERT_SELECTION_TO_POLYLINE',
}
export enum PeditAction {
INIT_PEDIT_TOOL = 'INIT_PEDIT_TOOL',
DESELECT_ENTITIES = 'DESELECT_ENTITIES',
CONVERT_SELECTION_TO_POLYLINE = 'CONVERT_SELECTION_TO_POLYLINE',
}
/**
* Pedit tool state machine
* This state machine is responsible for combining entities into a polyline
* It uses the select tool state machine to select entities to combine
* When the user presses enter, the selected entities are combined into one polyline
*/
export const peditToolStateMachine = createMachine(
{
types: {} as {
context: PeditContext;
events: StateEvent;
},
context: {
type: Tool.PEDIT,
},
initial: PeditState.INIT,
states: {
[PeditState.INIT]: {
description: 'Initializing the pedit tool',
always: {
actions: PeditAction.INIT_PEDIT_TOOL,
target: PeditState.CHECK_SELECTION,
},
},
[PeditState.CHECK_SELECTION]: {
description: 'Check if there is something selected',
always: [
{
guard: () => {
return getSelectedEntityIds().length > 0;
},
target: PeditState.CONVERT_SELECTION_TO_POLYLINE,
},
{
guard: () => {
return getSelectedEntityIds().length === 0;
},
target: PeditState.WAITING_FOR_SELECTION,
},
],
},
[PeditState.WAITING_FOR_SELECTION]: {
description: 'Select the entities that you want to combine into a polyline',
meta: {
instructions: 'Select what you want to combine into a polyline, then ENTER',
},
invoke: {
id: 'selectToolInsideThePeditTool',
src: selectToolStateMachine,
onDone: {
target: PeditState.CHECK_SELECTION,
},
},
on: {
MOUSE_CLICK: {
// Forward the event to the select tool
actions: sendTo('selectToolInsideThePeditTool', ({ event }) => {
return event;
}),
},
ESC: {
actions: [PeditAction.DESELECT_ENTITIES, PeditAction.INIT_PEDIT_TOOL],
},
ENTER: {
// Forward the event to the select tool
actions: sendTo('selectToolInsideThePeditTool', ({ event }) => {
return event;
}),
},
DRAW: {
// Forward the event to the select tool
actions: sendTo('selectToolInsideThePeditTool', ({ event }) => {
return event;
}),
},
},
},
[PeditState.CONVERT_SELECTION_TO_POLYLINE]: {
always: {
actions: PeditAction.CONVERT_SELECTION_TO_POLYLINE,
target: PeditState.INIT,
},
},
},
},
{
actions: {
[PeditAction.INIT_PEDIT_TOOL]: assign(() => {
setShouldDrawHelpers(false);
setGhostHelperEntities([]);
setAngleGuideOriginPoint(null);
return {};
}),
[PeditAction.CONVERT_SELECTION_TO_POLYLINE]: assign(() => {
const newPolyLine = new PolyLineEntity(getActiveLayerId(), getSelectedEntities());
const newEntities = [...getNotSelectedEntities(), newPolyLine];
setEntities(newEntities, true);
setSelectedEntityIds([]);
toast.success(`Created polyline with ${newPolyLine.numberOfSegments()} segments`);
return {};
}),
...selectToolStateMachine.implementations.actions,
},
}
);