<?php
namespace lib;
/**
* Description of Area
*
* @author lsf
*/
class Area{
// 一個表示區域的三維數組
protected $config = null;
// 包含每個區域的四邊形
protected $rectangles = null;
// 每個區域(多邊形)的所有邊
protected $lines = null;
// 要判斷的點的x, y坐標
protected $_x = null;
protected $_y = null;
public function __construct($config){
$this->config = $config;
$this->initRectangles();
$this->initLines();
}
/*
獲取包含每個配送區域的四邊形
*/
private function initRectangles(){
foreach ($this->config as $k => $v) {
$this->rectangles[$k]['minX'] = $this->getMinXInEachConfig($k);
$this->rectangles[$k]['minY'] = $this->getMinYInEachConfig($k);
$this->rectangles[$k]['maxX'] = $this->getMaxXInEachConfig($k);
$this->rectangles[$k]['maxY'] = $this->getMaxYInEachConfig($k);
}
}
/*
初始化每個區域(多邊形)的邊(線段:直線的一部分【限制x或者y坐標范圍】)
n 個頂點構成的多邊形,有 n-1 條邊
*/
private function initLines(){
foreach ($this->config as $k => $v) {
$pointNum = count($v); // 區域的頂點個數
$lineNum = $pointNum - 1; // 區域的邊條數
for($i=0; $i<$lineNum; $i++){
// y=kx+b : k
if($this->config[$k][$i]['x'] - $this->config[$k][$i+1]['x'] == 0) $this->lines[$k][$i]['k'] = 0;
else $this->lines[$k][$i]['k'] =
($this->config[$k][$i]['y'] - $this->config[$k][$i+1]['y'])/($this->config[$k][$i]['x'] - $this->config[$k][$i+1]['x']);
// y=kx+b : b
$this->lines[$k][$i]['b'] = $this->config[$k][$i+1]['y'] - $this->lines[$k][$i]['k'] * $this->config[$k][$i+1]['x'];
$this->lines[$k][$i]['lx'] = min($this->config[$k][$i]['x'], $this->config[$k][$i+1]['x']);
$this->lines[$k][$i]['rx'] = max($this->config[$k][$i]['x'], $this->config[$k][$i+1]['x']);
}
$pointNum-=1;
if($this->config[$k][$pointNum]['x'] - $this->config[$k][0]['x'] == 0) $this->lines[$k][$pointNum]['k'] = 0;
else $this->lines[$k][$pointNum]['k'] =
($this->config[$k][$pointNum]['y'] - $this->config[$k][0]['y'])/($this->config[$k][$pointNum]['x'] - $this->config[$k][0]['x']);
// y=kx+b : b
$this->lines[$k][$pointNum]['b'] = $this->config[$k][0]['y'] - $this->lines[$k][$pointNum]['k'] * $this->config[$k][0]['x'];
$this->lines[$k][$pointNum]['lx'] = min($this->config[$k][$pointNum]['x'], $this->config[$k][0]['x']);
$this->lines[$k][$pointNum]['rx'] = max($this->config[$k][$pointNum]['x'], $this->config[$k][0]['x']);
}
}
/*
獲取一組坐標中,x坐標最小值
*/
private function getMinXInEachConfig($index){
$minX = 200;
foreach ($this->config[$index] as $k => $v) {
if($v['x'] < $minX){
$minX = $v['x'];
}
}
return $minX;
}
/*
獲取一組坐標中,y坐標最小值
*/
private function getMinYInEachConfig($index){
$minY = 200;
foreach ($this->config[$index] as $k => $v) {
if($v['y'] < $minY){
$minY = $v['y'];
}
}
return $minY;
}
/*
獲取一組坐標中,x坐標最大值
*/
public function getMaxXInEachConfig($index){
$maxX = 0;
foreach ($this->config[$index] as $k => $v) {
if($v['x'] > $maxX){
$maxX = $v['x'];
}
}
return $maxX;
}
/*
獲取一組坐標中,y坐標最大值
*/
public function getMaxYInEachConfig($index){
$maxY = 0;
foreach ($this->config[$index] as $k => $v) {
if($v['y'] > $maxY){
$maxY = $v['y'];
}
}
return $maxY;
}
/*
獲取 y=y0 與特定區域的所有邊的交點,並去除和頂點重復的,再將交點分為左和右兩部分
*/
private function getCrossPointInCertainConfig($index){
$crossPoint = null;
foreach ($this->lines[$index] as $k => $v) {
if($v['k'] == 0) return true;
$x0 = ($this->_y - $v['b']) / $v['k']; // 交點x坐標
if($x0 == $this->_x) return true; // 點在邊上
if($x0 > $v['lx'] && $x0 < $v['rx']){
if($x0 < $this->_x) $crossPoint['left'][] = $x0;
if($x0 > $this->_x) $crossPoint['right'][] = $x0;
}
}
return $crossPoint;
}
/*
檢測一個點,是否在區域內
返回結果:
return === false : 點不在區域內
return 0, 1, 2, 3 ... 點所在的區域編號(配置文件中的區域編號。)
*/
public function checkPoint($x, $y){
$this->_x = $x;
$this->_y = $y;
$contain = null;
foreach ($this->rectangles as $k => $v) {
if($x > $v['maxX'] || $x < $v['minX'] || $y > $v['maxY'] || $y < $v['minY']){
continue;
}else{
$contain = $k;
break;
}
}
if($contain === null) return false;
$crossPoint = $this->getCrossPointInCertainConfig($contain);
if($crossPoint == true) return $contain;
//if(count($crossPoint['left'])%2 == 1 && count($crossPoint['right'])%2 == 1) return $contain;
return false;
}
}