import { genBound, onlyId } from "@/utils/shared"; import { Draw } from "../components/container/use-draw"; import { SceneResource, getResource } from "./platform-resource"; import { getBaseItem } from "@/core/components/util"; import { LineData, defaultStyle } from "@/core/components/line"; import { getInitCtx, normalLineData, } from "@/core/components/line/attach-server"; import { getIconStyle, IconData } from "@/core/components/icon"; import { defaultStyle as textDefaultStyle } from "@/core/components/text"; import { Transform } from "konva/lib/Util"; import { ElMessage } from "element-plus"; import { ImageData } from "@/core/components/image"; import { Pos, Size } from "@/utils/math"; import { watchEffect } from "vue"; import { TextData } from "@/core/components/text"; import { SelectSceneData } from "../dialog/ai"; const getSizePlaceBoxImage = ({ width, height }: Size) => { const borderWidth = 10; const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d")!; // 画背景白色 ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, width, height); // 画边框 ctx.lineWidth = borderWidth; ctx.strokeStyle = "#000"; // 黑色边框 // 注意strokeRect位置和尺寸要配合线宽,这里我们让边框完整显示在canvas内 ctx.strokeRect( borderWidth / 2, borderWidth / 2, width - borderWidth, height - borderWidth ); return canvas.toDataURL(); // 返回图片的Data URL字符串 }; const getCoverShapes = (cover: SceneResource["cover"]) => { let geo: LineData = { ...getBaseItem(), lines: [], points: [], polygon: [], zIndex: -1, createTime: Date.now(), }; if (!cover) { return { geo }; } const geoCtx = getInitCtx(); cover.geos.forEach((item) => { let a: string = onlyId(), b: string; let i = 0; for (i = 0; i < item.length - 1; i++) { b = onlyId(); const lId = onlyId(); const l = { id: onlyId(), a, b, ...defaultStyle }; const p = { id: a, ...item[i] }; geoCtx.add.points[a] = p; geoCtx.add.lines[lId] = l; geo.points.push(p); geo.lines.push(l); a = b; } const p = { id: b!, ...item[i] }; geoCtx.add.points[b!] = p; geo.points.push(p); }); geo = normalLineData(geo, geoCtx); if (!cover.thumb) return { geo }; const width = cover.bound.x_max - cover.bound.x_min; const height = cover.bound.y_max - cover.bound.y_min; const mat = new Transform().translate( cover.bound.x_min + width / 2, cover.bound.y_min + height / 2 ); const thumb: ImageData = { ...getBaseItem(), createTime: geo.createTime, url: cover.thumb, mat: mat.m, width, height, cornerRadius: 0, zIndex: -2, }; return { geo, thumb }; }; const getTaggingShapes = async (taggings: SceneResource["taggings"]) => { const icons: IconData[] = []; const images: ImageData[] = []; const texts: TextData[] = []; const now = Date.now(); const reqs: Promise[] = []; for (let ndx = 0; ndx < taggings.length; ndx++) { const item = taggings[ndx]; const mat = new Transform(item.mat); if (!item.mat && item.position) { mat.translate(item.position.x, item.position.y); item.rotate && mat.rotate(item.rotate); } if (item.isText) { texts.push({ ...getBaseItem(), ...textDefaultStyle, content: item.url, zIndex: 1, mat: mat.m, }); continue; } const shape = { ...getBaseItem(), ...(item.size || { width: 100, height: 100 }), name: item.name, createTime: now + ndx, url: item.url, mat: mat.m, zIndex: 1, }; if (!item.url.includes(".svg")) { images.push(shape); continue; } reqs.push( getIconStyle(item.url, shape.width, shape.height, item.fixed) .then((style) => { icons.push({ ...shape, ...style }); }) .catch(() => {}) ); } await Promise.all(reqs) return { texts, images, icons, }; }; const getDrawResourceOffset = ( draw: Draw, bound: ReturnType, thumb?: ImageData ) => new Promise((resolve, reject) => { ElMessage.warning("请在画图面板中选择放置位置,按右键取消"); const data = thumb ? thumb : { url: getSizePlaceBoxImage(bound.get()!) }; const key = "place-plaform"; draw.enterDrawShape("image", { ...data, key }, true); const stopWatch = watchEffect(() => { if (draw.drawing) return; stopWatch(); const item = draw.store.items.find( (item) => item.key === key ) as ImageData; if (!item) { reject("用户已取消"); } else { draw.store.delItem("image", item.id); resolve({ x: item.mat[4], y: item.mat[5], }); } }); }); const drawSceneResource = async (resource: SceneResource, draw: Draw) => { const { geo, thumb } = getCoverShapes(resource.cover); const geoKey = "scene-geo-resource"; let offset = { x: 0, y: 0 }; let oldGeo = draw.store.getTypeItems("line")[0]; if (oldGeo?.itemName === geoKey) { const bound = genBound(); geo.points.forEach((p) => bound.update(p)); offset = await getDrawResourceOffset(draw, bound, thumb); } else if (oldGeo) { oldGeo.itemName = geoKey; } else { geo.itemName = geoKey; } const bound = genBound(); geo.points.forEach((p) => { p.x += offset.x; p.y += offset.y; bound.update(p); }); const { icons, images, texts } = await getTaggingShapes(resource.taggings); const tagShapes = [...icons, ...images, ...texts, thumb]; tagShapes.forEach((shape) => { if (shape) { shape.mat[4] += offset.x; shape.mat[5] += offset.y; bound.update({ x: shape.mat[4], y: shape.mat[5] }); } }); if (oldGeo) { oldGeo.points = oldGeo.points.concat(geo.points); oldGeo.lines = oldGeo.lines.concat(geo.lines); oldGeo.polygon = oldGeo.polygon.concat(geo.polygon); draw.store.setItem("line", { id: geo.id, value: geo }); } else { draw.store.addItem("line", geo); } console.log(icons, texts, images) draw.store.addItems("icon", icons); draw.store.addItems("text", texts); draw.store.addItems("image", images); if (thumb) { draw.store.addItem("image", thumb); } return bound.get()!; }; export const drawPlatformResource = async ( sceneData: SelectSceneData, draw: Draw ) => { // 默认为米,转为厘米 const resource = await getResource({ ...sceneData, scale: 100 }); let bound = null as ReturnType["get"]>; console.log(resource) await draw.history.onceTrack(async () => { draw.store.setConfig({ proportion: { scale: 10, unit: "mm" } }); bound = await drawSceneResource(resource, draw); if (typeof resource.compass === "number") { draw.store.setConfig({ compass: { rotation: resource.compass, url: draw.store.config.compass.url, }, }); } }); if (!draw.viewer.size || !bound) return; const size = draw.viewer.size; if (bound.width < 10 || bound.height < 10) { draw.viewer.setViewMat([ 1, 0, 0, 1, bound.center.x + size.width / 2, bound.center.y + size.height / 2, ]); } else { const viewWidth = Math.max(bound.width, size.width); const viewHeight = Math.max(bound.height, size.height); const padding = Math.max( Math.min((viewWidth - bound.width) / 2, (viewHeight - bound.height) / 2), 40 ); draw.viewer.setBound({ targetBound: bound, padding }); } };