| | |
| | |
|
| | | 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);
|
| | |
|
| | | // 按坐标排序,优先按x坐标,其次按y坐标
|
| | | grayRects.sort((a, b) => {
|
| | | if (a.x !== b.x) return a.x - b.x;
|
| | | return a.y - b.y;
|
| | |
| | |
|
| | | 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),
|
| | |
| | | height: Math.round(current.height),
|
| | | isRemain: true
|
| | | });
|
| | | current = { ...next };
|
| | | }
|
| | | }
|
| | |
|
| | | const nonGray = glassDetails.filter(r => !r.isRemain);
|
| | | //删除原数组拼接新的小片跟余料
|
| | | glassDetails.splice(0, glassDetails.length, ...nonGray, ...merged);
|
| | | // 重新构建数组:非余料 + 合并后的余料
|
| | | 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));
|
| | | //余料的数据
|
| | | const currentGrayRects = glassDetails.filter(r => r.isRemain);
|
| | | //循环余料数据跟全部的对比
|
| | | currentGrayRects.forEach((_, index) => {
|
| | | if (index >= remainingAreas.length) {
|
| | | glassDetails.splice(index, 1);
|
| | | }
|
| | | });
|
| | |
|
| | | 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({
|
| | | // 3. 去重处理 - 更严格的去重逻辑
|
| | | const uniqueArr = removeDuplicateAreas(remainingAreas);
|
| | |
|
| | | // 4. 获取当前已存在的余料矩形(需要保留引用以便更新)
|
| | | const currentGrayRects = glassDetails.filter(r => r.isRemain);
|
| | |
|
| | | // 5. 清除所有现有的余料矩形
|
| | | // 先收集非余料矩形
|
| | | const nonRemainRects = glassDetails.filter(r => !r.isRemain);
|
| | |
|
| | | // 6. 重新构建玻璃详情数组
|
| | | // 保留非余料矩形
|
| | | const newGlassDetails = [...nonRemainRects];
|
| | |
|
| | | // 添加新的余料矩形
|
| | | uniqueArr.forEach((area) => {
|
| | | newGlassDetails.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;
|
| | |
|
| | | // 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);
|
| | | }
|
| | | });
|
| | |
|
| | | mergeAdjacentGrayRects(glassDetails,layout.width, layout.height);
|
| | | return result;
|
| | | };
|
| | |
|
| | |
|
| | |
|
| | | //旋转方法
|
| | | const rotateRect = (layoutIndex, rectIndex) => {
|
| | |
| | | };
|
| | |
|
| | | //重新计算余料坐标以及尺寸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 newRemaining = [];
|
| | |
|
| | | remainingAreas.forEach(area => {
|
| | | // 如果障碍物与当前区域有重叠
|
| | | if (checkOverlap(area, obstacle)) {
|
| | | // 切分当前区域为最多4个新区域
|
| | |
|
| | | // 左侧区域
|
| | | if (obstacle.x > area.x) {
|
| | | newRemaining.push({
|
| | | 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),
|
| | | width: area.x + area.width - (obstacle.x + obstacle.width),
|
| | | height: area.height
|
| | | });
|
| | | }
|
| | |
|
| | | // 上方区域(仅在障碍物左右边界之间的区域)
|
| | | if (obstacle.y > area.y) {
|
| | | const startX = Math.max(area.x, obstacle.x);
|
| | | const endX = Math.min(area.x + area.width, obstacle.x + obstacle.width);
|
| | | if (endX > startX) {
|
| | | newRemaining.push({
|
| | | x: area.x,
|
| | | x: startX,
|
| | | y: area.y,
|
| | | width: area.width,
|
| | | width: endX - startX,
|
| | | 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)
|
| | | });
|
| | | }
|
| | |
|
| | | // 下方区域(仅在障碍物左右边界之间的区域)
|
| | | if (obstacle.y + obstacle.height < area.y + area.height) {
|
| | | const startX = Math.max(area.x, obstacle.x);
|
| | | const endX = Math.min(area.x + area.width, obstacle.x + obstacle.width);
|
| | | if (endX > startX) {
|
| | | newRemaining.push({
|
| | | x: startX,
|
| | | y: obstacle.y + obstacle.height,
|
| | | width: endX - startX,
|
| | | height: area.y + area.height - (obstacle.y + obstacle.height)
|
| | | });
|
| | | }
|
| | | }
|
| | | } else {
|
| | | // 没有重叠,保留原区域
|
| | | newRemaining.push(area);
|
| | | }
|
| | | });
|
| | |
|
| | | return newRemaining;
|
| | | };
|
| | |
|