feat: improve snapping to grid in selector middleware

This commit is contained in:
chenshenhai 2024-06-15 13:49:40 +08:00
parent ee87f3c2e6
commit 9ea6699e3f
10 changed files with 375 additions and 62 deletions

View file

@ -307,12 +307,6 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
extend: true
});
if (modifiedOptions) {
// TODO
// this.#viewer.modifyViewVisibleInfoMap(data, {
// viewSizeInfo,
// viewScaleInfo,
// modifyOptions: modifiedOptions
// });
this.#viewer.resetViewVisibleInfoMap(data, {
viewSizeInfo,
viewScaleInfo

View file

@ -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);
}
}

View file

@ -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<PointSize[]>
export const keySelectedReferenceYLines = Symbol(`${key}_selectedReferenceYLines`); // Array<PointSize[]>
export const keyGroupQueue = Symbol(`${key}_groupQueue`); // Array<Element<'group'>> | []
export const keyGroupQueueVertexesList = Symbol(`${key}_groupQueueVertexesList`); // Array<ViewRectVertexes> | []
export const keyIsMoving = Symbol(`${key}_isMoving`); // boolean | null

View file

@ -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<DeepSelectorSharedStorage, Core
sharer.setSharedStorage(keySelectedElementListVertexes, null);
sharer.setSharedStorage(keySelectedElementController, null);
sharer.setSharedStorage(keySelectedElementPosition, []);
sharer.setSharedStorage(keySelectedReferenceXLines, []);
sharer.setSharedStorage(keySelectedReferenceYLines, []);
sharer.setSharedStorage(keyIsMoving, null);
sharer.setSharedStorage(keyEnableSelectInGroup, null);
};
@ -430,8 +426,6 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
},
pointMove: (e: PointWatcherEvent) => {
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<DeepSelectorSharedStorage, Core
if (is.y(referenceInfo.offsetY) && referenceInfo.offsetY !== null) {
totalMoveY = calculator.toGridNum(totalMoveY + referenceInfo.offsetY);
}
sharer.setSharedStorage(keySelectedReferenceXLines, referenceInfo.xLines);
sharer.setSharedStorage(keySelectedReferenceYLines, referenceInfo.yLines);
}
} catch (err) {
// eslint-disable-next-line no-console
@ -518,10 +510,6 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
});
}
});
// calculator.updateVisiableStatus({
// viewSizeInfo,
// viewScaleInfo
// });
sharer.setActiveStorage('data', data);
}
@ -613,10 +601,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
pointEnd(e: PointWatcherEvent) {
inBusyMode = null;
sharer.setSharedStorage(keySelectedReferenceXLines, []);
sharer.setSharedStorage(keySelectedReferenceYLines, []);
sharer.setSharedStorage(keyIsMoving, false);
const data = sharer.getActiveStorage('data');
const selectedElements = sharer.getSharedStorage(keySelectedElementList);
const hoverElement = sharer.getSharedStorage(keyHoverElement);
const resizeType = sharer.getSharedStorage(keyResizeType);
const actionType = sharer.getSharedStorage(keyActionType);
// const viewScaleInfo = sharer.getActiveViewScaleInfo();
@ -684,7 +672,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (type === 'resize') {
type = 'resizeElement';
}
eventHub.trigger(eventChange, { data, type });
eventHub.trigger(eventChange, { data, type, selectedElements, hoverElement });
}
viewer.drawFrame();
};
@ -787,13 +775,23 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
hideControllers: !!isMoving && actionType === 'drag'
});
if (actionType === 'drag') {
const xLines = sharer.getSharedStorage(keySelectedReferenceXLines);
const yLines = sharer.getSharedStorage(keySelectedReferenceYLines);
if (enableSnapToGrid === true) {
drawReferenceLines(overlayContext, {
xLines,
yLines
const referenceInfo = calcReferenceInfo(elem.uuid, {
calculator,
data: activeStore.data as Data,
groupQueue,
viewScaleInfo,
viewSizeInfo
});
if (referenceInfo) {
const { offsetX, offsetY, xLines, yLines } = referenceInfo;
if (offsetX === 0 || offsetY === 0) {
drawReferenceLines(overlayContext, {
xLines,
yLines
});
}
}
}
}
}
@ -821,13 +819,23 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
hideControllers: !!isMoving && actionType === 'drag'
});
if (actionType === 'drag') {
const xLines = sharer.getSharedStorage(keySelectedReferenceXLines);
const yLines = sharer.getSharedStorage(keySelectedReferenceYLines);
if (enableSnapToGrid === true) {
drawReferenceLines(overlayContext, {
xLines,
yLines
const referenceInfo = calcReferenceInfo(elem.uuid, {
calculator,
data: activeStore.data as Data,
groupQueue,
viewScaleInfo,
viewSizeInfo
});
if (referenceInfo) {
const { offsetX, offsetY, xLines, yLines } = referenceInfo;
if (offsetX === 0 || offsetY === 0) {
drawReferenceLines(overlayContext, {
xLines,
yLines
});
}
}
}
}
} else if (actionType === 'area' && areaStart && areaEnd) {

View file

@ -71,6 +71,154 @@ const getClosestNumInSortedKeys = (sortedKeys: number[], target: number) => {
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);

View file

@ -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<PointSize[]>;
[keySelectedReferenceYLines]: Array<PointSize[]>;
[keyIsMoving]: boolean | null;
[keyEnableSelectInGroup]: boolean | null;
[keyEnableSnapToGrid]: boolean | null;

View file

@ -253,9 +253,9 @@ export class iDraw {
core.trigger(eventKeys.change, { data, type: 'moveElement' });
}
async getImageBlobURL(opts: ExportImageFileBaseOptions): Promise<ExportImageFileResult> {
async getImageBlobURL(opts?: ExportImageFileBaseOptions): Promise<ExportImageFileResult> {
const data = this.getData() || { elements: [] };
const { devicePixelRatio } = opts;
const { devicePixelRatio } = opts || { devicePixelRatio: 1 };
const outputSize = calcElementListSize(data.elements);
const { viewSizeInfo } = this.getViewInfo();

View file

@ -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;

View file

@ -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';

View file

@ -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<Element<'group'>> = [];
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;
}