| | |
| | | <template>
|
| | | <div style="display: flex; height: 90vh;">
|
| | | <!-- Sidebar -->
|
| | | <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px;">
|
| | | <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px; height: 93%; overflow-y: auto; max-height: 90vh; border-radius: 8px;">
|
| | | <div
|
| | | v-for="(layout, layoutIndex) in layouts"
|
| | | :key="layoutIndex"
|
| | | class="sidebar-item"
|
| | | @click="selectLayout(layoutIndex)"
|
| | | :class="{ 'selected': selectedLayoutIndex === layoutIndex }"
|
| | | v-for="(layout, layoutIndex) in layouts"
|
| | | :key="layoutIndex"
|
| | | class="sidebar-item"
|
| | | @click="selectLayout(layoutIndex)"
|
| | | :class="{ 'selected': selectedLayoutIndex === layoutIndex }" style="margin-bottom: 5px;"
|
| | | >
|
| | | {{ layout.width }} × {{ layout.height }} × {{ layout.quantity }}
|
| | | {{ layout.realWidth }} × {{ layout.realHeight }} × {{ layout.quantity }}
|
| | | </div>
|
| | | </div>
|
| | |
|
| | |
| | | </div>
|
| | |
|
| | | <!-- Layout Container -->
|
| | | <div class="layout-container" :style="layoutContainerStyle1(layoutIndex)">
|
| | | <div class="layout-container" :style="layoutContainerStyle(layoutIndex)">
|
| | | <!-- 灰色矩形 -->
|
| | | <div
|
| | |
| | | :key="`gray-${rectIndex}`"
|
| | | :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
|
| | | class="layout-glassDetail"
|
| | | :style="rectStyle(glassDetail, layoutIndex)"
|
| | | :style="rectStyle1(glassDetail, layoutIndex)"
|
| | | @contextmenu.prevent="handleGrayRectRightClick(layoutIndex, rectIndex,glassDetail)"
|
| | | >
|
| | | <!-- <div class="glassDetail-content">
|
| | |
| | | >
|
| | | <div class="glassDetail-content">
|
| | | <div class="size">{{ glassDetail.realWidth }}×{{ glassDetail.realHeight }}</div>
|
| | | <div>{{rectIndex }}</div>
|
| | | <div>{{glassDetail.polySort }}</div>
|
| | | <div v-if="showJiaHao" class="jia-hao">{{ glassDetail.rackNo }}</div>
|
| | | <div v-if="showProcessId" class="liuchengka">{{ glassDetail.processId }}</div>
|
| | | </div>
|
| | | </div>
|
| | | </div>
|
| | | </div>
|
| | | </div>
|
| | | </div>
|
| | |
|
| | | <!-- 提交按钮 -->
|
| | | <button @click="submitLayouts" style="position: fixed; bottom: 20px; right: 20px; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;">
|
| | | <button @click="submitLayouts" style="position: fixed; top: 90px; right: 20px; padding: 10px; background: #409eff; color: white; border: none; border-radius: 5px; cursor: pointer;">
|
| | | 保存调整
|
| | | </button>
|
| | | </div>
|
| | |
| | |
|
| | | <script setup>
|
| | | import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
| | | import { useRouter } from 'vue-router';
|
| | | import request from "@/utils/request";
|
| | | const router = useRouter();
|
| | | import { useI18n } from "vue-i18n";
|
| | | import { ElMessage, ElMessageBox } from "element-plus";
|
| | | import useUserInfoStore from "@/stores/userInfo";
|
| | |
| | | const submitLayouts = async () => {
|
| | | layouts.value.forEach(layout => {
|
| | | layout.glassDetails.forEach(glassDetail => {
|
| | | glassDetail.x = Math.round(glassDetail.x);
|
| | | glassDetail.y = Math.round(glassDetail.y);
|
| | | glassDetail.width = Math.round(glassDetail.width);
|
| | | glassDetail.height = Math.round(glassDetail.height);
|
| | | glassDetail.x = parseFloat(glassDetail.x.toFixed(2));
|
| | | glassDetail.y = parseFloat(glassDetail.y.toFixed(2));
|
| | | glassDetail.width = parseFloat(glassDetail.width.toFixed(2));
|
| | | glassDetail.height = parseFloat(glassDetail.height.toFixed(2));
|
| | | });
|
| | | });
|
| | | const savedProjectNo = localStorage.getItem('projectNo');
|
| | | const processId = savedProjectNo;
|
| | | layoutsHead.value.Layouts=layouts.value
|
| | | // 构造与原始数据结构一致的对象
|
| | | const saveData = {
|
| | | projectNo: processId,
|
| | | layouts: layouts.value,
|
| | | // 复制原始数据中的其他必要字段
|
| | | ...layoutsHead.value
|
| | | };
|
| | |
|
| | | // 确保 Layouts 字段是序列化的字符串
|
| | | const requestData = {
|
| | | Layouts: JSON.stringify(saveData)
|
| | | };
|
| | | request.post(`/glassOptimize/updateOptimizeResult/${processId}`, layoutsHead.value, {
|
| | | headers: {
|
| | | 'Content-Type': 'application/json'
|
| | |
| | | }).then((res) => {
|
| | | if (res.code == 200 && res.data === true) {
|
| | | ElMessage.success(t('basicData.msg.saveSuccess'));
|
| | | // // 保存成功后跳转到数控界面
|
| | | // router.push({ path: '/main/glassOptimize/OptimizeControl' });
|
| | | } else {
|
| | | ElMessage.warning(res.msg);
|
| | | }
|
| | |
| | | );
|
| | | return {
|
| | | position: 'absolute',
|
| | | left: `20px`,
|
| | | top: `140px`,
|
| | | left: `${layout.leftTrim * scale}px`,
|
| | | bottom: `${layout.upTrim * scale}px`,
|
| | | width: `${layout.width * scale}px`,
|
| | | height: `${layout.height * scale}px`,
|
| | | overflow: 'visible',
|
| | | border: '1px solid #ccc',
|
| | | //border: '1px solid #ccc',
|
| | | background: '#fff'
|
| | | };
|
| | | };
|
| | |
|
| | | const layoutContainerStyle1 = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const scale = Math.min(0.25
|
| | | );
|
| | | return {
|
| | | position: 'absolute',
|
| | | left: `20px`,
|
| | | top: `140px`,
|
| | | width: `${layout.realWidth * scale}px`,
|
| | | height: `${layout.realHeight * scale}px`,
|
| | | overflow: 'visible',
|
| | | //border: '1px solid #ccc',
|
| | | background: '#fff'
|
| | | };
|
| | | };
|
| | |
| | | return {
|
| | | position: 'absolute',
|
| | | left: `${glassDetail.x * scale}px`,
|
| | | bottom: `${glassDetail.y * scale}px`,
|
| | | top: `${glassDetail.y * scale}px`,
|
| | | width: `${glassDetail.width * scale}px`,
|
| | | height: `${glassDetail.height * scale}px`,
|
| | | backgroundColor: glassDetail.isRemain ? '#f0f0f0' : themeColor.value,
|
| | | border: '1px solid #000',
|
| | | cursor: 'pointer',
|
| | | draggable: !glassDetail.isRemain,
|
| | | zIndex: glassDetail.isRemain ? 1 : 2
|
| | | };
|
| | | };
|
| | |
|
| | | const rectStyle1 = (glassDetail, layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const scale = Math.min(0.25
|
| | | );
|
| | | return {
|
| | | position: 'absolute',
|
| | | left: `${glassDetail.x * scale}px`,
|
| | | top: `${glassDetail.y * scale}px`,
|
| | | width: `${glassDetail.width * scale}px`,
|
| | | height: `${glassDetail.height * scale}px`,
|
| | | backgroundColor: glassDetail.isRemain ? '#f0f0f0' : themeColor.value,
|
| | |
| | | moveUpAndRotateItem.textContent = '向上移动并旋转';
|
| | | moveUpAndRotateItem.style.cursor = 'pointer';
|
| | | moveUpAndRotateItem.addEventListener('click', () => {
|
| | | moveRectAndRotate(layoutIndex, rectIndex, 'up');
|
| | | moveRectAndRotate(layoutIndex, rectIndex, 'down');
|
| | | document.body.removeChild(contextMenu);
|
| | | });
|
| | |
|
| | |
| | | moveDownAndRotateItem.textContent = '向下移动并旋转';
|
| | | moveDownAndRotateItem.style.cursor = 'pointer';
|
| | | moveDownAndRotateItem.addEventListener('click', () => {
|
| | | moveRectAndRotate(layoutIndex, rectIndex, 'down');
|
| | | moveRectAndRotate(layoutIndex, rectIndex, 'up');
|
| | | document.body.removeChild(contextMenu);
|
| | | });
|
| | |
|
| | |
| | | moveUpItem.textContent = '向上移动';
|
| | | moveUpItem.style.cursor = 'pointer';
|
| | | moveUpItem.addEventListener('click', () => {
|
| | | moveRect(layoutIndex, rectIndex, 'up');
|
| | | moveRect(layoutIndex, rectIndex, 'down');
|
| | | document.body.removeChild(contextMenu);
|
| | | });
|
| | |
|
| | |
| | | moveDownItem.textContent = '向下移动';
|
| | | moveDownItem.style.cursor = 'pointer';
|
| | | moveDownItem.addEventListener('click', () => {
|
| | | moveRect(layoutIndex, rectIndex, 'down');
|
| | | moveRect(layoutIndex, rectIndex, 'up');
|
| | | document.body.removeChild(contextMenu);
|
| | | });
|
| | |
|
| | |
| | | const rectIndex = dragRect.value.rectIndex;
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const glassDetail = layout.glassDetails[rectIndex];
|
| | |
|
| | | // 保存原始坐标用于计算偏移量
|
| | | const originalX = glassDetail.x;
|
| | | const originalY = glassDetail.y;
|
| | |
|
| | | const scale = Math.min(
|
| | | (props.gw - 100) / layout.width,
|
| | | (props.gh - 100) / layout.height
|
| | |
| | | if (isValidMove) {
|
| | | glassDetail.x = newRect.x;
|
| | | glassDetail.y = newRect.y;
|
| | |
|
| | | // 更新glassPoint坐标
|
| | | if (glassDetail.glassPoint && Array.isArray(glassDetail.glassPoint)) {
|
| | | const offsetX = glassDetail.x - originalX;
|
| | | const offsetY = glassDetail.y - originalY;
|
| | |
|
| | | glassDetail.glassPoint.forEach(point => {
|
| | | point.X += offsetX;
|
| | | point.Y += offsetY;
|
| | | // 添加精度控制
|
| | | point.X = parseFloat(point.X.toFixed(2));
|
| | | point.Y = parseFloat(point.Y.toFixed(2));
|
| | | });
|
| | | }
|
| | |
|
| | | dragStartPos.value = {
|
| | | x: event.clientX,
|
| | | y: event.clientY
|
| | |
| | | (props.gh - 100) / layout.height
|
| | | );
|
| | |
|
| | | glassDetail.x = Math.round(glassDetail.x);
|
| | | glassDetail.y = Math.round(glassDetail.y);
|
| | | glassDetail.x = parseFloat(glassDetail.x.toFixed(2));
|
| | | glassDetail.y = parseFloat(glassDetail.y.toFixed(2));
|
| | | adjustAlignmentPosition(layoutIndex, rectIndex);
|
| | | }
|
| | |
|
| | |
| | | if (Math.abs(glassDetail.y - otherRect.y) < threshold) {
|
| | | glassDetail.y = Math.round((glassDetail.y + otherRect.y) / 2);
|
| | | }
|
| | | // 垂直对齐下边缘
|
| | | // 垂直对齐上边缘
|
| | | if (Math.abs((glassDetail.y + glassDetail.height) - (otherRect.y + otherRect.height)) < threshold) {
|
| | | glassDetail.y = Math.round((otherRect.y + otherRect.height - glassDetail.height));
|
| | | }
|
| | |
| | | adjustGrayRectangles(layoutIndex);
|
| | | };
|
| | |
|
| | | const mergeAdjacentGrayRects = (glassDetails,totalWidth,totalHeight) => {
|
| | | const mergeAdjacentGrayRects = (glassDetails, totalWidth, totalHeight) => {
|
| | | const grayRects = glassDetails.filter(r => r.isRemain);
|
| | | const grayRects2 = glassDetails.filter(r => r.isRemain);
|
| | | let merged = [];
|
| | | const nonGrayRects = glassDetails.filter(r => !r.isRemain);
|
| | |
|
| | | // 按坐标排序,优先按y坐标,其次按x坐标(这样更符合从上到下、从左到右的阅读习惯)
|
| | | grayRects.sort((a, b) => {
|
| | | if (a.x !== b.x) return a.x - b.x;
|
| | | return a.y - b.y;
|
| | | if (a.y !== b.y) return a.y - b.y;
|
| | | return a.x - b.x;
|
| | | });
|
| | |
|
| | | if (grayRects.length === 0) return;
|
| | |
|
| | | merged.push({ ...grayRects[0] });
|
| | | const merged = [];
|
| | | let current = { ...grayRects[0] };
|
| | |
|
| | | // 遍历所有余料矩形进行合并
|
| | | for (let i = 1; i < grayRects.length; i++) {
|
| | | const last = merged[merged.length-1];
|
| | | const current = grayRects[i];
|
| | | const next = grayRects[i];
|
| | |
|
| | |
|
| | | if (current.x === last.x + last.width &&
|
| | | current.y === last.y &&
|
| | | current.height === last.height) {
|
| | | last.width += current.width;
|
| | | last.x = Math.round(last.x);
|
| | | last.y = Math.round(last.y);
|
| | | last.width = Math.round(last.width);
|
| | | last.height = Math.round(last.height);
|
| | | } else if (current.y === last.y + last.height &&
|
| | | current.x === last.x &&
|
| | | current.width === last.width) {
|
| | | last.height += current.height;
|
| | | last.x = Math.round(last.x);
|
| | | last.y = Math.round(last.y);
|
| | | last.width = Math.round(last.width);
|
| | | last.height = Math.round(last.height);
|
| | | } else {
|
| | | // 检查是否可以水平合并(同一行,高度相同,相邻)
|
| | | if (current.y === next.y &&
|
| | | current.height === next.height &&
|
| | | current.x + current.width === next.x) {
|
| | | // 水平合并
|
| | | current.width += next.width;
|
| | | }
|
| | | // 检查是否可以垂直合并(同一列,宽度相同,相邻)
|
| | | else if (current.x === next.x &&
|
| | | current.width === next.width &&
|
| | | current.y + current.height === next.y) {
|
| | | // 垂直合并
|
| | | current.height += next.height;
|
| | | }
|
| | | else {
|
| | | // 无法合并,保存当前矩形,开始新的合并
|
| | | merged.push({
|
| | | x: Math.round(current.x),
|
| | | y: Math.round(current.y),
|
| | | width: Math.round(current.width),
|
| | | height: Math.round(current.height),
|
| | | x: current.x,
|
| | | y: current.y,
|
| | | width: current.width,
|
| | | height: current.height,
|
| | | isRemain: true
|
| | | });
|
| | | current = { ...next };
|
| | | }
|
| | | }
|
| | |
|
| | | const nonGray = glassDetails.filter(r => !r.isRemain);
|
| | | //删除原数组拼接新的小片跟余料
|
| | | glassDetails.splice(0, glassDetails.length, ...nonGray, ...merged);
|
| | | // 添加最后一个矩形
|
| | | merged.push({
|
| | | x: current.x,
|
| | | y: current.y,
|
| | | width: current.width,
|
| | | height: current.height,
|
| | | isRemain: true
|
| | | });
|
| | |
|
| | | // 重新构建数组:非余料 + 合并后的余料
|
| | | glassDetails.splice(0, glassDetails.length, ...nonGrayRects, ...merged);
|
| | | };
|
| | |
|
| | | // 确保返回的区域不重叠
|
| | | const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => {
|
| | | // 从整个原片开始
|
| | | let remaining = [{ x: 0, y: 0, width: totalWidth, height: totalHeight }];
|
| | |
|
| | | // 逐个处理障碍物(已放置的玻璃片)
|
| | | obstacles.forEach(obstacle => {
|
| | | remaining = cutRemainingAreas(remaining, obstacle, totalWidth, totalHeight);
|
| | | });
|
| | |
|
| | | // 对结果进行排序,确保一致性
|
| | | remaining.sort((a, b) => {
|
| | | if (a.x !== b.x) return a.x - b.x;
|
| | | return a.y - b.y;
|
| | | });
|
| | |
|
| | | return remaining;
|
| | | };
|
| | |
|
| | |
|
| | | // 调整后重新计算灰色余料
|
| | | const adjustGrayRectangles = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const glassDetails = layout.glassDetails;
|
| | | //小片的数据
|
| | |
|
| | | // 1. 筛选出非余料的玻璃片(即实际要切割的玻璃片)
|
| | | const nonGrayRects = glassDetails.filter(glassDetail => !glassDetail.isRemain);
|
| | |
|
| | | //所有的小片余料坐标跟尺寸
|
| | | // 2. 计算剩余可用区域
|
| | | let remainingAreas = calculateRemainingAreas(layout.width, layout.height, nonGrayRects);
|
| | | const uniqueArr = Array.from(
|
| | | new Set(remainingAreas.map(item => JSON.stringify(item)))
|
| | | ).map(item => JSON.parse(item));
|
| | | //余料的数据
|
| | |
|
| | | // 3. 去重处理 - 更严格的去重逻辑
|
| | | const uniqueArr = removeDuplicateAreas(remainingAreas);
|
| | |
|
| | | // 4. 获取当前已存在的余料矩形(需要保留引用以便更新)
|
| | | const currentGrayRects = glassDetails.filter(r => r.isRemain);
|
| | | //循环余料数据跟全部的对比
|
| | | currentGrayRects.forEach((_, index) => {
|
| | | if (index >= remainingAreas.length) {
|
| | | glassDetails.splice(index, 1);
|
| | | }
|
| | |
|
| | | // 5. 清除所有现有的余料矩形
|
| | | // 先收集非余料矩形
|
| | | const nonRemainRects = glassDetails.filter(r => !r.isRemain);
|
| | |
|
| | | // 6. 重新构建玻璃详情数组
|
| | | // 保留非余料矩形
|
| | | const newGlassDetails = [...nonRemainRects];
|
| | |
|
| | | // 添加新的余料矩形
|
| | | uniqueArr.forEach((area) => {
|
| | | newGlassDetails.push({
|
| | | x: area.x,
|
| | | y: area.y,
|
| | | width: area.width,
|
| | | height: area.height,
|
| | | isRemain: true
|
| | | });
|
| | | });
|
| | |
|
| | | uniqueArr.forEach((area, index) => {
|
| | | if (index < currentGrayRects.length) {
|
| | | currentGrayRects[index].x = Math.round(area.x);
|
| | | currentGrayRects[index].y = Math.round(area.y);
|
| | | currentGrayRects[index].width = Math.round(area.width);
|
| | | currentGrayRects[index].height = Math.round(area.height);
|
| | | } else {
|
| | | glassDetails.push({
|
| | | x: Math.round(area.x),
|
| | | y: Math.round(area.y),
|
| | | width: Math.round(area.width),
|
| | | height: Math.round(area.height),
|
| | | isRemain: true
|
| | | });
|
| | | }
|
| | | });
|
| | | // 7. 更新布局的玻璃详情
|
| | | layout.glassDetails = newGlassDetails;
|
| | |
|
| | | mergeAdjacentGrayRects(glassDetails,layout.width, layout.height);
|
| | | // 8. 合并相邻的余料矩形
|
| | | mergeAdjacentGrayRects(layout.glassDetails, layout.width, layout.height);
|
| | | };
|
| | |
|
| | |
|
| | | const removeDuplicateAreas = (areas) => {
|
| | | const result = [];
|
| | |
|
| | | areas.forEach(area => {
|
| | | // 检查是否与已存在的区域重叠或相等
|
| | | const isDuplicate = result.some(existingArea => {
|
| | | return (
|
| | | existingArea.x === area.x &&
|
| | | existingArea.y === area.y &&
|
| | | existingArea.width === area.width &&
|
| | | existingArea.height === area.height
|
| | | );
|
| | | });
|
| | |
|
| | | if (!isDuplicate) {
|
| | | result.push(area);
|
| | | }
|
| | | });
|
| | |
|
| | | return result;
|
| | | };
|
| | |
|
| | |
|
| | |
|
| | | //旋转方法
|
| | | const rotateRect = (layoutIndex, rectIndex) => {
|
| | |
| | | }
|
| | |
|
| | | if (isValidRotation) {
|
| | | // 更新glassPoint坐标(如果存在)
|
| | | if (glassDetail.glassPoint && Array.isArray(glassDetail.glassPoint)) {
|
| | | // 保存原始点坐标
|
| | | const originalPoints = JSON.parse(JSON.stringify(glassDetail.glassPoint));
|
| | |
|
| | | // 旋转点坐标(以矩形左上角为原点的旋转)
|
| | | glassDetail.glassPoint.forEach((point, index) => {
|
| | | // 计算相对于矩形左上角的坐标
|
| | | const relX = originalPoints[index].X - originalState.x;
|
| | | const relY = originalPoints[index].Y - originalState.y;
|
| | |
|
| | | // 旋转90度后的坐标(顺时针)
|
| | | point.X = originalState.x + relY;
|
| | | point.Y = originalState.y + (originalState.width - relX);
|
| | | // 添加精度控制
|
| | | point.X = parseFloat(point.X.toFixed(2));
|
| | | point.Y = parseFloat(point.Y.toFixed(2));
|
| | | });
|
| | | }
|
| | | adjustGrayRectangles(layoutIndex);
|
| | | } else {
|
| | | glassDetail.width = originalState.width;
|
| | |
| | | return;
|
| | | }
|
| | |
|
| | |
|
| | | // 保存原始坐标
|
| | | const originalX = glassDetail.x;
|
| | | const originalY = glassDetail.y;
|
| | |
|
| | | switch (direction) {
|
| | | case 'up':
|
| | | glassDetail.y += maxStep;
|
| | |
| | | }
|
| | |
|
| | | if (isValidMove) {
|
| | | // 更新glassPoint坐标
|
| | | if (glassDetail.glassPoint && Array.isArray(glassDetail.glassPoint)) {
|
| | | const offsetX = glassDetail.x - originalX;
|
| | | const offsetY = glassDetail.y - originalY;
|
| | |
|
| | | glassDetail.glassPoint.forEach(point => {
|
| | | point.X += offsetX;
|
| | | point.Y += offsetY;
|
| | | // 添加精度控制
|
| | | point.X = parseFloat(point.X.toFixed(2));
|
| | | point.Y = parseFloat(point.Y.toFixed(2));
|
| | | });
|
| | | }
|
| | | adjustGrayRectangles(layoutIndex);
|
| | | } else {
|
| | | glassDetail.x = originalState.x;
|
| | |
| | | };
|
| | |
|
| | | //重新计算余料坐标以及尺寸1
|
| | | const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => {
|
| | | let remaining = [{ x: 0, y: 0, width: totalWidth, height: totalHeight }];
|
| | | obstacles.forEach(glassDetail => {
|
| | | remaining = cutRemainingAreas(remaining, glassDetail,totalWidth,totalHeight);
|
| | | });
|
| | | return remaining;
|
| | | };
|
| | | // const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => {
|
| | | // let remaining = [{ x: 0, y: 0, width: totalWidth, height: totalHeight }];
|
| | | // obstacles.forEach(glassDetail => {
|
| | | // remaining = cutRemainingAreas(remaining, glassDetail,totalWidth,totalHeight);
|
| | | // });
|
| | | // return remaining;
|
| | | // };
|
| | |
|
| | | //重新计算余料坐标以及尺寸2
|
| | | const cutRemainingAreas = (remainingAreas, obstacle,totalWidth,totalHeight) => {
|
| | | const cutRemainingAreas = (remainingAreas, obstacle, totalWidth, totalHeight) => {
|
| | | const newRemaining = [];
|
| | | remainingAreas.forEach(area => {
|
| | | if (checkOverlap(area, obstacle)) {
|
| | | if (obstacle.x > area.x) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: area.y,
|
| | | width: obstacle.x - area.x,
|
| | | height: area.height
|
| | | });
|
| | | }
|
| | | if (obstacle.x + obstacle.width < area.x + area.width) {
|
| | | newRemaining.push({
|
| | | x: obstacle.x + obstacle.width,
|
| | | y: area.y,
|
| | | width: area.width - (obstacle.x + obstacle.width - area.x),
|
| | | height: area.height
|
| | | });
|
| | | }
|
| | | if (obstacle.y > area.y) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: area.y,
|
| | | width: area.width,
|
| | | height: obstacle.y - area.y
|
| | | });
|
| | | }
|
| | | if (obstacle.y + obstacle.height < area.y + area.height ) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: obstacle.y + obstacle.height,
|
| | | width: area.width,
|
| | | height: area.height - (obstacle.y + obstacle.height - area.y)
|
| | | });
|
| | |
|
| | | }
|
| | | } else {
|
| | | remainingAreas.forEach(area => {
|
| | | // 如果障碍物与当前区域没有重叠,保留原区域
|
| | | if (!checkOverlap(area, obstacle)) {
|
| | | newRemaining.push(area);
|
| | | return;
|
| | | }
|
| | |
|
| | | // 计算重叠区域的边界
|
| | | const overlapLeft = Math.max(area.x, obstacle.x);
|
| | | const overlapRight = Math.min(area.x + area.width, obstacle.x + obstacle.width);
|
| | | const overlapTop = Math.max(area.y, obstacle.y);
|
| | | const overlapBottom = Math.min(area.y + area.height, obstacle.y + obstacle.height);
|
| | |
|
| | | // 生成四个可能的新区域(上、下、左、右)
|
| | |
|
| | | // 上方区域
|
| | | if (overlapTop > area.y) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: area.y,
|
| | | width: area.width,
|
| | | height: overlapTop - area.y
|
| | | });
|
| | | }
|
| | |
|
| | | // 下方区域
|
| | | if (overlapBottom < area.y + area.height) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: overlapBottom,
|
| | | width: area.width,
|
| | | height: area.y + area.height - overlapBottom
|
| | | });
|
| | | }
|
| | |
|
| | | // 左方区域(仅在重叠区域的垂直范围内)
|
| | | if (overlapLeft > area.x) {
|
| | | const regionTop = overlapTop;
|
| | | const regionBottom = overlapBottom;
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | y: regionTop,
|
| | | width: overlapLeft - area.x,
|
| | | height: regionBottom - regionTop
|
| | | });
|
| | | }
|
| | |
|
| | | // 右方区域(仅在重叠区域的垂直范围内)
|
| | | if (overlapRight < area.x + area.width) {
|
| | | const regionTop = overlapTop;
|
| | | const regionBottom = overlapBottom;
|
| | | newRemaining.push({
|
| | | x: overlapRight,
|
| | | y: regionTop,
|
| | | width: area.x + area.width - overlapRight,
|
| | | height: regionBottom - regionTop
|
| | | });
|
| | | }
|
| | | });
|
| | |
|
| | | return newRemaining;
|
| | | };
|
| | |
|
| | |
| | | if (!glassDetail) return '';*/
|
| | | const totalRects = layouts.value.length;
|
| | | const currentRectIndex = layoutIndex + 1;
|
| | | const width = layout.width;
|
| | | const height = layout.height;
|
| | | const width = layout.realWidth;
|
| | | const height = layout.realHeight;
|
| | | const percentage = (layout.usageRate * 100).toFixed(2) + '%';
|
| | | return `${currentRectIndex}/${totalRects} ${width}×${height} ×1 ${percentage}`;
|
| | | };
|
| | |
| | |
|
| | | //向上移动计算坐标
|
| | | const getAvailableSpaceUp = (glassDetail, layout, obstacles) => {
|
| | | let maxSpace = layout.height - (glassDetail.y + glassDetail.height);
|
| | | let maxSpace = layout.height - glassDetail.y - glassDetail.height;
|
| | | obstacles.forEach(obstacle => {
|
| | | if (obstacle.y > glassDetail.y + glassDetail.height &&
|
| | | obstacle.x <= glassDetail.x + glassDetail.width &&
|
| | |
| | | event.preventDefault();
|
| | | if (!moveInterval) {
|
| | | moveInterval = setInterval(() => {
|
| | | moveRect(layoutIndex, rectIndex, 'up');
|
| | | moveRect(layoutIndex, rectIndex, 'down');
|
| | | }, 50);
|
| | | }
|
| | | break;
|
| | |
| | | event.preventDefault();
|
| | | if (!moveInterval) {
|
| | | moveInterval = setInterval(() => {
|
| | | moveRect(layoutIndex, rectIndex, 'down');
|
| | | moveRect(layoutIndex, rectIndex, 'up');
|
| | | }, 50);
|
| | | }
|
| | | break;
|
| | |
| | | // 更新矩形位置
|
| | | glassDetail.x = newX;
|
| | | glassDetail.y = newY;
|
| | |
|
| | | // 更新glassPoint坐标
|
| | | if (glassDetail.glassPoint && Array.isArray(glassDetail.glassPoint)) {
|
| | | glassDetail.glassPoint.forEach(point => {
|
| | | point.X = width - point.X;
|
| | | point.X = parseFloat(point.X.toFixed(2));
|
| | | point.Y = parseFloat(point.Y.toFixed(2));
|
| | | });
|
| | | }
|
| | | });
|
| | |
|
| | | // 更新布局
|
| | |
| | |
|
| | | // 更新矩形位置
|
| | | glassDetail.y = newY;
|
| | |
|
| | | // 更新glassPoint坐标
|
| | | if (glassDetail.glassPoint && Array.isArray(glassDetail.glassPoint)) {
|
| | | glassDetail.glassPoint.forEach(point => {
|
| | | point.Y = height - point.Y;
|
| | | // 添加精度控制
|
| | | point.X = parseFloat(point.X.toFixed(2));
|
| | | point.Y = parseFloat(point.Y.toFixed(2));
|
| | | });
|
| | | }
|
| | | });
|
| | |
|
| | | // 更新布局
|