2021-05-09 12:03:39 +00:00
|
|
|
import { createSelectorHook, createState } from "@state-designer/react"
|
2021-05-09 13:04:42 +00:00
|
|
|
import { clamp, screenToWorld } from "utils/utils"
|
2021-05-09 21:22:25 +00:00
|
|
|
import * as vec from "utils/vec"
|
2021-05-10 12:16:57 +00:00
|
|
|
import { Data } from "types"
|
|
|
|
import { defaultDocument } from "./data"
|
|
|
|
import * as Sessions from "./sessions"
|
2021-05-09 12:03:39 +00:00
|
|
|
|
2021-05-09 21:22:25 +00:00
|
|
|
const initialData: Data = {
|
2021-05-09 13:04:42 +00:00
|
|
|
camera: {
|
|
|
|
point: [0, 0],
|
|
|
|
zoom: 1,
|
|
|
|
},
|
2021-05-10 12:16:57 +00:00
|
|
|
brush: undefined,
|
|
|
|
pointedId: null,
|
|
|
|
selectedIds: [],
|
2021-05-09 21:22:25 +00:00
|
|
|
currentPageId: "page0",
|
2021-05-10 12:16:57 +00:00
|
|
|
document: defaultDocument,
|
2021-05-09 13:04:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const state = createState({
|
|
|
|
data: initialData,
|
|
|
|
on: {
|
|
|
|
ZOOMED_CAMERA: {
|
|
|
|
do: "zoomCamera",
|
|
|
|
},
|
|
|
|
PANNED_CAMERA: {
|
|
|
|
do: "panCamera",
|
|
|
|
},
|
|
|
|
},
|
2021-05-10 12:16:57 +00:00
|
|
|
initial: "selecting",
|
|
|
|
states: {
|
|
|
|
selecting: {
|
|
|
|
on: {
|
|
|
|
POINTED_CANVAS: { to: "brushSelecting" },
|
2021-05-12 09:48:35 +00:00
|
|
|
POINTED_SHAPE: [
|
|
|
|
"setPointedId",
|
|
|
|
{
|
|
|
|
if: "isPressingShiftKey",
|
|
|
|
then: {
|
|
|
|
if: "isPointedShapeSelected",
|
|
|
|
do: "pullPointedIdFromSelectedIds",
|
|
|
|
else: "pushPointedIdToSelectedIds",
|
|
|
|
},
|
|
|
|
else: ["clearSelectedIds", "pushPointedIdToSelectedIds"],
|
|
|
|
},
|
|
|
|
],
|
2021-05-10 12:16:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
brushSelecting: {
|
2021-05-11 10:13:07 +00:00
|
|
|
onEnter: [
|
2021-05-12 09:48:35 +00:00
|
|
|
{ unless: "isPressingShiftKey", do: "clearSelectedIds" },
|
2021-05-11 10:13:07 +00:00
|
|
|
"startBrushSession",
|
|
|
|
],
|
2021-05-10 12:16:57 +00:00
|
|
|
on: {
|
|
|
|
MOVED_POINTER: "updateBrushSession",
|
2021-05-10 20:44:17 +00:00
|
|
|
PANNED_CAMERA: "updateBrushSession",
|
2021-05-10 12:16:57 +00:00
|
|
|
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
|
|
|
|
CANCELLED: { do: "cancelSession", to: "selecting" },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-05-11 10:13:07 +00:00
|
|
|
conditions: {
|
2021-05-12 09:48:35 +00:00
|
|
|
isPointedShapeSelected(data) {
|
|
|
|
return data.selectedIds.includes(data.pointedId)
|
|
|
|
},
|
2021-05-11 10:13:07 +00:00
|
|
|
isPressingShiftKey(data, payload: { shiftKey: boolean }) {
|
|
|
|
return payload.shiftKey
|
|
|
|
},
|
|
|
|
},
|
2021-05-09 13:04:42 +00:00
|
|
|
actions: {
|
2021-05-10 12:16:57 +00:00
|
|
|
cancelSession(data) {
|
|
|
|
session.cancel(data)
|
|
|
|
session = undefined
|
|
|
|
},
|
|
|
|
completeSession(data) {
|
|
|
|
session.complete(data)
|
|
|
|
session = undefined
|
|
|
|
},
|
2021-05-10 20:44:17 +00:00
|
|
|
startBrushSession(data, payload: { point: number[] }) {
|
|
|
|
session = new Sessions.BrushSession(
|
|
|
|
data,
|
|
|
|
screenToWorld(payload.point, data)
|
|
|
|
)
|
2021-05-10 12:16:57 +00:00
|
|
|
},
|
2021-05-10 20:44:17 +00:00
|
|
|
updateBrushSession(data, payload: { point: number[] }) {
|
|
|
|
session.update(data, screenToWorld(payload.point, data))
|
2021-05-10 12:16:57 +00:00
|
|
|
},
|
2021-05-11 10:13:07 +00:00
|
|
|
// Selection
|
2021-05-12 09:48:35 +00:00
|
|
|
setPointedId(data, payload: { id: string }) {
|
|
|
|
data.pointedId = payload.id
|
|
|
|
},
|
|
|
|
clearPointedId(data) {
|
|
|
|
data.pointedId = undefined
|
|
|
|
},
|
|
|
|
clearSelectedIds(data) {
|
2021-05-11 10:13:07 +00:00
|
|
|
data.selectedIds = []
|
|
|
|
},
|
2021-05-12 09:48:35 +00:00
|
|
|
pullPointedIdFromSelectedIds(data) {
|
|
|
|
const { selectedIds, pointedId } = data
|
|
|
|
selectedIds.splice(selectedIds.indexOf(pointedId, 1))
|
|
|
|
},
|
|
|
|
pushPointedIdToSelectedIds(data) {
|
|
|
|
data.selectedIds.push(data.pointedId)
|
|
|
|
},
|
2021-05-11 10:13:07 +00:00
|
|
|
// Camera
|
2021-05-09 13:04:42 +00:00
|
|
|
zoomCamera(data, payload: { delta: number; point: number[] }) {
|
|
|
|
const { camera } = data
|
|
|
|
const p0 = screenToWorld(payload.point, data)
|
|
|
|
camera.zoom = clamp(
|
|
|
|
camera.zoom - (payload.delta / 100) * camera.zoom,
|
|
|
|
0.5,
|
|
|
|
3
|
|
|
|
)
|
|
|
|
const p1 = screenToWorld(payload.point, data)
|
|
|
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
|
|
|
},
|
|
|
|
panCamera(data, payload: { delta: number[]; point: number[] }) {
|
|
|
|
const { camera } = data
|
|
|
|
data.camera.point = vec.sub(
|
|
|
|
camera.point,
|
|
|
|
vec.div(payload.delta, camera.zoom)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
2021-05-11 10:13:07 +00:00
|
|
|
values: {
|
|
|
|
selectedIds(data) {
|
|
|
|
return new Set(data.selectedIds)
|
|
|
|
},
|
|
|
|
},
|
2021-05-09 13:04:42 +00:00
|
|
|
})
|
2021-05-09 12:03:39 +00:00
|
|
|
|
2021-05-10 12:16:57 +00:00
|
|
|
let session: Sessions.BaseSession
|
|
|
|
|
2021-05-09 12:03:39 +00:00
|
|
|
export default state
|
|
|
|
|
|
|
|
export const useSelector = createSelectorHook(state)
|