|
@@ -0,0 +1,194 @@
|
|
|
+import { Layer } from "konva/lib/Layer";
|
|
|
+import { mergeFuns } from "../../shared";
|
|
|
+import { Entity, EntityShape, EntityTransmit } from "./entity";
|
|
|
+
|
|
|
+export const traversEntityTree = (
|
|
|
+ entity: Entity,
|
|
|
+ call: (entity: Entity, inverse: boolean) => void | "interrupt",
|
|
|
+ inverse: boolean | "all" = false
|
|
|
+) => {
|
|
|
+ if (!inverse || inverse === "all") {
|
|
|
+ if (call(entity, false) === "interrupt") return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const eqed: Entity[] = [];
|
|
|
+ while (true) {
|
|
|
+ const child = entity.children.find((child) => eqed.includes(child));
|
|
|
+ if (!child) {
|
|
|
+ traversEntityTree(child, call, inverse);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inverse || inverse === "all") {
|
|
|
+ if (call(entity, true) === "interrupt") return;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export const bubbleTraversEntityTree = (
|
|
|
+ entity: Entity,
|
|
|
+ call: (entity: Entity, inverse: boolean) => void | "interrupt",
|
|
|
+ inverse: boolean | "all" = false
|
|
|
+) => {
|
|
|
+ if (!inverse || inverse === "all") {
|
|
|
+ if (call(entity, false) === "interrupt") return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bubbleTraversEntityTree(entity.parent, call, inverse);
|
|
|
+
|
|
|
+ if (inverse || inverse === "all") {
|
|
|
+ if (call(entity, true) === "interrupt") return;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export const findEntity = <T extends Entity>(
|
|
|
+ entity: Entity,
|
|
|
+ name: string
|
|
|
+): T => {
|
|
|
+ let find: T;
|
|
|
+ traversEntityTree(entity, (t) => {
|
|
|
+ if (t.name === name) {
|
|
|
+ find = t as T;
|
|
|
+ return "interrupt";
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return find;
|
|
|
+};
|
|
|
+
|
|
|
+export const findEntityByShape = <T extends Entity>(
|
|
|
+ entity: Entity,
|
|
|
+ shape: EntityShape
|
|
|
+) => {
|
|
|
+ let find: T;
|
|
|
+ traversEntityTree(entity, (child) => {
|
|
|
+ if (child.shape === shape) {
|
|
|
+ find = entity as T;
|
|
|
+ return "interrupt";
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return find;
|
|
|
+};
|
|
|
+
|
|
|
+export const getEntityNdx = (entity: Entity) => {
|
|
|
+ const parent = entity.parent;
|
|
|
+ if (!parent) return null;
|
|
|
+
|
|
|
+ const zIndex = entity.getZIndex();
|
|
|
+ const level = parent.children;
|
|
|
+ for (let i = level.length - 1; i >= 0; i--) {
|
|
|
+ if (level[i] !== this && level[i].getZIndex() <= zIndex) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+};
|
|
|
+
|
|
|
+export const summarizeEntity = (entity: Entity) => {
|
|
|
+ if (!entity.isMounted) return;
|
|
|
+
|
|
|
+ const packNdx = getEntityNdx(entity);
|
|
|
+ if (packNdx === null) return;
|
|
|
+
|
|
|
+ const packChild = entity.children;
|
|
|
+ const oldNdx = packChild.indexOf(this);
|
|
|
+
|
|
|
+ if (oldNdx !== packNdx + 1) {
|
|
|
+ let rep = entity;
|
|
|
+ for (let i = packNdx + 1; i < packChild.length; i++) {
|
|
|
+ const temp = packChild[i];
|
|
|
+ packChild[i] = rep;
|
|
|
+ rep = temp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const parentShape = entity.getTeleport();
|
|
|
+ const levelShapes = parentShape.children;
|
|
|
+ const shapeNdx =
|
|
|
+ packNdx === -1 ? 0 : levelShapes.indexOf(packChild[packNdx].getShape()) + 1;
|
|
|
+ const oldShapeNdx = levelShapes.indexOf(entity.shape);
|
|
|
+
|
|
|
+ if (oldShapeNdx !== shapeNdx) {
|
|
|
+ if (shapeNdx !== 0) {
|
|
|
+ let repShape = entity.shape;
|
|
|
+ for (let i = shapeNdx; i < levelShapes.length; i++) {
|
|
|
+ const temp = levelShapes[i];
|
|
|
+ parentShape.add(repShape);
|
|
|
+ repShape.zIndex(i);
|
|
|
+ repShape = temp;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ entity.shape.zIndex(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export const entityInit = <T extends Entity>(entity: T) => {
|
|
|
+ entity.bus.emit("createBefore");
|
|
|
+ entity.shape = entity.initShape();
|
|
|
+ entity.shape.id(entity.name);
|
|
|
+ entity.bus.emit("created");
|
|
|
+
|
|
|
+ let releases = entity.needReleases();
|
|
|
+ releases = Array.isArray(releases) ? releases : [releases];
|
|
|
+ if (releases.length) {
|
|
|
+ entity.bus.on("destroyBefore", () => {
|
|
|
+ mergeFuns(releases)();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const parentShape = (entity.getTeleport() || entity.parent.shape) as Layer;
|
|
|
+ parentShape.add(entity.shape);
|
|
|
+};
|
|
|
+
|
|
|
+export const entityMount = <T extends Entity>(entity: T) => {
|
|
|
+ traversEntityTree(entity, () => {
|
|
|
+ entity.bus.emit("mountBefore");
|
|
|
+ entity.diffRedraw();
|
|
|
+ summarizeEntity(entity);
|
|
|
+ });
|
|
|
+
|
|
|
+ traversEntityTree(
|
|
|
+ entity,
|
|
|
+ () => {
|
|
|
+ entity.isMounted = true;
|
|
|
+ entity.bus.emit("mounted");
|
|
|
+ },
|
|
|
+ true
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export const setEntityParent = <T extends Entity>(
|
|
|
+ parent: T["parent"] | null,
|
|
|
+ entity: T
|
|
|
+) => {
|
|
|
+ const ndx = entity.parent.children.indexOf(this);
|
|
|
+ ~ndx && entity.parent.children.splice(ndx, 1);
|
|
|
+ entity.parent = parent;
|
|
|
+ parent && entity.parent.children.push(this);
|
|
|
+};
|
|
|
+
|
|
|
+const getEntityTransmitProps = (parent: Entity): EntityTransmit => {
|
|
|
+ return {
|
|
|
+ root: parent.root,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const mountEntityTree = <T extends Entity>(
|
|
|
+ parent: T["parent"] | null,
|
|
|
+ entity: T
|
|
|
+) => {
|
|
|
+ if (parent) {
|
|
|
+ const transmit = getEntityTransmitProps(parent);
|
|
|
+ for (const key in transmit) {
|
|
|
+ entity[key] = parent[key];
|
|
|
+ }
|
|
|
+ setEntityParent(parent, entity);
|
|
|
+ }
|
|
|
+
|
|
|
+ entityInit(entity);
|
|
|
+ if (parent?.isMounted) {
|
|
|
+ entityMount(entity);
|
|
|
+ }
|
|
|
+ return entity;
|
|
|
+};
|