js使用run编码计算region的交集并集差集

所有shape都转为run编码

转为run编码后再运算可以节约大量内存

subtractIntervals 函数的逻辑:目前的实现假设了所有的 subIntervals 都会与 intervals 完全重叠,这可能导致计算不准确。应该将 subIntervals 从 intervals 中去除时,考虑到可能的部分重叠。

差集计算:subtractIntervals 函数需要更准确地处理部分重叠和完整重叠的情况。

效果图

result

关键代码

/**
         * 绘制矩形
         * @param {CanvasRenderingContext2D} ctx - Canvas 2D 渲染上下文
         * @param {number[]} rect - 矩形 [x, y, width, height]
         * @param {string} color - 颜色
         */
        function drawRect(ctx, rect, color) {
            ctx.fillStyle = color;
            ctx.fillRect(rect[0], rect[1], rect[2], rect[3]);
        }

        /**
         * 绘制 RLE 区域
         * @param {CanvasRenderingContext2D} ctx - Canvas 2D 渲染上下文
         * @param {Object[]} rle - RLE 区域数组
         * @param {string} color - 颜色
         */
        function drawRLE(ctx, rle, color) {
            ctx.fillStyle = color;
            rle.forEach(({ row, intervals }) => {
                intervals.forEach(({ startCol, runLength }) => {
                    ctx.fillRect(startCol, row, runLength, 1);
                });
            });
        }

        /**
         * 将矩形转换为 RLE 编码
         * @param {number[]} rect - 矩形 [x, y, width, height]
         * @returns {Object[]} - RLE 区域数组
         */
        function rectangleToRLE(rect) {
            const [x, y, w, h] = rect;
            const rle = [];
            
            for (let i = y; i < y + h; i++) {
                rle.push({
                    row: i,
                    intervals: [{ startCol: x, runLength: w }]
                });
            }
            
            return rle;
        }

        /**
         * 计算两个 RLE 区域的交集
         * @param {Object[]} rle1 - 第一个 RLE 区域数组
         * @param {Object[]} rle2 - 第二个 RLE 区域数组
         * @returns {Object[]} - 交集 RLE 区域数组
         */
        function intersectRLE(rle1, rle2) {
            const intersected = [];
            
            const rleMap = (rle) => {
                const map = new Map();
                rle.forEach(({ row, intervals }) => {
                    if (!map.has(row)) {
                        map.set(row, []);
                    }
                    map.get(row).push(...intervals);
                });
                return map;
            };

            const rleMap1 = rleMap(rle1);
            const rleMap2 = rleMap(rle2);

            const allRows = new Set([...rleMap1.keys(), ...rleMap2.keys()]);
            
            allRows.forEach(row => {
                const intervals1 = rleMap1.get(row) || [];
                const intervals2 = rleMap2.get(row) || [];
                const rowIntersected = intersectIntervals(intervals1, intervals2);
                if (rowIntersected.length > 0) {
                    intersected.push({ row, intervals: rowIntersected });
                }
            });
            
            return intersected;
        }

        /**
         * 计算两个区间数组的交集
         * @param {Object[]} intervals1 - 第一个区间数组
         * @param {Object[]} intervals2 - 第二个区间数组
         * @returns {Object[]} - 交集区间数组
         */
        function intersectIntervals(intervals1, intervals2) {
            const result = [];
            intervals1.forEach(({ startCol: start1, runLength: length1 }) => {
                intervals2.forEach(({ startCol: start2, runLength: length2 }) => {
                    const start = Math.max(start1, start2);
                    const end = Math.min(start1 + length1, start2 + length2);
                    if (start < end) {
                        result.push({ startCol: start, runLength: end - start });
                    }
                });
            });
            return result;
        }

        /**
         * 计算两个 RLE 区域的并集
         * @param {Object[]} rle1 - 第一个 RLE 区域数组
         * @param {Object[]} rle2 - 第二个 RLE 区域数组
         * @returns {Object[]} - 并集 RLE 区域数组
         */
        function unionRLE(rle1, rle2) {
            const union = [];
            
            const rleMap = (rle) => {
                const map = new Map();
                rle.forEach(({ row, intervals }) => {
                    if (!map.has(row)) {
                        map.set(row, []);
                    }
                    map.get(row).push(...intervals);
                });
                return map;
            };

            const rleMap1 = rleMap(rle1);
            const rleMap2 = rleMap(rle2);

            const allRows = new Set([...rleMap1.keys(), ...rleMap2.keys()]);
            
            allRows.forEach(row => {
                const intervals1 = rleMap1.get(row) || [];
                const intervals2 = rleMap2.get(row) || [];
                const rowUnion = unionIntervals(intervals1, intervals2);
                if (rowUnion.length > 0) {
                    union.push({ row, intervals: rowUnion });
                }
            });
            
            return union;
        }

        /**
         * 计算两个区间数组的并集
         * @param {Object[]} intervals1 - 第一个区间数组
         * @param {Object[]} intervals2 - 第二个区间数组
         * @returns {Object[]} - 并集区间数组
         */
        function unionIntervals(intervals1, intervals2) {
            const result = [];
            const allIntervals = [...intervals1, ...intervals2];
            allIntervals.sort((a, b) => a.startCol - b.startCol);

            let currentStart = -Infinity;
            let currentEnd = -Infinity;
            
            allIntervals.forEach(({ startCol, runLength }) => {
                const end = startCol + runLength;
                if (startCol > currentEnd) {
                    if (currentEnd > currentStart) {
                        result.push({ startCol: currentStart, runLength: currentEnd - currentStart });
                    }
                    currentStart = startCol;
                    currentEnd = end;
                } else {
                    currentEnd = Math.max(currentEnd, end);
                }
            });
            
            if (currentEnd > currentStart) {
                result.push({ startCol: currentStart, runLength: currentEnd - currentStart });
            }
            
            return result;
        }

        /**
         * 计算一个 RLE 区域相对于另一个区域的差集
         * @param {Object[]} rle1 - 第一个 RLE 区域数组
         * @param {Object[]} rle2 - 第二个 RLE 区域数组
         * @returns {Object[]} - 差集 RLE 区域数组
         */
        function subtractRLE(rle1, rle2) {
            const difference = [];
            
            const rleMap = (rle) => {
                const map = new Map();
                rle.forEach(({ row, intervals }) => {
                    if (!map.has(row)) {
                        map.set(row, []);
                    }
                    map.get(row).push(...intervals);
                });
                return map;
            };

            const rleMap1 = rleMap(rle1);
            const rleMap2 = rleMap(rle2);

            const allRows = new Set([...rleMap1.keys(), ...rleMap2.keys()]);
            
            allRows.forEach(row => {
                const intervals1 = rleMap1.get(row) || [];
                const intervals2 = rleMap2.get(row) || [];
                const rowDifference = subtractIntervals(intervals1, intervals2);
                if (rowDifference.length > 0) {
                    difference.push({ row, intervals: rowDifference });
                }
            });
            
            return difference;
        }

        /**
         * 计算两个区间数组的差集
         * @param {Object[]} intervals - 原始区间数组
         * @param {Object[]} subIntervals - 子区间数组
         * @returns {Object[]} - 差集区间数组
         */
        function subtractIntervals(intervals, subIntervals) {
            const result = [];
            
            intervals.forEach(({ startCol, runLength }) => {
                let currentStart = startCol;
                let currentLength = runLength;

                subIntervals.forEach(({ startCol: subStart, runLength: subLength }) => {
                    if (subStart >= currentStart + currentLength || subStart + subLength <= currentStart) {
                        return;
                    }

                    const overlapStart = Math.max(currentStart, subStart);
                    const overlapEnd = Math.min(currentStart + currentLength, subStart + subLength);
                    
                    if (overlapStart > currentStart) {
                        result.push({ startCol: currentStart, runLength: overlapStart - currentStart });
                    }
                    if (overlapEnd < currentStart + currentLength) {
                        result.push({ startCol: overlapEnd, runLength: (currentStart + currentLength) - overlapEnd });
                    }
                    let currentStartBack=currentStart;
                    currentStart = Math.max(currentStart, overlapEnd);
                    currentLength = (currentStartBack + currentLength) - currentStart;
                });

                if (currentLength > 0) {
                    result.push({ startCol: currentStart, runLength: currentLength });
                }
            });

            return result;
        }

        /**
         * 绘制 RLE 运算结果
         * @param {CanvasRenderingContext2D} ctxIntersect - 交集 Canvas 渲染上下文
         * @param {CanvasRenderingContext2D} ctxUnion - 并集 Canvas 渲染上下文
         * @param {CanvasRenderingContext2D} ctxDifference - 差集 Canvas 渲染上下文
         * @param {number[]} rect1 - 第一个矩形 [x, y, width, height]
         * @param {number[]} rect2 - 第二个矩形 [x, y, width, height]
         */
        function drawResults(ctxIntersect, ctxUnion, ctxDifference, rect1, rect2) {
            const rle1 = rectangleToRLE(rect1);
            const rle2 = rectangleToRLE(rect2);

            const intersectedRLE = intersectRLE(rle1, rle2);
            const unionRLE1 = unionRLE(rle1, rle2);
            const differenceRLE = subtractRLE(rle1, rle2);

            // 清空 Canvas
            ctxIntersect.clearRect(0, 0, ctxIntersect.canvas.width, ctxIntersect.canvas.height);
            ctxUnion.clearRect(0, 0, ctxUnion.canvas.width, ctxUnion.canvas.height);
            ctxDifference.clearRect(0, 0, ctxDifference.canvas.width, ctxDifference.canvas.height);

            // 绘制交集结果
            drawRLE(ctxIntersect, intersectedRLE, 'rgba(255, 255, 0, 0.5)'); // Yellow for intersection

            // 绘制并集结果
            drawRLE(ctxUnion, unionRLE1, 'rgba(0, 255, 0, 0.5)'); // Green for union

            // 绘制差集结果
            drawRLE(ctxDifference, differenceRLE, 'rgba(255, 0, 0, 0.5)'); // Red for difference
        }

        const canvasIntersect = document.getElementById('canvasIntersect');
        const canvasUnion = document.getElementById('canvasUnion');
        const canvasDifference = document.getElementById('canvasDifference');

        const ctxIntersect = canvasIntersect.getContext('2d');
        const ctxUnion = canvasUnion.getContext('2d');
        const ctxDifference = canvasDifference.getContext('2d');

        const rect1 = [0, 0, 200, 200]; // x, y, width, height
        const rect2 = [50, 50, 150, 150]; // x, y, width, height

        drawResults(ctxIntersect, ctxUnion, ctxDifference, rect1, rect2);
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值