【问题标题】:Horizontal polygon sides subdivision in JavaScriptJavaScript 中的水平多边形边细分
【发布时间】:2022-12-10 05:48:34
【问题描述】:

假设有一些具有调整公共边的任意多边形。

任务是通过使用简单的算法方法simpleInterpolation(curve_, n_) 细分水平边,其中点 X 彼此不多,结果将是:

附加的 sn-p 需要在 polygons.forEach(...) 处更新。

polygons.forEach((polygon_, i_) => {

    let segments = [];

    for(let i = 0; i < polygon_.length; i++){

        let p1 = polygon_[i];
        let p2 = polygon_[(i + 1) % polygon_.length];
        let p3 = polygon_[(i + 2) % polygon_.length];

        if(p1.x !== p2.x && p3.x !== p2.x){


            if(segments.length === 0) {

                segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3]});

            }
            else{

                let lastSegment = segments[segments.length - 1];

                if((i + 1) % polygon_.length !== lastSegment.indices[lastSegment.indices.length - 1]){

                    segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3] });

                }
                else { 
                    
                    lastSegment.indices.push((i + 2) % polygon_.length);
                    lastSegment.points.push(p3);
                
                }

            }

        }

    }

    segments.forEach((segment_) => { segment_.points = simpleInterpolation(segment_.points, 4); })

    console.log(segments);

})

已经有部分找到要更新的段,我不知道如何用simpleInterpolation(curve_, n_)生成的新点替换旧点。这些段的开始索引可能大于结束,因为代码检查最后一个点和两个第一个点,所以它使更新变得棘手并且段可能是随机的,所以起始索引不能适合简单的增量。

const data = {

    up: [

        { x: 200, y: 140 },
        { x: 300, y: 100 },
        { x: 500, y: 120 },
        { x: 600, y: 140 },
        { x: 600, y: 352.2 },
        { x: 400, y: 532.2 },
        { x: 200, y: 352.2 }

    ],

    down: [

        { x: 200, y: 352.2 },
        { x: 400, y: 532.2 },
        { x: 600, y: 352.2 },
        { x: 600, y: 660 },
        { x: 200, y: 660 }

    ],

    debug0: [

        { x: 200, y: 140 },
        { x: 400, y: 100 },
        { x: 600, y: 140 },

    ],

    debug1: [

        { x: 200, y: 352.2 },
        { x: 400, y: 532.2 },
        { x: 600, y: 352.2 }

    ],

    debugUp: [

        { x: 200, y: 140},
        { x: 212.5, y: 137.5},
        { x: 275, y: 130},
        { x: 400, y: 125},
        { x: 525, y: 130},
        { x: 587.5, y: 137.5},
        { x: 600, y: 140},
        { x: 600, y: 352.2},
        { x: 587.5, y: 363.45},
        { x: 525, y: 397.2},
        { x: 400, y: 419.70},
        { x: 275, y: 397.2},
        { x: 212.5, y: 363.45},
        { x: 200, y: 352.2}

    ],

    debugDown: [

        { x: 200, y: 352.2},
        { x: 212.5, y: 363.45},
        { x: 275, y: 397.2},
        { x: 400, y: 419.70},
        { x: 525, y: 397.2},
        { x: 587.5, y: 363.45},
        { x: 600, y: 352.2},
        { x: 600, y: 660 },
        { x: 200, y: 660 }

    ]

};

let svg, curve0, curve1, EPS = 1E-5;

svg = d3.select("#scene");

let polygons = [data.up, data.down];

polygons.forEach((polygon_, i_) => {

    let segments = [];

    for(let i = 0; i < polygon_.length; i++){

        let p1 = polygon_[i];
        let p2 = polygon_[(i + 1) % polygon_.length];
        let p3 = polygon_[(i + 2) % polygon_.length];

        if(p1.x !== p2.x && p3.x !== p2.x){


            if(segments.length === 0) {

                segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3]});

            }
            else{

                let lastSegment = segments[segments.length - 1];

                if((i + 1) % polygon_.length !== lastSegment.indices[lastSegment.indices.length - 1]){

                    segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3] });

                }
                else { 
                    
                    lastSegment.indices.push((i + 2) % polygon_.length);
                    lastSegment.points.push(p3);
                
                }

            }

        }

    }

    segments.forEach((segment_) => { segment_.points = simpleInterpolation(segment_.points, 4); })

    console.log(segments);

})

let left = svg.append("g");

left.append("path")
.attr("d", generatePathFromPoints(data.up, true))
.attr("stroke", "#FF00FF")
.attr("fill", "#808080");

left.append("path")
.attr("d", generatePathFromPoints(data.down, true))
.attr("stroke", "#00FFFF")
.attr("fill", "#404040");

debugCurve0 = simpleInterpolation(data.debug0, 4);

left. append("path")
.attr("d", generatePathFromPoints(debugCurve0, false))
.attr("stroke", "#FF0000")
.attr("fill", "none");

let dots0 = left.selectAll(".debug0")
.data(debugCurve0)
.enter()
.append("circle")
.attr("class", "debug0")
.attr("cx", d_ => d_.x)
.attr("cy", d_ => d_.y)
.attr("r", 4)
.attr("fill", "#FF0000");

debugCurve1 = simpleInterpolation(data.debug1, 4);

left.append("path")
.attr("d", generatePathFromPoints(debugCurve1, false))
.attr("stroke", "#FF0000")
.attr("fill", "none");

let dots1 = left.selectAll(".debug1")
.data(debugCurve1)
.enter()
.append("circle")
.attr("class", "debug0")
.attr("cx", d_ => d_.x)
.attr("cy", d_ => d_.y)
.attr("r", 4)
.attr("fill", "#FF0000");

let right = svg.append("g").attr("transform", "translate(800, 0)");

right.append("path")
.attr("d", generatePathFromPoints(data.debugUp, true))
.attr("stroke", "#FF00FF")
.attr("fill", "#808080");

right.append("path")
.attr("d", generatePathFromPoints(data.debugDown, true))
.attr("stroke", "#00FFFF")
.attr("fill", "#404040");


function simpleInterpolation(curve_, n_){

    let out, input = [...curve_];

    for(let i = 0; i < n_; i++){

        out = [input[0]];

        for(let j = 0; j < input.length - 1; j++){

            let p0 = input[j];
            let p1 = input[j + 1];

            let m01 = median(p0, p1, 0.5);

            out.push(m01);

        }

        out.push(input[input.length - 1]);
        input = out;

    }

    return out;

}

function median(p0_, p1_, t_){ return {x: p0_.x + (p1_.x - p0_.x) * t_, y: p0_.y + (p1_.y - p0_.y) * t_}; }

function generatePathFromPoints(points_, closed_){

    let d = `M${points_[0].x} ${points_[0].y}`;

    for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; }

    if(closed_) { d += "Z"; }

    return d;

}
body: { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="scene" viewBox="0 0 1600 800" preserveAspectRatio="xMinYMin meet"></svg>

【问题讨论】:

    标签: javascript interpolation polygon division


    【解决方案1】:

    一个非常粗略的解决方案。正确的形状仅供参考。

    const data = {
    
        up: [
    
            { x: 200, y: 140 },
            { x: 400, y: 100},
            { x: 600, y: 140 },
            { x: 600, y: 352.2 },
            { x: 400, y: 532.2 },
            { x: 200, y: 352.2 }
    
        ],
    
        down: [
    
            { x: 200, y: 352.2 },
            { x: 400, y: 532.2 },
            { x: 600, y: 352.2 },
            { x: 600, y: 660 },
            { x: 200, y: 660 }
    
        ],
    
        debug0: [
    
            { x: 200, y: 140 },
            { x: 400, y: 100 },
            { x: 600, y: 140 },
    
        ],
    
        debug1: [
    
            { x: 200, y: 352.2 },
            { x: 400, y: 532.2 },
            { x: 600, y: 352.2 }
    
        ],
    
        debugUp: [
    
            { x: 200, y: 140},
            { x: 212.5, y: 137.5},
            { x: 275, y: 130},
            { x: 400, y: 125},
            { x: 525, y: 130},
            { x: 587.5, y: 137.5},
            { x: 600, y: 140},
            { x: 600, y: 352.2},
            { x: 587.5, y: 363.45},
            { x: 525, y: 397.2},
            { x: 400, y: 419.70},
            { x: 275, y: 397.2},
            { x: 212.5, y: 363.45},
            { x: 200, y: 352.2}
    
        ],
    
        debugDown: [
    
            { x: 200, y: 352.2},
            { x: 212.5, y: 363.45},
            { x: 275, y: 397.2},
            { x: 400, y: 419.70},
            { x: 525, y: 397.2},
            { x: 587.5, y: 363.45},
            { x: 600, y: 352.2},
            { x: 600, y: 660 },
            { x: 200, y: 660 }
    
        ]
    
    };
    
    let svg, curve0, curve1, EPS = 1E-5;
    
    svg = d3.select("#scene");
    
    data.up = subdividePolygon(data.up, 4);
    data.down = subdividePolygon(data.down, 4);
    
    let left = svg.append("g");
    
    left.append("path")
    .attr("d", generatePathFromPoints(data.up, true))
    .attr("stroke", "#FF00FF")
    .attr("fill", "#808080");
    
    left.append("path")
    .attr("d", generatePathFromPoints(data.down, true))
    .attr("stroke", "#00FFFF")
    .attr("fill", "#404040");
    
    debugCurve0 = simpleInterpolation(data.debug0, 4);
    
    left. append("path")
    .attr("d", generatePathFromPoints(debugCurve0, false))
    .attr("stroke", "#FF0000")
    .attr("fill", "none");
    
    let dots0 = left.selectAll(".debug0")
    .data(debugCurve0)
    .enter()
    .append("circle")
    .attr("class", "debug0")
    .attr("cx", d_ => d_.x)
    .attr("cy", d_ => d_.y)
    .attr("r", 4)
    .attr("fill", "#FF0000");
    
    debugCurve1 = simpleInterpolation(data.debug1, 4);
    
    left.append("path")
    .attr("d", generatePathFromPoints(debugCurve1, false))
    .attr("stroke", "#FF0000")
    .attr("fill", "none");
    
    let dots1 = left.selectAll(".debug1")
    .data(debugCurve1)
    .enter()
    .append("circle")
    .attr("class", "debug0")
    .attr("cx", d_ => d_.x)
    .attr("cy", d_ => d_.y)
    .attr("r", 4)
    .attr("fill", "#FF0000");
    
    let right = svg.append("g").attr("transform", "translate(800, 0)");
    
    right.append("path")
    .attr("d", generatePathFromPoints(data.debugUp, true))
    .attr("stroke", "#FF00FF")
    .attr("fill", "#808080");
    
    right.append("path")
    .attr("d", generatePathFromPoints(data.debugDown, true))
    .attr("stroke", "#00FFFF")
    .attr("fill", "#404040");
    
    function subdividePolygon(polygon_, n_) {
    
        let segments = [];
    
        for(let i = 0; i < polygon_.length; i++){
    
        let p1 = polygon_[i];
        let p2 = polygon_[(i + 1) % polygon_.length];
        let p3 = polygon_[(i + 2) % polygon_.length];
    
        if(p1.x !== p2.x && p3.x !== p2.x){
    
    
            if(segments.length === 0) {
    
                segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3]});
    
            }
            else{
    
                let lastSegment = segments[segments.length - 1];
    
                if((i + 1) % polygon_.length !== lastSegment.indices[lastSegment.indices.length - 1]){
    
                    segments.push({ indices: [i, (i + 1) % polygon_.length, (i + 2) % polygon_.length], points: [p1, p2, p3] });
    
                }
                else { 
                    
                    lastSegment.indices.push((i + 2) % polygon_.length);
                    lastSegment.points.push(p3);
                
                }
    
            }
    
        }
    
        }
    
        segments.forEach((segment_) => { segment_.points = simpleInterpolation(segment_.points, n_); })
    
        let newpoints = Array.from({length: polygon_.length}, (d_, i_)=> i_);
    
        segments.forEach((segment_) => {
    
        newpoints.splice(newpoints.indexOf(segment_.indices[0]), segment_.indices.length, ...segment_.points);
    
        })
    
        for(let i = 0; i < newpoints.length; i++){
    
        if(typeof newpoints[i] == 'number') { newpoints[i] = polygon_[newpoints[i]]; }
    
        }
    
        return newpoints;
    
    }
    
    function simpleInterpolation(curve_, n_){
    
        let out, input = [...curve_];
    
        for(let i = 0; i < n_; i++){
    
            out = [input[0]];
    
            for(let j = 0; j < input.length - 1; j++){
    
                let p0 = input[j];
                let p1 = input[j + 1];
    
                let m01 = median(p0, p1, 0.5);
    
                out.push(m01);
    
            }
    
            out.push(input[input.length - 1]);
            input = out;
    
        }
    
        return out;
    
    }
    
    function median(p0_, p1_, t_){ return {x: p0_.x + (p1_.x - p0_.x) * t_, y: p0_.y + (p1_.y - p0_.y) * t_}; }
    
    function generatePathFromPoints(points_, closed_){
    
        let d = `M${points_[0].x} ${points_[0].y}`;
    
        for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; }
    
        if(closed_) { d += "Z"; }
    
        return d;
    
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg id="scene" viewBox="0 0 1600 800" preserveAspectRatio="xMinYMin meet"></svg>

    【讨论】:

      【解决方案2】:

      我不确定我是否完全理解你的问题,但如果你想插入不包含垂直段的所有段序列,那么解决方案似乎更简单:

          const NMultiplyByInterpolation = 5;
          // multiply the number of points in the interpolated curve by a factor of NMultiplyByInterpolation
          // e.g., for 3 points generate 3*NMultiplyByInterpolation
          // the larger NMultiplyByInterpolation, the smoother the interpolation
      
          const data = {
              up: [
                  {x: 200, y: 140},
                  {x: 300, y: 100},
                  {x: 500, y: 120},
                  {x: 600, y: 140},
                  {x: 600, y: 352.2},
                  {x: 400, y: 532.2},
                  {x: 200, y: 352.2}
              ],
      
              down: [
                  {x: 200, y: 352.2},
                  {x: 400, y: 532.2},
                  {x: 600, y: 352.2},
                  {x: 600, y: 660},
                  {x: 200, y: 660}
              ]
          };
      
          const polygonNames = Object.keys(data); // ["up", "down"]
          const interpolatedData = Object.fromEntries(polygonNames.map(key=>[key, []])); // {up: [], down: []}
      
          polygonNames.forEach((key_) => {
              const polygon_ = data[key_]; // original polygon
              const newPolygon = interpolatedData[key_]; // new polygon we build
      
              let p2 = polygon_[0];
              let segments = [p2];
              for(let i = 0; i < polygon_.length; i++){
                  let p1 = p2;
                  p2 = polygon_[(i + 1) % polygon_.length];
                  if(p1.x !== p2.x){
                      segments.push(p2);
                  }
                  else{
                      if(segments.length > 1){
                          newPolygon.push(...simpleInterpolation(segments, segments.length*NMultiplyByInterpolation));
                      }
                      else if(segments.length === 1){
                          newPolygon.push(segments[0]);
                      }
                      if(newPolygon.length === 0){
                          newPolygon.push(p1);
                      }
                      newPolygon.push(p1,p2);
                      segments = [p2];
                  }
              }
              if(segments.length > 1){
                  newPolygon.push(...simpleInterpolation(segments_.points, segments_.points.length*NMultiplyByInterpolation));
              }
              else if(segments.length === 1){
                  newPolygon.push(segments[0]);
              }
          });
      
      
          let svg = d3.select("#scene");
      
          let left = svg.append("g");
      
          left.append("path")
              .attr("d", generatePathFromPoints(data.up, true))
              .attr("stroke", "#FF00FF")
              .attr("fill", "#808080");
      
          left.append("path")
              .attr("d", generatePathFromPoints(data.down, true))
              .attr("stroke", "#00FFFF")
              .attr("fill", "#404040");
      
      
          let right = svg.append("g").attr("transform", "translate(800, 0)");
      
          right.append("path")
              .attr("d", generatePathFromPoints(interpolatedData.up, true))
              .attr("stroke", "#FF00FF")
              .attr("fill", "#808080");
      
          right.append("path")
              .attr("d", generatePathFromPoints(interpolatedData.down, true))
              .attr("stroke", "#00FFFF")
              .attr("fill", "#404040");
      
      
          function simpleInterpolation(curve_, n_){
              let out, input = [...curve_];
              for(let i = 0; i < n_; i++){
                  out = [input[0]];
                  for(let j = 0; j < input.length - 1; j++){
                      let p0 = input[j];
                      let p1 = input[j + 1];
                      let m01 = median(p0, p1, 0.5);
                      out.push(m01);
                  }
                  out.push(input[input.length - 1]);
                  input = out;
              }
              return out;
          }
      
          function median(p0_, p1_, t_){ return {x: p0_.x + (p1_.x - p0_.x) * t_, y: p0_.y + (p1_.y - p0_.y) * t_}; }
      
          function generatePathFromPoints(points_, closed_){
              let d = `M${points_[0].x} ${points_[0].y}`;
              for(let i = 1; i < points_.length; i++){
                  d += `L${points_[i].x} ${points_[i].y}`;
              }
              if(closed_){
                  d += "Z";
              }
              return d;
          }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
      <svg id="scene" viewBox="0 0 1600 800" preserveAspectRatio="xMinYMin meet"></svg>

      【讨论】:

        猜你喜欢
        • 2012-01-19
        • 2017-10-01
        • 2011-06-29
        • 1970-01-01
        • 2019-06-04
        • 1970-01-01
        • 1970-01-01
        • 2018-12-31
        • 1970-01-01
        相关资源
        最近更新 更多