単に範囲Aと範囲Bが被っているか否かを識別するならば、次の関数があっさり書けて便利です。
/** 範囲が被っているならば true */
function isOverLap($a, $b)
{
// 範囲が被らない条件を全て否定できれば、範囲が被っている
// Aの終点がBの始点の左側にあるか、Aの終点がBの始点の右側にあれば被っていない
// 被っていないならば true になる条件の否定で、被っているならば true
return !($a['end'] < $b['start'] || $b['end'] < $a['start']);
}
図でざっくばらんに説明すると次です。
# Aの終点がBの始点の左側にある A |----| B |----| # Aの始点がBの終点の右側にある A |----| B |----| # それ以外の場合は被っている
求められているものが被っているか否かの判定のみでしたら、これであっさり作れます。一つ一つ被る条件を確かめるより計算量が少なく済むのもなかなかうれしいです。
被っているいないの真偽にとどまらず両者の関係も分けるならば、次の様に数直線上の並びを表現した if を繰り返すのが単純で理解しやすいです。
<?php
/** 被っているか被っていないか、それはどういう状況なのかの種類をまとめた enum */
enum OverlapType
{
case NO_OVERLAP_A_MORE_LEFT_OF_B;
case NO_OVERLAP_A_MORE_RIGHT_OF_B;
case A_RIGHT_LAP_B_LEFT;
case A_INCLUDE_B;
case A_INCLUDED_B;
case A_LEFT_LAP_B_RIGHT;
}
/** 図解コメント付き */
function getOverlapTypeWithChartComment($a, $b): OverlapType
{
if (isset($a['end'], $a['start'], $b['end'], $b['start'])) {
throw new \InvalidArgumentException('存在しない値');
}
if ($a['end'] < $a['start'] || $b['end'] < $b['start']) {
throw new \InvalidArgumentException('不正な大小');
}
// AがBの左側にある
if ($a['end'] < $b['start']) {
return OverlapType::NO_OVERLAP_A_MORE_LEFT_OF_B;
}
// AはBの右側にある
if ($b['end'] < $a['start']) {
return OverlapType::NO_OVERLAP_A_MORE_RIGHT_OF_B;
}
// Aの右端にBの左端が被っている
// A |----|
// B |----|
if ($a['start'] <= $b['start'] && $b['start'] <= $a['end'] && $a['end'] <= $b['end']) {
return OverlapType::A_RIGHT_LAP_B_LEFT;
}
// # AがBを包含している
// A |-----------|
// B |----|
if ($a['start'] <= $b['start'] && $b['start'] <= $b['end'] && $b['end'] <= $a['end']) {
return OverlapType::A_INCLUDE_B;
}
// # AがBに包含されている
// A |----|
// B |-----------|
if ($b['start'] <= $a['start'] && $a['start'] <= $a['end'] && $a['end'] <= $b['end']) {
return OverlapType::A_INCLUDED_B;
}
// # Aの左端にBの右端が被っている
// A |----|
// B |-----------|
if ($b['start'] <= $a['start'] && $a['start'] <= $b['end'] && $b['end'] <= $a['end']) {
return OverlapType::A_LEFT_LAP_B_RIGHT;
}
throw new \RuntimeException('想定していない分岐');
}
/** 名前の工夫のみ */
function getOverlapTypeWithValName($a, $b): OverlapType
{
if (isset($a['end'], $a['start'], $b['end'], $b['start'])) {
throw new \InvalidArgumentException('存在しない値');
}
if ($a['end'] < $a['start'] || $b['end'] < $b['start']) {
throw new \InvalidArgumentException('不正な大小');
}
// AS,AE,BS,BE で今どれを参照しているか見やすくするために変数化
$AStart = $a['start'];
$AEnd = $a['end'];
$BStart = $b['start'];
$BEnd = $b['end'];
if ($AEnd < $BStart) {// AはBの左側にある
return OverlapType::NO_OVERLAP_A_MORE_LEFT_OF_B;
}
if ($BEnd < $AStart) {// AはBの右側にある
return OverlapType::NO_OVERLAP_A_MORE_RIGHT_OF_B;
}
// Aの右端にBの左端が被っている
if ($AStart <= $BStart && $BStart <= $AEnd && $AEnd <= $BEnd) {
return OverlapType::A_RIGHT_LAP_B_LEFT;
}
// AがBを包含している
if ($AStart <= $BStart && $BStart <= $BEnd && $BEnd <= $AEnd) {
return OverlapType::A_INCLUDE_B;
}
// AがBに包含されている
if ($BStart <= $AStart && $AStart <= $AEnd && $AEnd <= $BEnd) {
return OverlapType::A_INCLUDED_B;
}
// Aの左端にBの右端が被っている
if ($BStart <= $AStart && $AStart <= $BEnd && $BEnd <= $AEnd) {
return OverlapType::A_LEFT_LAP_B_RIGHT;
}
throw new \RuntimeException('想定していない分岐');
}