确定(x,y)点的对象是否属于SVG路径

时间:2018-06-18 17:44:35

标签: javascript d3.js svg

我正在研究D3图,我试图将散点图分组到我使用路径变量创建的区域/区域。我正在寻找一些简单,计算的方法来做到这一点。以下是我正在使用的示例:

const shallowRightPath = 'M0 25  L0 55  L28 50  L23 25  L0 25'; 
const shallowLeftPath = 'M85 25  L85 55  L57 50  L62 25  L85 25'; 
const shallowCenterPath = 'M28 50  L23 25  L62 25  L57 50  L28 50';
const slotPath = 'M27 89  L32 71  L53 71  L58 89  L27 89';
const deepSlotPath = 'M32 71  L28 50  L57 50  L53 71  L32 71';   
const attackingRightPath = 'M0 55  L0 72  A28 28 1 0 0 5.7 89  L27 89  L32 71  L28 50  L0 55';
const attackingLeftPath = 'M85 55  L85 72  A28 28 0 0 1 79.3 89  L58 89  L53 71  L57 50  L85 55';


const zoneInfo = [
      {"num": 1, "name": "Shallow Right", "path": shallowRightPath, "color":"blue"},
      {"num": 2, "name": "Shallow Left", "path": shallowLeftPath, "color":"yellow"},
      {"num": 3, "name": "Shallow Center Right", "path": shallowCenterPath, "color":"green"},
      {"num": 4, "name": "Slot", "path": slotPath, "color":"green"},
      {"num": 5, "name": "Deep Slot", "path": deepSlotPath, "color":"red"},
      {"num": 6, "name": "Attacking Right", "path": attackingRightPath, "color":"yellow"},
      {"num": 7, "name": "Attacking Left", "path": attackingLeftPath, "color":"blue"}

];

// append shaded paths
const shades = d3.select('svg').append('g')
for(var i = 0; i < zoneInfo.length; i++) {
  shades.append("path")
    .attr("d", zoneInfo[i].path)
    .attr("fill", zoneInfo[i].color)
    .attr("stroke", "black")
    .attr("stroke-width", '0.3')
    .attr("opacity", 0.85);
}

const points = [
  {x: 15, y: 35, zonenum: ""},
  {x: 29, y: 35, zonenum: ""},
  {x: 59, y: 55, zonenum: ""},
  {x: 45, y: 75, zonenum: ""},
  {x: 57, y: 49, zonenum: ""}
]
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg></svg>

这些路径中的大多数是梯形和其他奇数四边形,并且基于梯形线的函数通过一系列不等式计算每个点的区域需要相当长的时间(除非我有一个函数来做到这一点)以及)。

是否有更简单的方法来确定我的点列表属于哪个区域?谢谢!

2 个答案:

答案 0 :(得分:1)

如果可以使用坐标将路径转换为数组数组,例如......

[[85, 25], [85, 55], [57, 50], [62, 25], [85, 25]]

...您可以使用d3.polygonContains(),其中......

  

当且仅当指定的点位于指定的多边形内时才返回true。

例如,过滤您的zoneInfo数组并将num值分配给相应的点:

points.forEach(function(d) {
  d.zonenum = zoneInfo.filter(function(e) {
    return d3.polygonContains(e.polygon, [d.x, d.y])
  })[0].num
});

这是一个包含一些路径和点的演示:

&#13;
&#13;
const zoneInfo = [{
    "num": 1,
    "name": "Shallow Right",
    "color": "blue",
    "polygon": [
      [0, 25],
      [0, 55],
      [28, 50],
      [23, 25],
      [0, 25]
    ]
  },
  {
    "num": 2,
    "name": "Shallow Left",
    "color": "yellow",
    "polygon": [
      [85, 25],
      [85, 55],
      [57, 50],
      [62, 25],
      [85, 25]
    ]
  },
  {
    "num": 3,
    "name": "Shallow Center Right",
    "color": "green",
    "polygon": [
      [28, 50],
      [23, 25],
      [62, 25],
      [57, 50],
      [28, 50]
    ]
  },
  {
    "num": 4,
    "name": "Slot",
    "color": "green",
    "polygon": [
      [27, 89],
      [32, 71],
      [53, 71],
      [58, 89],
      [27, 89]
    ]
  },
  {
    "num": 5,
    "name": "Deep Slot",
    "color": "red",
    "polygon": [
      [32, 71],
      [28, 50],
      [57, 50],
      [53, 71],
      [32, 71]
    ]
  }
];


const points = [{
    x: 15,
    y: 35,
    zonenum: ""
  },
  {
    x: 29,
    y: 35,
    zonenum: ""
  },
  {
    x: 45,
    y: 75,
    zonenum: ""
  },
  {
    x: 57,
    y: 49,
    zonenum: ""
  }
];

points.forEach(function(d) {
  d.zonenum = zoneInfo.filter(function(e) {
    return d3.polygonContains(e.polygon, [d.x, d.y])
  })[0].num
});

console.log(points)
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg></svg>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

R中的这个解决方案为我做了诀窍

createZones <- function(xvals, yvals) {

    getM <- function(x1, y1, x2, y2) {
      return((y2 - y1) / (x2 - x1))
    }
    getB <- function(x1, y1, x2, y2) {
      slopeM <- (y2 - y1) / (x2 - x1)
      return(y1 - slopeM*x1)
    }


    # based on xScale inverted due to d3 coordinates
    zonenumbers = rep(0, length(xvals))
    zonenames = rep("", length(xvals))

    zonenumbers[which(yvals <= 25)] <- 1
    zonenames[which(yvals <= 25)] <- 'Neutral Zone'

    zonenumbers[which(yvals > 25 & 
                        yvals > getM(23, 25, 28, 50) * xvals + getB(23, 25, 28, 50) & 
                        yvals < getM(0, 55, 28, 50) * xvals + getB(0, 55, 28, 50))] <- 2
    zonenames[which(yvals > 25 & 
                      yvals > getM(23, 25, 28, 50) * xvals + getB(23, 25, 28, 50) & 
                      yvals < getM(0, 55, 28, 50) * xvals + getB(0, 55, 28, 50))] <- 'Shallow Right'

    zonenumbers[which(yvals > 25 & 
                        yvals < getM(57, 50, 85, 55) * xvals + getB(57, 50, 85, 55) & 
                        yvals > getM(57, 50, 62, 25) * xvals + getB(57, 50, 62, 25))] <- 3
    zonenames[which(yvals > 25 & 
                      yvals < getM(57, 50, 85, 55) * xvals + getB(57, 50, 85, 55) & 
                      yvals > getM(57, 50, 62, 25) * xvals + getB(57, 50, 62, 25))] <- 'Shallow Left'

    zonenumbers[which(yvals > 25 & yvals <= 50 &  
                        yvals < getM(23, 25, 28, 50) * xvals + getB(23, 25, 28, 50) & 
                        yvals < getM(57, 50, 62, 25) * xvals + getB(57, 50, 62, 25))] <- 4
    zonenames[which(yvals > 25 & yvals <= 50 & 
                      yvals < getM(23, 25, 28, 50) * xvals + getB(23, 25, 28, 50) & 
                      yvals < getM(57, 50, 62, 25) * xvals + getB(57, 50, 62, 25))] <- 'Shallow Center'

    zonenumbers[which(yvals > 71 & yvals <= 89 &
                        yvals > getM(27, 89, 32, 71) * xvals + getB(27, 89, 32, 71) & 
                        yvals > getM(53, 71, 58, 89) * xvals + getB(53, 71, 58, 89))] <- 5
    zonenames[which(yvals > 71 & yvals <= 89 & 
                      yvals > getM(27, 89, 32, 71) * xvals + getB(27, 89, 32, 71) & 
                      yvals > getM(53, 71, 58, 89) * xvals + getB(53, 71, 58, 89))] <- 'Slot'

    zonenumbers[which(yvals > 50 & yvals <= 71 & 
                        yvals < getM(32, 71, 28, 50) * xvals + getB(32, 71, 28, 50) & 
                        yvals < getM(57, 50, 53, 71) * xvals + getB(57, 50, 53, 71))] <- 6
    zonenames[which(yvals > 50 & yvals <= 71 & 
                      yvals < getM(27, 89, 32, 71) * xvals + getB(27, 89, 32, 71) & 
                      yvals < getM(53, 71, 58, 89) * xvals + getB(53, 71, 58, 89))] <- 'Deep Slot'

    zonenumbers[which(yvals <= 89 &  
                        yvals < getM(27, 89, 32, 71) * xvals + getB(27, 89, 32, 71) & 
                        yvals > getM(32, 71, 28, 50) * xvals + getB(32, 71, 28, 50) &
                        yvals > getM(28, 50, 0, 55) * xvals + getB(28, 50, 0, 55))] <- 7
    zonenames[which(yvals <= 89 &
                      yvals < getM(27, 89, 32, 71) * xvals + getB(27, 89, 32, 71) & 
                      yvals > getM(32, 71, 28, 50) * xvals + getB(32, 71, 28, 50) &
                      yvals > getM(28, 50, 0, 55) * xvals + getB(28, 50, 0, 55))] <- 'Attacking Right'

    zonenumbers[which(yvals <= 89 &
                        yvals < getM(58, 89, 53, 71) * xvals + getB(58, 89, 53, 71) & 
                        yvals > getM(53, 71, 57, 50) * xvals + getB(53, 71, 57, 50) &
                        yvals > getM(57, 50, 85, 55) * xvals + getB(57, 50, 85, 55))] <- 8
    zonenames[which(yvals <= 89 &
                      yvals < getM(58, 89, 53, 71) * xvals + getB(58, 89, 53, 71) & 
                      yvals > getM(53, 71, 57, 50) * xvals + getB(53, 71, 57, 50) &
                      yvals > getM(57, 50, 85, 55) * xvals + getB(57, 50, 85, 55))] <- 'Attacking Left'

    zonenumbers[which(yvals > 89)] <- 9 
    zonenames[which(yvals > 89)] <- 'Behind The Net'

    return(data.frame(zonenumber = zonenumbers, zonename = zonenames, stringsAsFactors = FALSE))
  }