import { WholeLineAttrib, WholeLineLine, WholeLinePoint, WholeLinePointAttrib, WholeLinePolygonAttrib, } from "../view"; import { KonvaEventObject } from "konva/lib/Node"; import { getFlatChildren, getRealAbsoluteSize, round, shapeParentsEq, } from "../../../shared"; import { wholeLineAddPoint, wholeLineFixLineAddPoint, wholeLinePolygonLastAddPoint, } from "./whole-line-mouse"; import { Container } from "../../container"; import { Attrib, ShapeType } from "../../../type"; import { WholeLineChange, generateWholeLinePoygonId, getWholeLinePoint, getWholeLinePolygonPoints, mergeChange, wholeLineAddLineByPointIds, wholeLineDelPointByPointIds, wholeLineDelPolygon, } from "./whole-line-base"; import { getAdsorbPosition } from "../../../shared/adsorb"; export type PenWholeLinePoygonsEditProps< P extends Omit > = { tree: Container; onChange?: (change: WholeLineChange) => void; config: WholeLineAttrib

; polygonId?: string; adsorbRadius?: number; autoAdd?: boolean; closeAutoQuit?: boolean; pointAttribFactory?: (pos: number[]) => Omit; quotePoint?: boolean | ((point: WholeLinePointAttrib) => boolean); canOper?: ( tree: WholeLineLine | WholeLinePoint, operShape: ShapeType ) => boolean; canDelPoint?: (point: P, evt: KonvaEventObject) => boolean; changePolygon?: (polygonId: string) => void; autoClose?: boolean; }; /** * 钢笔模式编辑多边形 * @param polygonId */ export const penWholeLinePoygonsEdit = < P extends Omit >({ tree, config, polygonId, pointAttribFactory, quotePoint, canOper, autoAdd, adsorbRadius, canDelPoint, closeAutoQuit, changePolygon, autoClose, }: PenWholeLinePoygonsEditProps

) => { pointAttribFactory = pointAttribFactory || ((pos: number[]) => ({ x: pos[0], y: pos[1], } as P)); const getPolygonAttrib = (polygonId: string) => { if (!polygonId) { const id = generateWholeLinePoygonId(config); config.polygons.push({ id, lineIds: [], }); return config.polygons[config.polygons.length - 1]; } else { return config.polygons.find(({ id }) => id === polygonId); } }; let polyginAttrib: WholeLinePolygonAttrib; let newMode = false; let prevId: string | null = null; let canAddPolygon = true; const start = (currentPolygonId: string | null) => { polyginAttrib = getPolygonAttrib(currentPolygonId); if (!polyginAttrib) { throw `${currentPolygonId}的多边形不存在!`; } changePolygon && changePolygon(polyginAttrib.id); polygonId = polyginAttrib.id; newMode = !currentPolygonId; prevId = null; canAddPolygon = autoAdd || polyginAttrib.lineIds.length < 2; }; start(polygonId); const removePolygon = () => { const ndx = config.polygons.indexOf(polyginAttrib); if (~ndx) { config.polygons.splice(ndx, 1); } }; const continuous = ( evt: KonvaEventObject ): { change: WholeLineChange; isClose?: boolean } => { let change: WholeLineChange = { lineChange: { del: [], update: [], add: [], }, polygonChange: { add: [], update: [], del: [], }, pointChange: { add: [], del: [], }, }; const target = shapeParentsEq(evt.target, (shape) => { const id = shape.id(); return ( id.includes(WholeLineLine.namespace) || id.includes(WholeLinePoint.namespace) ); }); let child = target && tree.find(target.id()); const pixel = [evt.evt.offsetX, evt.evt.offsetY]; console.log(child); if (child instanceof WholeLineLine) { if (!canOper || canOper(child, evt.target)) { if (polyginAttrib.lineIds.includes(child.attrib.id)) { const { change: cChange } = wholeLineFixLineAddPoint( config, child.attrib.id, pointAttribFactory(tree.getRealFromStage(pixel)) ); return { change: mergeChange(change, cChange), isClose: false }; } } } let pointAttrib: P & Attrib; const polygonPoints = getWholeLinePolygonPoints(config, polyginAttrib.id); let isQuotePoint; while (true) { if (child instanceof WholeLinePoint) { if (canOper && !canOper(child, evt.target)) { return { change, isClose: false }; } isQuotePoint = typeof quotePoint === "function" ? quotePoint(child.attrib) : !!quotePoint; pointAttrib = isQuotePoint ? child.attrib : { ...child.attrib }; if (polyginAttrib.id === prevId) { return { change, isClose: false }; } } else { let position: number[]; if (adsorbRadius) { const points = polygonPoints.map(({ x, y }) => [x, y]); if (prevId) { const prev = getWholeLinePoint(config, prevId); points.push([prev.x, prev.y]); } position = getAdsorbPosition({ tree, pixel, radius: adsorbRadius, points: [...points].reverse(), }); console.log(position); } else { position = tree.getRealFromStage(pixel); } const flatChildren = getFlatChildren(tree); let i = 0; for (; i < flatChildren.length; i++) { if (flatChildren[i] instanceof WholeLinePoint) { const point = flatChildren[i] as WholeLinePoint; if ( round(point.attrib.x - position[0], 8) === 0 && round(point.attrib.y - position[1], 8) === 0 ) { child = tree.children[i]; break; } } } if (i !== flatChildren.length) { child = flatChildren[i]; continue; } pointAttrib = pointAttribFactory(position) as P & Attrib; } break; } const curNdx = polygonPoints.findIndex(({ id }) => id === pointAttrib.id); const isClose = curNdx === 0 && (!autoClose || polygonPoints.length >= 3); // 存在的情况下删除 if (curNdx >= 0 && !isClose) { const cChange = wholeLineDelPointByPointIds( config, pointAttrib.id, !canDelPoint || canDelPoint(pointAttrib, evt) ); change = mergeChange(change, cChange); if (polyginAttrib.lineIds.length === 0) { wholeLineDelPolygon(config, polyginAttrib.id); const repPoint = cChange.pointChange.del.find( ({ id }) => id !== pointAttrib.id ); start(null); const prev = wholeLineAddPoint(config, { ...repPoint, id: undefined }); prevId = prev.id; change.pointChange.add.push(prev); } return { change, isClose }; } if (!isQuotePoint) { delete pointAttrib.id; } const afterHandler = (iChange: WholeLineChange) => { // 线段闭合重新启一个多边形 if (!closeAutoQuit && isClose) { start(null); } return { change: iChange, isClose }; }; if (polyginAttrib.lineIds.length > 0) { if (newMode) { const mChange = wholeLinePolygonLastAddPoint( config, polygonId, pointAttrib ).change; // 持续添加模式 return afterHandler(mergeChange(change, mChange)); } else { if (canAddPolygon) { // 直接当成新建操作 start(null); return continuous(evt); } else { return { change, isClose }; } } } else if (prevId) { const [offset] = getRealAbsoluteSize(tree.stage, [4, 4], true); const prev = getWholeLinePoint(config, prevId); if ( !( Math.abs(pointAttrib.x - prev.x) > offset || Math.abs(pointAttrib.y - prev.y) > offset ) ) { return afterHandler(change); } const { line, change: mChange } = wholeLineAddLineByPointIds(config, [ prevId, wholeLineAddPoint(config, pointAttrib).id, ]); change = mergeChange(change, mChange, { polygonChange: { update: [ { before: { ...polyginAttrib, lineIds: [...polyginAttrib.lineIds] }, after: { ...polyginAttrib, lineIds: [...polyginAttrib.lineIds, line.id], }, }, ], }, pointChange: { add: [pointAttrib], }, }); polyginAttrib.lineIds.push(line.id); prevId = null; return afterHandler(change); } else { const addPoint = wholeLineAddPoint(config, pointAttrib)!; prevId = addPoint.id; change.pointChange.add.push(addPoint); return afterHandler(change); } }; const end = () => { // 没有两个点以上的多边形直接删除 if (polyginAttrib.lineIds.length === 0) { if (prevId) { wholeLineDelPointByPointIds(config, prevId); } removePolygon(); } changePolygon && changePolygon(null); }; const getStatus = () => ({ newMode, polyginAttribId: polyginAttrib.id, prevId, config, }); return { continuous, end, getStatus, setStatus: (status: ReturnType) => { newMode = status.newMode; polyginAttrib = status.config.polygons.find( ({ id }) => id === status.polyginAttribId ); polygonId = polyginAttrib.id; prevId = status.prevId; config = status.config; }, }; };