diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 79cc41a..ba8a8b8 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -307,12 +307,6 @@ export class Board { extend: true }); if (modifiedOptions) { - // TODO - // this.#viewer.modifyViewVisibleInfoMap(data, { - // viewSizeInfo, - // viewScaleInfo, - // modifyOptions: modifiedOptions - // }); this.#viewer.resetViewVisibleInfoMap(data, { viewSizeInfo, viewScaleInfo diff --git a/packages/board/src/lib/calculator.ts b/packages/board/src/lib/calculator.ts index 6bdca24..eaf7339 100644 --- a/packages/board/src/lib/calculator.ts +++ b/packages/board/src/lib/calculator.ts @@ -188,30 +188,53 @@ export class Calculator implements ViewCalculator { const viewVisibleInfoMap = this.#store.get('viewVisibleInfoMap'); if (type === 'deleteElement') { const { element } = content as ModifyOptions<'deleteElement'>['content']; - delete viewVisibleInfoMap[element.uuid]; - } else if (type === 'addElement' || type === 'updateElement') { + const uuids: string[] = []; + const _walk = (e: Element) => { + uuids.push(e.uuid); + if (e.type === 'group' && Array.isArray((e as Element<'group'>).detail.children)) { + (e as Element<'group'>).detail.children.forEach((child) => { + _walk(child); + }); + } + }; + _walk(element); + uuids.forEach((uuid) => { + delete viewVisibleInfoMap[uuid]; + }); + this.#store.set('viewVisibleInfoMap', viewVisibleInfoMap); + } + // else if (type === 'updateElement') { + // // TODO + // this.resetViewVisibleInfoMap(data, { viewScaleInfo, viewSizeInfo }); + // } + else if (type === 'addElement' || type === 'updateElement') { const { position } = content as ModifyOptions<'addElement'>['content']; const element = findElementFromListByPosition(position, data.elements); const groupQueue = getGroupQueueByElementPosition(list, position); if (element) { - const originRectInfo = calcElementOriginRectInfo(element, { - groupQueue: groupQueue || [] - }); - const newViewVisibleInfo: ViewVisibleInfo = { - originRectInfo, - rangeRectInfo: is.angle(element.angle) ? originRectInfoToRangeRectInfo(originRectInfo) : originRectInfo, - isVisibleInView: true, - isGroup: element?.type === 'group', - position: [...position] - }; - viewVisibleInfoMap[element.uuid] = newViewVisibleInfo; - if (type === 'updateElement') { - this.updateVisiableStatus({ viewScaleInfo, viewSizeInfo }); + if (type === 'updateElement' && element.type === 'group') { + // TODO + this.resetViewVisibleInfoMap(data, { viewScaleInfo, viewSizeInfo }); + } else { + const originRectInfo = calcElementOriginRectInfo(element, { + groupQueue: groupQueue || [] + }); + const newViewVisibleInfo: ViewVisibleInfo = { + originRectInfo, + rangeRectInfo: is.angle(element.angle) ? originRectInfoToRangeRectInfo(originRectInfo) : originRectInfo, + isVisibleInView: true, + isGroup: element?.type === 'group', + position: [...position] + }; + viewVisibleInfoMap[element.uuid] = newViewVisibleInfo; + this.#store.set('viewVisibleInfoMap', viewVisibleInfoMap); + if (type === 'updateElement') { + this.updateVisiableStatus({ viewScaleInfo, viewSizeInfo }); + } } } } else if (type === 'moveElement') { this.resetViewVisibleInfoMap(data, { viewScaleInfo, viewSizeInfo }); } - this.#store.set('viewVisibleInfoMap', viewVisibleInfoMap); } } diff --git a/packages/core/src/middleware/selector/config.ts b/packages/core/src/middleware/selector/config.ts index 2085e8c..6ff2498 100644 --- a/packages/core/src/middleware/selector/config.ts +++ b/packages/core/src/middleware/selector/config.ts @@ -11,8 +11,6 @@ export const keySelectedElementList = Symbol(`${key}_selectedElementList`); // A export const keySelectedElementListVertexes = Symbol(`${key}_selectedElementListVertexes`); // ViewRectVertexes | null export const keySelectedElementController = Symbol(`${key}_selectedElementController`); // ElementSizeController export const keySelectedElementPosition = Symbol(`${key}_selectedElementPosition`); // ElementPosition | [] -export const keySelectedReferenceXLines = Symbol(`${key}_selectedReferenceXLines`); // Array -export const keySelectedReferenceYLines = Symbol(`${key}_selectedReferenceYLines`); // Array export const keyGroupQueue = Symbol(`${key}_groupQueue`); // Array> | [] export const keyGroupQueueVertexesList = Symbol(`${key}_groupQueueVertexesList`); // Array | [] export const keyIsMoving = Symbol(`${key}_isMoving`); // boolean | null diff --git a/packages/core/src/middleware/selector/index.ts b/packages/core/src/middleware/selector/index.ts index 5d65d61..18fc082 100644 --- a/packages/core/src/middleware/selector/index.ts +++ b/packages/core/src/middleware/selector/index.ts @@ -12,7 +12,7 @@ import { getElementPositionFromList, deepResizeGroupElement } from '@idraw/util'; -import type { ViewRectVertexes, CoreEventMap, ElementPosition, ViewScaleInfo, ViewSizeInfo, ElementSizeController } from '@idraw/types'; +import type { Data, ViewRectVertexes, CoreEventMap, ElementPosition, ViewScaleInfo, ViewSizeInfo, ElementSizeController } from '@idraw/types'; import type { Point, PointSize, @@ -61,8 +61,6 @@ import { keySelectedElementListVertexes, keySelectedElementController, keySelectedElementPosition, - keySelectedReferenceXLines, - keySelectedReferenceYLines, keyIsMoving, keyEnableSelectInGroup, keyEnableSnapToGrid, @@ -175,8 +173,6 @@ export const MiddlewareSelector: BoardMiddleware { - sharer.setSharedStorage(keySelectedReferenceXLines, []); - sharer.setSharedStorage(keySelectedReferenceYLines, []); sharer.setSharedStorage(keyIsMoving, true); const data = sharer.getActiveStorage('data'); const elems = getActiveElements(); @@ -470,8 +464,6 @@ export const MiddlewareSelector: BoardMiddleware { const isEqualNum = (a: number, b: number) => Math.abs(a - b) < 0.00001; +// export function calcSnapOffsetInfo( +// uuid: string, +// opts: { +// data: Data; +// groupQueue: Element<'group'>[]; +// calculator: ViewCalculator; +// viewScaleInfo: ViewScaleInfo; +// viewSizeInfo: ViewSizeInfo; +// } +// ) { +// const { data, groupQueue, calculator, viewScaleInfo, viewSizeInfo } = opts; +// let targetElements: Element[] = data.elements || []; +// if (groupQueue?.length > 0) { +// targetElements = (groupQueue[groupQueue.length - 1] as Element<'group'>)?.detail?.children || []; +// } +// const siblingViewRectInfoList: ViewRectInfo[] = []; +// targetElements.forEach((elem: Element) => { +// if (elem.uuid !== uuid) { +// const info = calculator.calcViewRectInfoFromRange(elem.uuid, { checkVisible: true, viewScaleInfo, viewSizeInfo }); +// if (info) { +// siblingViewRectInfoList.push(info); +// } +// } +// }); + +// const targetRectInfo = calculator.calcViewRectInfoFromRange(uuid, { viewScaleInfo, viewSizeInfo }); + +// if (!targetRectInfo) { +// return null; +// } + +// const vTargetLineDotMap: DotMap = {}; // target vertical line dots +// const hTargetLineDotMap: DotMap = {}; // target horizontal line dots + +// const vRefLineDotMap: DotMap = {}; // reference vertical line dots +// const hRefLineDotMap: DotMap = {}; // reference horizontal line dots + +// let sortedRefXKeys: number[] = []; // hRefLineDotMap key nums +// let sortedRefYKeys: number[] = []; // vRefLineDotMap key nums + +// const targetBox = getViewBoxInfo(targetRectInfo); + +// vTargetLineDotMap[targetBox.minX] = [targetBox.minY, targetBox.midY, targetBox.maxY]; +// vTargetLineDotMap[targetBox.midX] = [targetBox.minY, targetBox.midY, targetBox.maxY]; +// vTargetLineDotMap[targetBox.maxX] = [targetBox.minY, targetBox.midY, targetBox.maxY]; + +// hTargetLineDotMap[targetBox.minY] = [targetBox.minX, targetBox.midX, targetBox.maxX]; +// hTargetLineDotMap[targetBox.midY] = [targetBox.minX, targetBox.midX, targetBox.maxX]; +// hTargetLineDotMap[targetBox.maxY] = [targetBox.minX, targetBox.midX, targetBox.maxX]; + +// siblingViewRectInfoList.forEach((info) => { +// const box = getViewBoxInfo(info); +// if (!vRefLineDotMap[box.minX]) { +// vRefLineDotMap[box.minX] = []; +// } +// if (!vRefLineDotMap[box.midX]) { +// vRefLineDotMap[box.midX] = []; +// } +// if (!vRefLineDotMap[box.maxX]) { +// vRefLineDotMap[box.maxX] = []; +// } +// if (!hRefLineDotMap[box.minY]) { +// hRefLineDotMap[box.minY] = []; +// } +// if (!hRefLineDotMap[box.midY]) { +// hRefLineDotMap[box.midY] = []; +// } +// if (!hRefLineDotMap[box.maxY]) { +// hRefLineDotMap[box.maxY] = []; +// } + +// vRefLineDotMap[box.minX] = [box.minY, box.midY, box.maxY]; +// vRefLineDotMap[box.midX] = [box.minY, box.midY, box.maxY]; +// vRefLineDotMap[box.maxX] = [box.minY, box.midY, box.maxY]; + +// sortedRefXKeys.push(box.minX); +// sortedRefXKeys.push(box.midX); +// sortedRefXKeys.push(box.maxX); + +// hRefLineDotMap[box.minY] = [box.minX, box.midX, box.maxX]; +// hRefLineDotMap[box.midY] = [box.minX, box.midX, box.maxX]; +// hRefLineDotMap[box.maxY] = [box.minX, box.midX, box.maxX]; + +// sortedRefYKeys.push(box.minY); +// sortedRefYKeys.push(box.midY); +// sortedRefYKeys.push(box.maxY); +// }); + +// sortedRefXKeys = sortedRefXKeys.sort((a, b) => a - b); +// sortedRefYKeys = sortedRefYKeys.sort((a, b) => a - b); + +// let offsetX: number | null = null; +// let offsetY: number | null = null; +// let closestMinX: number | null = null; +// let closestMidX: number | null = null; +// let closestMaxX: number | null = null; +// let closestMinY: number | null = null; +// let closestMidY: number | null = null; +// let closestMaxY: number | null = null; + +// if (sortedRefXKeys.length > 0) { +// closestMinX = getClosestNumInSortedKeys(sortedRefXKeys, targetBox.minX); +// closestMidX = getClosestNumInSortedKeys(sortedRefXKeys, targetBox.midX); +// closestMaxX = getClosestNumInSortedKeys(sortedRefXKeys, targetBox.maxX); + +// const distMinX = Math.abs(closestMinX - targetBox.minX); +// const distMidX = Math.abs(closestMidX - targetBox.midX); +// const distMaxX = Math.abs(closestMaxX - targetBox.maxX); +// const closestXDist = Math.min(distMinX, distMidX, distMaxX); + +// if (closestXDist <= unitSize / viewScaleInfo.scale) { +// if (isEqualNum(closestXDist, distMinX)) { +// offsetX = closestMinX - targetBox.minX; +// } else if (isEqualNum(closestXDist, distMidX)) { +// offsetX = closestMidX - targetBox.midX; +// } else if (isEqualNum(closestXDist, distMaxX)) { +// offsetX = closestMaxX - targetBox.maxX; +// } +// } +// } + +// if (sortedRefYKeys.length > 0) { +// closestMinY = getClosestNumInSortedKeys(sortedRefYKeys, targetBox.minY); +// closestMidY = getClosestNumInSortedKeys(sortedRefYKeys, targetBox.midY); +// closestMaxY = getClosestNumInSortedKeys(sortedRefYKeys, targetBox.maxY); + +// const distMinY = Math.abs(closestMinY - targetBox.minY); +// const distMidY = Math.abs(closestMidY - targetBox.midY); +// const distMaxY = Math.abs(closestMaxY - targetBox.maxY); +// const closestYDist = Math.min(distMinY, distMidY, distMaxY); + +// if (closestYDist <= unitSize / viewScaleInfo.scale) { +// if (isEqualNum(closestYDist, distMinY)) { +// offsetY = closestMinY - targetBox.minY; +// } else if (isEqualNum(closestYDist, distMidY)) { +// offsetY = closestMidY - targetBox.midY; +// } else if (isEqualNum(closestYDist, distMaxY)) { +// offsetY = closestMaxY - targetBox.maxY; +// } +// } +// } + +// return { +// offsetX, +// offsetY +// }; +// } + export function calcReferenceInfo( uuid: string, opts: { @@ -235,7 +383,7 @@ export function calcReferenceInfo( yList: [] }; vLine.yList.push(newTargetBox.minY); - vLine.yList.push(newTargetBox.midY); + // vLine.yList.push(newTargetBox.midY); vLine.yList.push(newTargetBox.maxY); vLine.yList.push(...(hRefLineDotMap?.[closestMinX] || [])); vHelperLineDotMapList.push(vLine); @@ -247,7 +395,7 @@ export function calcReferenceInfo( yList: [] }; vLine.yList.push(newTargetBox.minY); - vLine.yList.push(newTargetBox.midY); + // vLine.yList.push(newTargetBox.midY); vLine.yList.push(newTargetBox.maxY); vLine.yList.push(...(hRefLineDotMap?.[closestMidX] || [])); vHelperLineDotMapList.push(vLine); @@ -259,7 +407,7 @@ export function calcReferenceInfo( yList: [] }; vLine.yList.push(newTargetBox.minY); - vLine.yList.push(newTargetBox.midY); + // vLine.yList.push(newTargetBox.midY); vLine.yList.push(newTargetBox.maxY); vLine.yList.push(...(hRefLineDotMap?.[closestMaxX] || [])); vHelperLineDotMapList.push(vLine); @@ -273,7 +421,7 @@ export function calcReferenceInfo( xList: [] }; hLine.xList.push(newTargetBox.minX); - hLine.xList.push(newTargetBox.midX); + // hLine.xList.push(newTargetBox.midX); hLine.xList.push(newTargetBox.maxX); hLine.xList.push(...(vRefLineDotMap?.[closestMinY] || [])); hHelperLineDotMapList.push(hLine); @@ -284,7 +432,7 @@ export function calcReferenceInfo( xList: [] }; hLine.xList.push(newTargetBox.minX); - hLine.xList.push(newTargetBox.midX); + // hLine.xList.push(newTargetBox.midX); hLine.xList.push(newTargetBox.maxX); hLine.xList.push(...(vRefLineDotMap?.[closestMinY] || [])); hHelperLineDotMapList.push(hLine); @@ -295,7 +443,7 @@ export function calcReferenceInfo( xList: [] }; hLine.xList.push(newTargetBox.minX); - hLine.xList.push(newTargetBox.midX); + // hLine.xList.push(newTargetBox.midX); hLine.xList.push(newTargetBox.maxX); hLine.xList.push(...(vRefLineDotMap?.[closestMaxY] || [])); hHelperLineDotMapList.push(hLine); diff --git a/packages/core/src/middleware/selector/types.ts b/packages/core/src/middleware/selector/types.ts index c25dd7e..3c23b7f 100644 --- a/packages/core/src/middleware/selector/types.ts +++ b/packages/core/src/middleware/selector/types.ts @@ -28,8 +28,6 @@ import { keySelectedElementListVertexes, keySelectedElementController, keySelectedElementPosition, - keySelectedReferenceXLines, - keySelectedReferenceYLines, keyIsMoving, keyEnableSelectInGroup, keyEnableSnapToGrid, @@ -104,8 +102,6 @@ export type DeepSelectorSharedStorage = { [keySelectedElementListVertexes]: ViewRectVertexes | null; [keySelectedElementController]: ElementSizeController | null; [keySelectedElementPosition]: ElementPosition; - [keySelectedReferenceXLines]: Array; - [keySelectedReferenceYLines]: Array; [keyIsMoving]: boolean | null; [keyEnableSelectInGroup]: boolean | null; [keyEnableSnapToGrid]: boolean | null; diff --git a/packages/idraw/src/idraw.ts b/packages/idraw/src/idraw.ts index 0070104..9df3715 100644 --- a/packages/idraw/src/idraw.ts +++ b/packages/idraw/src/idraw.ts @@ -253,9 +253,9 @@ export class iDraw { core.trigger(eventKeys.change, { data, type: 'moveElement' }); } - async getImageBlobURL(opts: ExportImageFileBaseOptions): Promise { + async getImageBlobURL(opts?: ExportImageFileBaseOptions): Promise { const data = this.getData() || { elements: [] }; - const { devicePixelRatio } = opts; + const { devicePixelRatio } = opts || { devicePixelRatio: 1 }; const outputSize = calcElementListSize(data.elements); const { viewSizeInfo } = this.getViewInfo(); diff --git a/packages/types/src/lib/core.ts b/packages/types/src/lib/core.ts index 922db13..c5b80f8 100644 --- a/packages/types/src/lib/core.ts +++ b/packages/types/src/lib/core.ts @@ -36,6 +36,8 @@ export interface CoreEventSelect { export interface CoreEventChange { type: string; data: Data; + selectedElements?: Element[] | null; + hoverElement?: Element | null; } export interface CoreEventScale { scale: number; diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index da5795e..d2d1a8f 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -88,3 +88,4 @@ export { calcViewCenterContent, calcViewCenter } from './lib/view-content'; export { modifyElement, getModifiedElement } from './lib/modify'; // export { ModifyRecorder } from './lib/modify-recorder'; export { enhanceFontFamliy } from './lib/text'; +export { flatElementList } from './lib/flat'; diff --git a/packages/util/src/lib/flat.ts b/packages/util/src/lib/flat.ts new file mode 100644 index 0000000..cdb662b --- /dev/null +++ b/packages/util/src/lib/flat.ts @@ -0,0 +1,143 @@ +import type { Element, ElementSize, Elements, ViewRectVertexes } from '@idraw/types'; +import { calcElementVertexesInGroup } from './vertex'; +import { limitAngle, parseAngleToRadian, calcElementCenterFromVertexes, rotatePoint } from './rotate'; + +function flatElementSize( + elemSize: ElementSize, + opts: { + groupQueue: Element<'group'>[]; + } +): ElementSize { + const { groupQueue } = opts; + let { x, y, w, h, angle = 0 } = elemSize; + let totalAngle = 0; + groupQueue.forEach((group) => { + x += group.x; + y += group.y; + totalAngle += group.angle || 0; + }); + totalAngle = limitAngle(totalAngle); + if (totalAngle === 0) { + return { + x, + y, + w, + h, + angle + }; + } + totalAngle += elemSize.angle || 0; + totalAngle = limitAngle(totalAngle); + + const vertexes = calcElementVertexesInGroup(elemSize, { groupQueue }) as ViewRectVertexes; + const center = calcElementCenterFromVertexes(vertexes); + const start = rotatePoint(center, vertexes[0], parseAngleToRadian(0 - totalAngle)); + x = start.x; + y = start.y; + return { + x, + y, + w, + h, + angle: totalAngle + }; +} + +function isValidElement(elem: Element) { + if (['rect', 'circle'].includes(elem.type)) { + const detail = (elem as Element<'rect'>).detail; + if (!detail.background && !detail.borderWidth) { + return false; + } + if (detail.background === 'transparent' && !detail.borderWidth) { + return false; + } + } + if (['group'].includes(elem.type)) { + const detail = (elem as Element<'group'>).detail || {}; + const { children } = detail; + + if (!(children.length > 0) && !detail.background && !detail.borderWidth) { + return false; + } + if (!(children.length > 0) && detail.background === 'transparent' && !detail.borderWidth) { + return false; + } + } + if (elem.type === 'text') { + if (!(elem as Element<'text'>).detail.text) { + return false; + } + } + + if (elem.type === 'image') { + if (!(elem as Element<'image'>).detail.src) { + return false; + } + } + + if (elem.type === 'html') { + if (!(elem as Element<'html'>).detail.html) { + return false; + } + } + + if (elem.type === 'svg') { + if (!(elem as Element<'svg'>).detail.svg) { + return false; + } + } + + if (elem.type === 'path') { + const detail = (elem as Element<'path'>).detail; + if (!(detail?.commands?.length > 0)) { + return false; + } + } + + return true; +} + +export function flatElementList(list: Elements): Elements { + const elemeList: Elements = []; + const currentGroupQueue: Array> = []; + + const _resetElemSize = (elem: Element) => { + if (!isValidElement(elem)) { + return; + } + const newSize = flatElementSize(elem, { groupQueue: currentGroupQueue }); + const resizeElem = { + ...elem, + ...newSize + }; + + elemeList.push(resizeElem); + }; + + const _walk = (elem: Element) => { + if (elem?.operations?.invisible === true) { + return; + } + + if (elem.type === 'group') { + const { detail } = elem as Element<'group'>; + const { children, ...restDetail } = detail; + _resetElemSize({ ...elem, ...{ detail: { ...restDetail, children: [] } } }); + + currentGroupQueue.push(elem as Element<'group'>); + children.forEach((child) => { + _walk(child); + }); + currentGroupQueue.pop(); + } else { + _resetElemSize(elem); + } + }; + for (let i = 0; i < list.length; i++) { + const elem = list[i]; + _walk(elem); + } + + return elemeList; +}