计算 svg 总表面积的问题。排除空格

问题描述 投票:0回答:1

我正在做一个项目。 ppl 可以在那里上传 svg 并将一些材料和其他东西应用到 svg。然后我想提供用这种材料制作它需要多少钱。对于价格公式,我需要获取 svg 中所有路径的总面积。

我知道我可以使用 Coral Draw 和 e-cut 插件来做到这一点。但我需要让它与 javascript 或 php 一起工作。

我尝试了很多库,但它们没有这样的功能。

我还有一个想法将路径转换为多边形点。然后像这样计算多边形的面积:

function area(polygon) {
        let total = 0;
        for (let i = 0; i < polygon.length; i++) {
            const addX = polygon[i][0];
            const addY = polygon[i === polygon.length - 1 ? 0 : i + 1][1];
            const subX = polygon[i === polygon.length - 1 ? 0 : i + 1][0];
            const subY = polygon[i][1];
            total += (addX * addY * 0.5) - (subX * subY * 0.5);
        }
        return Math.abs(total);
    }

我有结果了。但它与 e-cut 插件完全不同。我认为这是 100% 正确的。

也许有人有其他想法如何计算总面积? 谢谢你

javascript svg area
1个回答
1
投票

您可以结合本文中的不同方法
“如何计算贝塞尔曲线的面积?”

面积计算 JavaScript 助手

我想出了这个辅助函数,它的工作原理如下

  1. 查询 svg 中的所有几何元素
  2. 根据每个元素的类型计算其面积:
    2.1 如果它是像圆形这样的原始形状,我们可以直接应用像
    π * r²
    这样的简单公式
    2.2 如果它是一个
    <path>
    ,我们将形状切成曲线段和剩余的多边形

可以使用此函数计算贝塞尔曲线段的面积

function getBezierArea(coords) {
  // preceding command's final point
  let x0 = coords[0];
  let y0 = coords[1];
  //if is cubic command
  if (coords.length == 8) {
    // 1st bézier control point
    let x1 = coords[2];
    let y1 = coords[3];
    // 2nd bézier control point
    let x2 = coords[4];
    let y2 = coords[5];
    // curve's final point
    let x3 = coords[6];
    let y3 = coords[7];
    let area =
      ((x0 * (-2 * y1 - y2 + 3 * y3) +
        x1 * (2 * y0 - y2 - y3) +
        x2 * (y0 + y1 - 2 * y3) +
        x3 * (-3 * y0 + y1 + 2 * y2)) *
        3) /
      20;
    return area;
  } else {
    return 0;
  }
}

剩余的多边形面积可以使用您已在描述中包含的常见鞋带公式来计算。

function getSvgElsArea(svg) {
  //query only geometry elements
  let els = svg.querySelectorAll('path, circle, ellipse, rect, polygon');
  let area = 0;
  els.forEach(el => {
    area += getshapeArea(el);
  })
  return area;
}

function getshapeArea(el, decimals = 0) {
  let totalArea = 0;
  let polyPoints = [];
  let type = el.nodeName.toLowerCase();
  switch (type) {
    // 1. paths
    case "path":
      let pathData = el.getPathData({
        normalize: true
      });
      //check subpaths
      let subPathsData = splitSubpaths(pathData);
      let isCompoundPath = subPathsData.length > 1 ? true : false;
      let counterShapes = [];

      // check intersections for compund paths
      if (isCompoundPath) {
        let bboxArr = getSubPathBBoxes(subPathsData);
        bboxArr.forEach(function(bb, b) {
          //let path1 = path;
          for (let i = 0; i < bboxArr.length; i++) {
            let bb2 = bboxArr[i];
            if (bb != bb2) {
              let intersects = checkBBoxIntersections(bb, bb2);
              if (intersects) {
                counterShapes.push(i);
              }
            }
          }
        });
      }

      subPathsData.forEach(function(pathData, d) {
        //reset polygon points for each segment
        polyPoints = [];
        let bezierArea = 0;
        let pathArea = 0;
        let multiplier = 1;

        pathData.forEach(function(com, i) {
          let [type, values] = [com.type, com.values];
          if (values.length) {
            let prevC = i > 0 ? pathData[i - 1] : pathData[0];
            let prevCVals = prevC.values;
            let prevCValsL = prevCVals.length;
            let [x0, y0] = [
              prevCVals[prevCValsL - 2],
              prevCVals[prevCValsL - 1]
            ];
            // C commands
            if (values.length == 6) {
              let area = getBezierArea([
                x0,
                y0,
                values[0],
                values[1],
                values[2],
                values[3],
                values[4],
                values[5]
              ]);
              //push points to calculate inner/remaining polygon area
              polyPoints.push([x0, y0], [values[4], values[5]]);
              bezierArea += area;
            }
            // L commands
            else {
              polyPoints.push([x0, y0], [values[0], values[1]]);
            }
          }
        });
        //get area of remaining polygon
        let areaPoly = polygonArea(polyPoints, false);

        //subtract area by negative multiplier
        if (counterShapes.indexOf(d) !== -1) {
          multiplier = -1;
        }
        //values have the same sign - subtract polygon area
        if (
          (areaPoly < 0 && bezierArea < 0) ||
          (areaPoly > 0 && bezierArea > 0)
        ) {
          pathArea = (Math.abs(bezierArea) - Math.abs(areaPoly)) * multiplier;
        } else {
          pathArea = (Math.abs(bezierArea) + Math.abs(areaPoly)) * multiplier;
        }
        totalArea += pathArea;
      });
      break;

      // 2. primitives:
      // 2.1 circle an ellipse primitives
    case "circle":
    case "ellipse":
      totalArea = getEllipseArea(el);
      break;

      // 2.2 polygons
    case "polygon":
    case "polyline":
      totalArea = getPolygonArea(el);
      break;

      // 2.3 rectancle primitives
    case "rect":
      totalArea = getRectArea(el);
      break;
  }
  if (decimals > 0) {
    totalArea = +totalArea.toFixed(decimals);
  }
  return totalArea;
}

function getPathArea(pathData) {
  let totalArea = 0;
  let polyPoints = [];
  pathData.forEach(function(com, i) {
    let [type, values] = [com.type, com.values];
    if (values.length) {
      let prevC = i > 0 ? pathData[i - 1] : pathData[0];
      let prevCVals = prevC.values;
      let prevCValsL = prevCVals.length;
      let [x0, y0] = [prevCVals[prevCValsL - 2], prevCVals[prevCValsL - 1]];
      // C commands
      if (values.length == 6) {
        let area = getBezierArea([
          x0,
          y0,
          values[0],
          values[1],
          values[2],
          values[3],
          values[4],
          values[5]
        ]);
        //push points to calculate inner/remaining polygon area
        polyPoints.push([x0, y0], [values[4], values[5]]);
        totalArea += area;
      }
      // L commands
      else {
        polyPoints.push([x0, y0], [values[0], values[1]]);
      }
    }
  });
  let areaPoly = polygonArea(polyPoints);
  totalArea = Math.abs(areaPoly) + Math.abs(totalArea);
  return totalArea;
}

/**
 * James Godfrey-Kittle/@jamesgk : https://github.com/Pomax/BezierInfo-2/issues/238
 */
function getBezierArea(coords) {
  let x0 = coords[0];
  let y0 = coords[1];
  //if is cubic command
  if (coords.length == 8) {
    let x1 = coords[2];
    let y1 = coords[3];
    let x2 = coords[4];
    let y2 = coords[5];
    let x3 = coords[6];
    let y3 = coords[7];
    let area =
      ((x0 * (-2 * y1 - y2 + 3 * y3) +
          x1 * (2 * y0 - y2 - y3) +
          x2 * (y0 + y1 - 2 * y3) +
          x3 * (-3 * y0 + y1 + 2 * y2)) *
        3) /
      20;
    return area;
  } else {
    return 0;
  }
}

function polygonArea(points, absolute = true) {
  let area = 0;
  for (let i = 0; i < points.length; i++) {
    const addX = points[i][0];
    const addY = points[i === points.length - 1 ? 0 : i + 1][1];
    const subX = points[i === points.length - 1 ? 0 : i + 1][0];
    const subY = points[i][1];
    area += addX * addY * 0.5 - subX * subY * 0.5;
  }
  if (absolute) {
    area = Math.abs(area);
  }
  return area;
}

function getPolygonArea(el) {
  // convert point string to arra of numbers
  let points = el
    .getAttribute("points")
    .split(/,| /)
    .filter(Boolean)
    .map((val) => {
      return parseFloat(val);
    });
  let polyPoints = [];
  for (let i = 0; i < points.length; i += 2) {
    polyPoints.push([points[i], points[i + 1]]);
  }
  let area = polygonArea(polyPoints);
  return area;
}

function getRectArea(el) {
  let width = el.width.baseVal.value;
  let height = el.height.baseVal.value;
  let area = width * height;
  return area;
}

function getEllipseArea(el) {
  // if is circle
  let r = el.getAttribute("r") ? el.r.baseVal.value : '';
  let rx = el.getAttribute("rx");
  let ry = el.getAttribute("ry");
  //if circle – take radius
  rx = rx ? el.rx.baseVal.value : r;
  ry = ry ? el.ry.baseVal.value : r;
  let area = Math.PI * rx * ry;
  return area;
}

//path data helpers
function splitSubpaths(pathData) {
  let pathDataL = pathData.length;
  let subPathArr = [];
  let subPathMindex = [];
  pathData.forEach(function(com, i) {
    let [type, values] = [com["type"], com["values"]];
    if (type == "M") {
      subPathMindex.push(i);
    }
  });
  //split subPaths
  subPathMindex.forEach(function(index, i) {
    let end = subPathMindex[i + 1];
    let thisSeg = pathData.slice(index, end);
    subPathArr.push(thisSeg);
  });
  return subPathArr;
}

function getSubPathBBoxes(subPaths) {
  let ns = "http://www.w3.org/2000/svg";
  let svgTmp = document.createElementNS(ns, "svg");
  svgTmp.setAttribute("style", "position:absolute; width:0; height:0;");
  document.body.appendChild(svgTmp);
  let bboxArr = [];
  subPaths.forEach(function(pathData) {
    let pathTmp = document.createElementNS(ns, "path");
    svgTmp.appendChild(pathTmp);
    pathTmp.setPathData(pathData);
    let bb = pathTmp.getBBox();
    bboxArr.push(bb);
  });
  svgTmp.remove();
  return bboxArr;
}

function checkBBoxIntersections(bb, bb1) {
  let [x, y, width, height, right, bottom] = [
    bb.x,
    bb.y,
    bb.width,
    bb.height,
    bb.x + bb.width,
    bb.y + bb.height
  ];
  let [x1, y1, width1, height1, right1, bottom1] = [
    bb1.x,
    bb1.y,
    bb1.width,
    bb1.height,
    bb1.x + bb1.width,
    bb1.y + bb1.height
  ];
  let intersects = false;
  if (width * height != width1 * height1) {
    if (width * height > width1 * height1) {
      if (x < x1 && right > right1 && y < y1 && bottom > bottom1) {
        intersects = true;
      }
    }
  }
  return intersects;
}
body {
  font-family: 'Open Sans', 'Myriad', 'Segoe UI', sans-serif;
  font-size: 1.5em;
  line-height: 1.3em;
  background-image: url("data:image/svg+xml, %3Csvg viewBox='0 0 10 9' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M0 0 L10 0 L10 9' stroke-width='1' fill='none' stroke='%23eee' /%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 10px;
}

svg {
  width: 20em;
}
<h3>2 Circles</h3>
<svg id="svg_circles" viewBox="0 0 100 100">
  <circle id="circleOuter" cx="50%" cy="50%" r="50%" />
  <circle id="circleInner" cx="50%" cy="50%" r="25%" fill="red" />
</svg>
<p id="result0"></p>
<h3>Compound path</h3>
<svg id="svg" viewBox="0 0 100 100">
<path  fill-rule="evenodd" d="M50 0a50 50 0 0 1 50 50a50 50 0 0 1-50 50a50 50 0 0 1-50-50a50 50 0 0 1 50-50z 
M50 25a25 25 0 0 1 25 25a25 25 0 0 1-25 25a25 25 0 0 1-25-25a25 25 0 0 1 25-25z"/>
</svg>
<p id="result1"></p>

<h3>Multiple primitives</h3>
<svg id="svg_primitives" viewBox="0 0 200 100">
  <circle cx="50" cy="50" r="50" />
  <rect x="100" y="25" width="50" height="50" />
</svg>
<p id="result2"></p>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/path-data-polyfill.min.js"></script>

<script>
  window.addEventListener('DOMContentLoaded', e => {

    let areaOuter = getshapeArea(circleOuter);
    let areaInner = getshapeArea(circleInner);
    result0.textContent = areaOuter + '-' + areaInner + '=' + (areaOuter - areaInner);

    let areaCompound = getSvgElsArea(svg);
    result1.textContent = areaCompound;

    let areaPrim = getSvgElsArea(svg_primitives)
    result2.textContent = areaPrim;
  });
</script>

获取
<path>
坐标

我们需要一个解析器以结构化方式检索贝塞尔曲线控制点以进行计算。
我正在使用 Jarek Foksa 的路径数据 polyfill。 它还负责通过标准化选项 getPathData({normalize:true})

 转换 
相对于绝对命令

准确度

如您所见,比较示例 1 和 2 存在不准确性:
理想情况下,结果(外圈 - 内圈)应该是

精确面积: 7853.981-1963.495 = 5890.486(基于 π * r²)

由于圆弧到三次贝塞尔曲线的转换我们只能得到圆形几何形状的近似值

立方面积: 5892.136

所以偏差约为 0.028%

我有结果了。但它与 e-cut 插件完全不同。我 我认为 100% 正确。

实际上,您应该针对可以通过简单几何公式(如圆/椭圆)计算的简单示例进行测试

检测子路径

如果路径包含多个

M
命令(例如字母 O),我们将计算每个子路径的面积并通过自定义帮助器
getSubPathBBoxes(subPaths)
检查边界框交叉点。
如果子路径与外部形状相交 - 我们减去第二个区域。

限制

  • 此脚本不会尊重剪辑路径或蒙版
  • 转变 – 最重要的是
    scale()
  • 如何处理笔划?目前不包括在内,但某些元素可能会根据笔划宽度严重绘制形状
  • 可能还有更多...
    用于测试:参见 codepen 示例

暴力方法:通过potrace压平svg

我们实际上是将 svg 转换为位图并再次对其进行跟踪/矢量化,并应用之前的计算助手。
这样,我们就得到了一个组合重叠形状的扁平 svg。
由于我们只分析填充/不透明元素,因此可以避免上述有关蒙版和剪辑路径的问题。

显然,由于需要额外的转换,这种方法更加昂贵。

//svg area let areaSVG = getSvgElsArea(svg); //potrace area let scale = 2; (async() => { let areaTraced = await getTracedArea(svg, scale); result.textContent = `area svg: ${areaSVG} | area traced: ${areaTraced}`; })(); async function getTracedArea(el, scale = 1) { let filter = "brightness(0%)"; let dataUrl = await svg2PngDataUrl(el, scale, filter); let tracingOptions = { turnpolicy: "minority", turdsize: 2, optcurve: true, alphamax: 1, opttolerance: 0.75 }; let tracedSVG = await traceFromURL(dataUrl, tracingOptions); let areaTraced = getSvgElsArea(tracedSVG); document.body.append(tracedSVG); return areaTraced / scale / scale; } /** * svg to canvas */ async function svg2PngDataUrl(el, scale = 1, filter = "") { /** * clone svg to add width and height */ const svgEl = el.cloneNode(true); // get dimensions let { width, height } = el.getBBox(); let w = el.viewBox.baseVal.width ? svgEl.viewBox.baseVal.width : el.width.baseVal.value ? el.width.baseVal.value : width; let h = el.viewBox.baseVal.height ? svgEl.viewBox.baseVal.height : el.height.baseVal.value ? el.height.baseVal.value : height; // apply scaling [w, h] = [w * scale, h * scale]; // add width and height for firefox compatibility svgEl.setAttribute("width", w); svgEl.setAttribute("height", h); // create canvas let canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; let svgString = new XMLSerializer().serializeToString(svgEl); let blob = new Blob([svgString], { type: "image/svg+xml" }); let blobURL = URL.createObjectURL(blob); let tmpImg = new Image(); tmpImg.src = blobURL; tmpImg.width = w; tmpImg.height = h; tmpImg.crossOrigin = "anonymous"; await tmpImg.decode(); let ctx = canvas.getContext("2d"); ctx.fillStyle = "white"; ctx.fillRect(0, 0, w, h); // apply filter to enhance contrast if (filter) { ctx.filter = filter; } ctx.drawImage(tmpImg, 0, 0, w, h); let dataUrl = canvas.toDataURL(); return dataUrl; } /** * trace img from data URl */ async function traceFromURL(url, customOptions) { let defaults = { turnpolicy: "minority", turdsize: 2, optcurve: true, alphamax: 1, opttolerance: 0.75 }; let options = { ...defaults, ...customOptions }; // set parameters Potrace.setParameter(options); // load await Potrace.loadImageFromUrl(url); async function processPotraceAsync() { return new Promise((resolve) => { Potrace.process(() => { let svgTraced = new DOMParser() .parseFromString(Potrace.getSVG(1), "image/svg+xml") .querySelector("svg"); // remove attributes svgTraced.setAttribute( "viewBox", `0 0 ${svgTraced.width.baseVal.value} ${svgTraced.height.baseVal.value}` ); svgTraced.removeAttribute("id"); svgTraced.removeAttribute("version"); svgTraced.removeAttribute("width"); svgTraced.removeAttribute("height"); let path = svgTraced.querySelector("path"); path.removeAttribute("stroke"); path.removeAttribute("fill"); path.removeAttribute("fill-rule"); resolve(svgTraced); }); }); } let tracedSvgEl = await processPotraceAsync(); return tracedSvgEl; } function getSvgElsArea(svg) { let els = svg.querySelectorAll("path, circle, ellipse, rect, polygon"); let area = 0; els.forEach((el) => { area += getshapeArea(el); }); return area; } function getshapeArea(el, decimals = 0) { let totalArea = 0; let polyPoints = []; let type = el.nodeName.toLowerCase(); switch (type) { // 1. paths case "path": let pathData = el.getPathData({ normalize: true }); //check subpaths let subPathsData = splitSubpaths(pathData); let isCompoundPath = subPathsData.length > 1 ? true : false; let counterShapes = []; // check intersections for compund paths if (isCompoundPath) { let bboxArr = getSubPathBBoxes(subPathsData); bboxArr.forEach(function(bb, b) { //let path1 = path; for (let i = 0; i < bboxArr.length; i++) { let bb2 = bboxArr[i]; if (bb != bb2) { let intersects = checkBBoxIntersections(bb, bb2); if (intersects) { counterShapes.push(i); } } } }); } subPathsData.forEach(function(pathData, d) { //reset polygon points for each segment polyPoints = []; let bezierArea = 0; let pathArea = 0; let multiplier = 1; pathData.forEach(function(com, i) { let [type, values] = [com.type, com.values]; if (values.length) { let prevC = i > 0 ? pathData[i - 1] : pathData[0]; let prevCVals = prevC.values; let prevCValsL = prevCVals.length; let [x0, y0] = [ prevCVals[prevCValsL - 2], prevCVals[prevCValsL - 1] ]; // C commands if (values.length == 6) { let area = getBezierArea([ x0, y0, values[0], values[1], values[2], values[3], values[4], values[5] ]); //push points to calculate inner/remaining polygon area polyPoints.push([x0, y0], [values[4], values[5]]); bezierArea += area; } // L commands else { polyPoints.push([x0, y0], [values[0], values[1]]); } } }); //get area of remaining polygon let areaPoly = polygonArea(polyPoints, false); //subtract area by negative multiplier if (counterShapes.indexOf(d) !== -1) { multiplier = -1; } //values have the same sign - subtract polygon area if ( (areaPoly < 0 && bezierArea < 0) || (areaPoly > 0 && bezierArea > 0) ) { pathArea = (Math.abs(bezierArea) - Math.abs(areaPoly)) * multiplier; } else { pathArea = (Math.abs(bezierArea) + Math.abs(areaPoly)) * multiplier; } totalArea += pathArea; }); break; // 2.1 circle an ellipse primitives case "circle": case "ellipse": totalArea = getEllipseArea(el); break; // 2.2 polygons case "polygon": case "polyline": totalArea = getPolygonArea(el); break; // 2.3 rectancle primitives case "rect": totalArea = getRectArea(el); break; } if (decimals > 0) { totalArea = +totalArea.toFixed(decimals); } return totalArea; } function getPathArea(pathData) { let totalArea = 0; let polyPoints = []; pathData.forEach(function(com, i) { let [type, values] = [com.type, com.values]; if (values.length) { let prevC = i > 0 ? pathData[i - 1] : pathData[0]; let prevCVals = prevC.values; let prevCValsL = prevCVals.length; let [x0, y0] = [prevCVals[prevCValsL - 2], prevCVals[prevCValsL - 1]]; // C commands if (values.length == 6) { let area = getBezierArea([ x0, y0, values[0], values[1], values[2], values[3], values[4], values[5] ]); //push points to calculate inner/remaining polygon area polyPoints.push([x0, y0], [values[4], values[5]]); totalArea += area; } // L commands else { polyPoints.push([x0, y0], [values[0], values[1]]); } } }); let areaPoly = polygonArea(polyPoints); totalArea = Math.abs(areaPoly) + Math.abs(totalArea); return totalArea; } function getBezierArea(coords) { let x0 = coords[0]; let y0 = coords[1]; //if is cubic command if (coords.length == 8) { let x1 = coords[2]; let y1 = coords[3]; let x2 = coords[4]; let y2 = coords[5]; let x3 = coords[6]; let y3 = coords[7]; let area = ((x0 * (-2 * y1 - y2 + 3 * y3) + x1 * (2 * y0 - y2 - y3) + x2 * (y0 + y1 - 2 * y3) + x3 * (-3 * y0 + y1 + 2 * y2)) * 3) / 20; return area; } else { return 0; } } function polygonArea(points, absolute = true) { let area = 0; for (let i = 0; i < points.length; i++) { const addX = points[i][0]; const addY = points[i === points.length - 1 ? 0 : i + 1][1]; const subX = points[i === points.length - 1 ? 0 : i + 1][0]; const subY = points[i][1]; area += addX * addY * 0.5 - subX * subY * 0.5; } if (absolute) { area = Math.abs(area); } return area; } function getPolygonArea(el) { // convert point string to arra of numbers let points = el .getAttribute("points") .split(/,| /) .filter(Boolean) .map((val) => { return parseFloat(val); }); let polyPoints = []; for (let i = 0; i < points.length; i += 2) { polyPoints.push([points[i], points[i + 1]]); } let area = polygonArea(polyPoints); return area; } function getRectArea(el) { let width = el.width.baseVal.value; let height = el.height.baseVal.value; let area = width * height; return area; } function getEllipseArea(el) { // if is circle let r = el.getAttribute("r") ? el.r.baseVal.value : ""; let rx = el.getAttribute("rx"); let ry = el.getAttribute("ry"); //if circle – take radius rx = rx ? el.rx.baseVal.value : r; ry = ry ? el.ry.baseVal.value : r; let area = Math.PI * rx * ry; return area; } //path data helpers function splitSubpaths(pathData) { let pathDataL = pathData.length; let subPathArr = []; let subPathMindex = []; pathData.forEach(function(com, i) { let [type, values] = [com["type"], com["values"]]; if (type == "M") { subPathMindex.push(i); } }); //split subPaths subPathMindex.forEach(function(index, i) { let end = subPathMindex[i + 1]; let thisSeg = pathData.slice(index, end); subPathArr.push(thisSeg); }); return subPathArr; } function getSubPathBBoxes(subPaths) { let ns = "http://www.w3.org/2000/svg"; let svgTmp = document.createElementNS(ns, "svg"); svgTmp.setAttribute("style", "position:absolute; width:0; height:0;"); document.body.appendChild(svgTmp); let bboxArr = []; subPaths.forEach(function(pathData) { let pathTmp = document.createElementNS(ns, "path"); svgTmp.appendChild(pathTmp); pathTmp.setPathData(pathData); let bb = pathTmp.getBBox(); bboxArr.push(bb); }); svgTmp.remove(); return bboxArr; } function checkBBoxIntersections(bb, bb1) { let [x, y, width, height, right, bottom] = [ bb.x, bb.y, bb.width, bb.height, bb.x + bb.width, bb.y + bb.height ]; let [x1, y1, width1, height1, right1, bottom1] = [ bb1.x, bb1.y, bb1.width, bb1.height, bb1.x + bb1.width, bb1.y + bb1.height ]; let intersects = false; if (width * height != width1 * height1) { if (width * height > width1 * height1) { if (x < x1 && right > right1 && y < y1 && bottom > bottom1) { intersects = true; } } } return intersects; }
svg {
  width: 20em;
  height: auto;
  border: 1px solid #ccc;
  overflow: visible;
}
<svg id="svg" viewBox="0 0 200 100">
<path  fill="yellow" fill-rule="evenodd" d="M50 0a50 50 0 0 1 50 50a50 50 0 0 1-50 50a50 50 0 0 1-50-50a50 50 0 0 1 50-50zm0 25a25 25 0 0 1 25 25a25 25 0 0 1-25 25a25 25 0 0 1-25-25a25 25 0 0 1 25-25z"/>
<path  fill="pink" fill-rule="evenodd" d="M150 0a50 50 0 0 1 50 50a50 50 0 0 1-50 50a50 50 0 0 1-50-50a50 50 0 0 1 50-50zm0 25a25 25 0 0 1 25 25a25 25 0 0 1-25 25a25 25 0 0 1-25-25a25 25 0 0 1 25-25z"/>
</svg>

<p id="result"></p>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/path-data-polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/kilobtye/potrace@master/potrace.js"></script>

您还可以使用 paper.js 来计算表面积,如

lmeurs 所述

© www.soinside.com 2019 - 2024. All rights reserved.