2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,4 +1,6 @@
{{Wikipedia|Point_in_polygon}}
<br>
Given a point and a polygon, check if the point is inside or outside the polygon using the [[wp:Point in polygon#Ray casting algorithm|ray-casting algorithm]].
A pseudocode can be simply:
@ -27,6 +29,7 @@ So the problematic points are those inside the white area (the box delimited by
[[Image:posslope.png|128px|thumb|right]]
[[Image:negslope.png|128px|thumb|right]]
Let us take into account a segment AB (the point A having y coordinate always smaller than B's y coordinate, i.e. point A is always below point B) and a point P. Let us use the cumbersome notation PAX to denote the angle between segment AP and AX, where X is always a point on the horizontal line passing by A with x coordinate bigger than the maximum between the x coordinate of A and the x coordinate of B. As explained graphically by the figures on the right, if PAX is greater than the angle BAX, then the ray starting from P intersects the segment AB. (In the images, the ray starting from P<sub>A</sub> does not intersect the segment, while the ray starting from P<sub>B</sub> in the second picture, intersects the segment).
Points on the boundary or "on" a vertex are someway special and through this approach we do not obtain ''coherent'' results. They could be treated apart, but it is not necessary to do so.
@ -68,4 +71,5 @@ An algorithm for the previous speech could be (if P is a point, Px is its x coor
'''end''' '''if'''
'''end''' '''if'''
(To avoid the "ray on vertex" problem, the point is moved upward of a small quantity &epsilon;)
(To avoid the "ray on vertex" problem, the point is moved upward of a small quantity &nbsp; <big>&epsilon;</big>.)
<br><br>

View file

@ -0,0 +1,81 @@
#include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
using namespace std;
const double epsilon = numeric_limits<float>().epsilon();
const numeric_limits<double> DOUBLE;
const double MIN = DOUBLE.min();
const double MAX = DOUBLE.max();
struct Point { const double x, y; };
struct Edge {
const Point a, b;
bool operator()(const Point& p) const
{
if (a.y > b.y) return Edge{ b, a }(p);
if (p.y == a.y || p.y == b.y) return operator()({ p.x, p.y + epsilon });
if (p.y > b.y || p.y < a.y || p.x > max(a.x, b.x)) return false;
if (p.x < min(a.x, b.x)) return true;
auto blue = abs(a.x - p.x) > MIN ? (p.y - a.y) / (p.x - a.x) : MAX;
auto red = abs(a.x - b.x) > MIN ? (b.y - a.y) / (b.x - a.x) : MAX;
return blue >= red;
}
};
struct Figure {
const string name;
const initializer_list<Edge> edges;
bool contains(const Point& p) const
{
auto c = 0;
for (auto e : edges) if (e(p)) c++;
return c % 2 != 0;
}
template<unsigned char W = 3>
void check(const initializer_list<Point>& points, ostream& os) const
{
os << "Is point inside figure " << name << '?' << endl;
for (auto p : points)
os << " (" << setw(W) << p.x << ',' << setw(W) << p.y << "): " << boolalpha << contains(p) << endl;
os << endl;
}
};
int main()
{
const initializer_list<Point> points = { { 5.0, 5.0}, {5.0, 8.0}, {-10.0, 5.0}, {0.0, 5.0}, {10.0, 5.0}, {8.0, 5.0}, {10.0, 10.0} };
const Figure square = { "Square",
{ {{0.0, 0.0}, {10.0, 0.0}}, {{10.0, 0.0}, {10.0, 10.0}}, {{10.0, 10.0}, {0.0, 10.0}}, {{0.0, 10.0}, {0.0, 0.0}} }
};
const Figure square_hole = { "Square hole",
{ {{0.0, 0.0}, {10.0, 0.0}}, {{10.0, 0.0}, {10.0, 10.0}}, {{10.0, 10.0}, {0.0, 10.0}}, {{0.0, 10.0}, {0.0, 0.0}},
{{2.5, 2.5}, {7.5, 2.5}}, {{7.5, 2.5}, {7.5, 7.5}}, {{7.5, 7.5}, {2.5, 7.5}}, {{2.5, 7.5}, {2.5, 2.5}}
}
};
const Figure strange = { "Strange",
{ {{0.0, 0.0}, {2.5, 2.5}}, {{2.5, 2.5}, {0.0, 10.0}}, {{0.0, 10.0}, {2.5, 7.5}}, {{2.5, 7.5}, {7.5, 7.5}},
{{7.5, 7.5}, {10.0, 10.0}}, {{10.0, 10.0}, {10.0, 0.0}}, {{10.0, 0}, {2.5, 2.5}}
}
};
const Figure exagon = { "Exagon",
{ {{3.0, 0.0}, {7.0, 0.0}}, {{7.0, 0.0}, {10.0, 5.0}}, {{10.0, 5.0}, {7.0, 10.0}}, {{7.0, 10.0}, {3.0, 10.0}},
{{3.0, 10.0}, {0.0, 5.0}}, {{0.0, 5.0}, {3.0, 0.0}}
}
};
for(auto f : {square, square_hole, strange, exagon})
f.check(points, cout);
return EXIT_SUCCESS;
}

View file

@ -1,8 +1,8 @@
package main
import (
"math"
"fmt"
"math"
)
type xy struct {
@ -85,7 +85,12 @@ var tpg = []poly{
{p13, p14}, {p14, p9}, {p9, p11}}},
}
var tpt = []xy{{5, 5}, {5, 8}, {-10, 5}, {0, 5}, {10, 5}, {8, 5}, {10, 10}}
var tpt = []xy{
// test points common in other solutions on this page
{5, 5}, {5, 8}, {-10, 5}, {0, 5}, {10, 5}, {8, 5}, {10, 10},
// test points that show the problem with "strange"
{1, 2}, {2, 1},
}
func main() {
for _, pg := range tpg {

View file

@ -0,0 +1,50 @@
package main
import "fmt"
type xy struct {
x, y float64
}
type closedPoly struct {
name string
vert []xy
}
func inside(pt xy, pg closedPoly) bool {
if len(pg.vert) < 3 {
return false
}
in := rayIntersectsSegment(pt, pg.vert[len(pg.vert)-1], pg.vert[0])
for i := 1; i < len(pg.vert); i++ {
if rayIntersectsSegment(pt, pg.vert[i-1], pg.vert[i]) {
in = !in
}
}
return in
}
func rayIntersectsSegment(p, a, b xy) bool {
return (a.y > p.y) != (b.y > p.y) &&
p.x < (b.x-a.x)*(p.y-a.y)/(b.y-a.y)+a.x
}
var tpg = []closedPoly{
{"square", []xy{{0, 0}, {10, 0}, {10, 10}, {0, 10}}},
{"square hole", []xy{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0},
{2.5, 2.5}, {7.5, 2.5}, {7.5, 7.5}, {2.5, 7.5}, {2.5, 2.5}}},
{"strange", []xy{{0, 0}, {2.5, 2.5}, {0, 10}, {2.5, 7.5}, {7.5, 7.5},
{10, 10}, {10, 0}, {2.5, 2.5}}},
{"exagon", []xy{{3, 0}, {7, 0}, {10, 5}, {7, 10}, {3, 10}, {0, 5}}},
}
var tpt = []xy{{1, 2}, {2, 1}}
func main() {
for _, pg := range tpg {
fmt.Printf("%s:\n", pg.name)
for _, pt := range tpt {
fmt.Println(pt, inside(pt, pg))
}
}
}

View file

@ -0,0 +1,56 @@
import static java.lang.Math.*;
public class RayCasting {
static boolean intersects(int[] A, int[] B, double[] P) {
if (A[1] > B[1])
return intersects(B, A, P);
if (P[1] == A[1] || P[1] == B[1])
P[1] += 0.0001;
if (P[1] > B[1] || P[1] < A[1] || P[0] > max(A[0], B[0]))
return false;
if (P[0] < min(A[0], B[0]))
return true;
double red = (P[1] - A[1]) / (double) (P[0] - A[0]);
double blue = (B[1] - A[1]) / (double) (B[0] - A[0]);
return red >= blue;
}
static boolean contains(int[][] shape, double[] pnt) {
boolean inside = false;
int len = shape.length;
for (int i = 0; i < len; i++) {
if (intersects(shape[i], shape[(i + 1) % len], pnt))
inside = !inside;
}
return inside;
}
public static void main(String[] a) {
double[][] testPoints = {{10, 10}, {10, 16}, {-20, 10}, {0, 10},
{20, 10}, {16, 10}, {20, 20}};
for (int[][] shape : shapes) {
for (double[] pnt : testPoints)
System.out.printf("%7s ", contains(shape, pnt));
System.out.println();
}
}
final static int[][] square = {{0, 0}, {20, 0}, {20, 20}, {0, 20}};
final static int[][] squareHole = {{0, 0}, {20, 0}, {20, 20}, {0, 20},
{5, 5}, {15, 5}, {15, 15}, {5, 15}};
final static int[][] strange = {{0, 0}, {5, 5}, {0, 20}, {5, 15}, {15, 15},
{20, 20}, {20, 0}};
final static int[][] hexagon = {{6, 0}, {14, 0}, {20, 10}, {14, 20},
{6, 20}, {0, 10}};
final static int[][][] shapes = {square, squareHole, strange, hexagon};
}

View file

@ -0,0 +1,37 @@
package ray_casting
import java.lang.Double.*
import java.lang.Math.*
data class Point(val x: Double, val y: Double)
data class Edge(val s: Point, val e: Point) {
operator fun invoke(p: Point) : Boolean = when {
s.y > e.y -> Edge(e, s).invoke(p)
p.y == s.y || p.y == e.y -> invoke(Point(p.x, p.y + epsilon))
p.y > e.y || p.y < s.y || p.x > max(s.x, e.x) -> false
p.x < min(s.x, e.x) -> true
else -> {
val blue = if (abs(s.x - p.x) > MIN_VALUE) (p.y - s.y) / (p.x - s.x) else MAX_VALUE
val red = if (abs(s.x - e.x) > MIN_VALUE) (e.y - s.y) / (e.x - s.x) else MAX_VALUE
blue >= red
}
}
val epsilon = 0.00001
}
class Figure(val name: String, val edges: Array<Edge>) {
operator fun contains(p: Point) = edges.count({ it(p) }) % 2 != 0
}
object Ray_casting {
fun check(figures : Array<Figure>, points : List<Point>) {
println("points: " + points)
figures.forEach { f ->
println("figure: " + f.name)
f.edges.forEach { println(" " + it) }
println("result: " + (points.map { it in f }))
}
}
}

View file

@ -0,0 +1,19 @@
package ray_casting
fun main(args: Array<String>) {
val figures = arrayOf(Figure("Square", arrayOf(Edge(Point(0.0, 0.0), Point(10.0, 0.0)), Edge(Point(10.0, 0.0), Point(10.0, 10.0)),
Edge(Point(10.0, 10.0), Point(0.0, 10.0)),Edge(Point(0.0, 10.0), Point(0.0, 0.0)))),
Figure("Square hole", arrayOf(Edge(Point(0.0, 0.0), Point(10.0, 0.0)), Edge(Point(10.0, 0.0), Point(10.0, 10.0)),
Edge(Point(10.0, 10.0), Point(0.0, 10.0)), Edge(Point(0.0, 10.0), Point(0.0, 0.0)), Edge(Point(2.5, 2.5), Point(7.5, 2.5)),
Edge(Point(7.5, 2.5), Point(7.5, 7.5)),Edge(Point(7.5, 7.5), Point(2.5, 7.5)), Edge(Point(2.5, 7.5), Point(2.5, 2.5)))),
Figure("Strange", arrayOf(Edge(Point(0.0, 0.0), Point(2.5, 2.5)), Edge(Point(2.5, 2.5), Point(0.0, 10.0)),
Edge(Point(0.0, 10.0), Point(2.5, 7.5)), Edge(Point(2.5, 7.5), Point(7.5, 7.5)), Edge(Point(7.5, 7.5), Point(10.0, 10.0)),
Edge(Point(10.0, 10.0), Point(10.0, 0.0)), Edge(Point(10.0, 0.0), Point(2.5, 2.5)))),
Figure("Exagon", arrayOf(Edge(Point(3.0, 0.0), Point(7.0, 0.0)), Edge(Point(7.0, 0.0), Point(10.0, 5.0)), Edge(Point(10.0, 5.0), Point(7.0, 10.0)),
Edge(Point(7.0, 10.0), Point(3.0, 10.0)), Edge(Point(3.0, 10.0), Point(0.0, 5.0)), Edge(Point(0.0, 5.0), Point(3.0, 0.0)))))
val points = listOf(Point(5.0, 5.0), Point(5.0, 8.0), Point(-10.0, 5.0), Point(0.0, 5.0),
Point(10.0, 5.0), Point(8.0, 5.0), Point(10.0, 10.0))
Ray_casting.check(figures, points)
}

View file

@ -1,52 +1,51 @@
/*REXX pgm checks to see if a horizontal ray from point P intersects a polygon*/
call points 5 5, 5 8, -10 5, 0 5, 10 5, 8 5, 10 10
A=2.5; B=7.5 /*◄───for shorter args*/
call poly 0 0, 10 0, 10 10, 0 10 ; call test 'square'
call poly 0 0, 10 0, 10 10, 0 10, A A, B A, B B, A B ; call test 'square hole
call poly 0 0, A A, 0 10, A B, B B, 10 10, 10 0 ; call test 'irregular'
call poly 3 0, 7 0, 10 5, 7 10, 3 10, 0 5 ; call test 'hexagon'
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
in_out: procedure expose point. poly.; parse arg p; #=0
do side=1 to poly.0 by 2; #=#+ray_intersect(p,side)
end /*side*/
return #//2 /*ODD is inside. EVEN is outside. */
/*────────────────────────────────────────────────────────────────────────────*/
points: n=0; v='POINT.'; do j=1 for arg(); n=n+1; _=arg(j); parse var _ xx yy
call value v||n'.X',xx
call value v||n'.Y',yy
end /*j*/
call value v'0',n /*define the number of points.*/
/*REXX program verifies if a horizontal ray from point P intersects a polygon. */
call points 5 5, 5 8, -10 5, 0 5, 10 5, 8 5, 10 10
A=2.5; B=7.5 /* ◄───── used for shorter arguments (below).*/
call poly 0 0, 10 0, 10 10, 0 10 ; call test 'square'
call poly 0 0, 10 0, 10 10, 0 10, A A, B A, B B, A B ; call test 'square hole'
call poly 0 0, A A, 0 10, A B, B B, 10 10, 10 0 ; call test 'irregular'
call poly 3 0, 7 0, 10 5, 7 10, 3 10, 0 5 ; call test 'hexagon'
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
in$out: procedure expose point. poly.; parse arg p; #=0
do side=1 to poly.0 by 2; #=#+intersect(p,side); end /*side*/
return # // 2 /*ODD is inside. EVEN is outside.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
intersect: procedure expose point. poly.; parse arg ?,s; sp=s+1
epsilon='1e' || (-digits()%2); infinity="1e" || (digits() *2)
Px=point.?.x; Ax=poly.s.x; Ay=poly.s.y
Py=point.?.y; Bx=poly.sp.x; By=poly.sp.y /* [↓] do a swap.*/
if Ay>By then parse value Ax Ay Bx By with Bx By Ax Ay
if Py=Ay | Py=By then Py=Py + epsilon
if Py<Ay | Py>By | Px>max(Ax,Bx) then return 0
if Px<min(Ax,Bx) then return 1
if Ax\=Bx then m_red =(By-Ay) / (Bx-Ax)
else m_red =infinity
if Ax\=Px then m_blue=(Py-Ay) / (Px-Ax)
else return 1
return m_blue >= m_red
/*──────────────────────────────────────────────────────────────────────────────────────*/
points: wx=0; wy=0; do j=1 for arg(); parse value arg(j) with xx yy
wx=max(wx, length(xx) ); call value 'POINT.'j".X", xx
wy=max(wy, length(yy) ); call value 'POINT.'j".Y", yy
end /*j*/
call value point.0, j-1 /*define the number of points. */
return
/*────────────────────────────────────────────────────────────────────────────*/
poly: n=0; v='POLY.'; parse arg Fx Fy /* [↓] process the X,Y points*/
do j=1 for arg(); n=n+1; _=arg(j); parse var _ xx yy
call value v||n'.X', word(_,1); call value v||n'.Y', word(_,2)
if n//2 then iterate
n=n+1
call value v||n'.X', word(_,1); call value v||n'.Y', word(_,2)
end /*j*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
poly: @='POLY.'; parse arg Fx Fy /* [↓] process the X,Y points.*/
n=0
do j=1 for arg(); n=n+1; parse value arg(j) with xx yy
call value @ || n'.X', xx ; call value @ || n".Y", yy
if n//2 then iterate; n=n+1
call value @ || n'.X', xx ; call value @ || n".Y", yy
end /*j*/
n=n+1
call value v||n".X", Fx; call value v||n".Y", Fy; call value v'0',n
return /*POLY.0 is number of segments/sides.*/
/*────────────────────────────────────────────────────────────────────────────*/
ray_intersect: procedure expose point. poly.; parse arg ?,s; sp=s+1
epsilon='1e' || (digits()%2); infinity='1e' || (digits() *2)
Px=point.?.x; Ax=poly.s.x; Ay=poly.s.y
Py=point.?.y; Bx=poly.sp.x; By=poly.sp.y /* [↓] do a swap*/
if Ay>By then parse value Ax Ay Bx By with Bx By Ax Ay
if Py=Ay | Py=By then Py=Py+epsilon
if Py<Ay | Py>By | Px>max(Ax,Bx) then return 0
if Px<min(Ax,Bx) then return 1
if Ax\=Bx then m_red =(By-Ay)/(Bx-Ax)
else m_red =infinity
if Ax\=Px then m_blue=(Py-Ay)/(Px-Ax)
else return 1
return m_blue>=m_red
/*────────────────────────────────────────────────────────────────────────────*/
test: say; do k=1 for point.0; say right(' ['arg(1)"] point:",30),
right(point.k.x', 'point.k.y, 9) " is ",
word('outside inside', in_out(k)+1)
end /*k*/
return
call value @ || n'.X', Fx; call value @ || n".Y", Fy; call value @'0',n
return /*POLY.0 is # segments(sides).*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
test: say; do k=1 for point.0; w=wx+wy+2
say right(' ['arg(1)"] point:", 30),
right( right(point.k.x, wx)', 'right(point.k.y, wy), w) " is ",
right( word('outside inside', in$out(k)+1), 7)
end /*k*/
return

View file

@ -1,46 +1,47 @@
case class Figure(name: String, edges: ((Double, Double), (Double, Double))*) {}
package scala.ray_casting
case class Edge(_1: (Double, Double), _2: (Double, Double)) {
import Math._
import Double._
def raySegI(p: (Double, Double)): Boolean = {
if (_1._2 > _2._2) return Edge(_2, _1).raySegI(p)
if (p._2 == _1._2 || p._2 == _2._2) return raySegI((p._1, p._2 + epsilon))
if (p._2 > _2._2 || p._2 < _1._2 || p._1 > max(_1._1, _2._1))
return false
if (p._1 < min(_1._1, _2._1)) return true
val blue = if (abs(_1._1 - p._1) > MinValue) (p._2 - _1._2) / (p._1 - _1._1) else MaxValue
val red = if (abs(_1._1 - _2._1) > MinValue) (_2._2 - _1._2) / (_2._1 - _1._1) else MaxValue
blue >= red
}
final val epsilon = 0.00001
}
case class Figure(name: String, edges: Seq[Edge]) {
def contains(p: (Double, Double)) = edges.count(_.raySegI(p)) % 2 != 0
}
object Ray_casting extends App {
import Math._
import Double._
val figures = Seq(Figure("Square", Seq(((0.0, 0.0), (10.0, 0.0)), ((10.0, 0.0), (10.0, 10.0)),
((10.0, 10.0), (0.0, 10.0)),((0.0, 10.0), (0.0, 0.0)))),
Figure("Square hole", Seq(((0.0, 0.0), (10.0, 0.0)), ((10.0, 0.0), (10.0, 10.0)),
((10.0, 10.0), (0.0, 10.0)), ((0.0, 10.0), (0.0, 0.0)), ((2.5, 2.5), (7.5, 2.5)),
((7.5, 2.5), (7.5, 7.5)),((7.5, 7.5), (2.5, 7.5)), ((2.5, 7.5), (2.5, 2.5)))),
Figure("Strange", Seq(((0.0, 0.0), (2.5, 2.5)), ((2.5, 2.5), (0.0, 10.0)),
((0.0, 10.0), (2.5, 7.5)), ((2.5, 7.5), (7.5, 7.5)), ((7.5, 7.5), (10.0, 10.0)),
((10.0, 10.0), (10.0, 0.0)), ((10.0, 0.0), (2.5, 2.5)))),
Figure("Exagon", Seq(((3.0, 0.0), (7.0, 0.0)), ((7.0, 0.0), (10.0, 5.0)), ((10.0, 5.0), (7.0, 10.0)),
((7.0, 10.0), (3.0, 10.0)), ((3.0, 10.0), (0.0, 5.0)), ((0.0, 5.0), (3.0, 0.0)))))
val figures = Array(Figure("Square", ((0.0, 0.0), (10.0, 0.0)),
((10.0, 0.0), (10.0, 10.0)), ((10.0, 10.0), (0.0, 10.0)),
((0.0, 10.0), (0.0, 0.0))),
Figure("Square hole", ((0.0, 0.0), (10.0, 0.0)), ((10.0, 0.0), (10.0, 10.0)),
((10.0, 10.0), (0.0, 10.0)), ((0.0, 10.0), (0.0, 0.0)),
((2.5, 2.5), (7.5, 2.5)), ((7.5, 2.5), (7.5, 7.5)),
((7.5, 7.5), (2.5, 7.5)), ((2.5, 7.5), (2.5, 2.5))),
Figure("Strange", ((0.0, 0.0), (2.5, 2.5)), ((2.5, 2.5), (0.0, 10.0)),
((0.0, 10.0), (2.5, 7.5)), ((2.5, 7.5), (7.5, 7.5)),
((7.5, 7.5), (10.0, 10.0)), ((10.0, 10.0), (10.0, 0.0)),
((10.0, 0), (2.5, 2.5))),
Figure("Exagon", ((3.0, 0.0), (7.0, 0.0)), ((7.0, 0.0), (10.0, 5.0)),
((10.0, 5.0), (7.0, 10.0)), ((7.0, 10.0), (3.0, 10.0)),
((3.0, 10.0), (0.0, 5.0)), ((0.0, 5.0), (3.0, 0.0))))
val points = Seq((5.0, 5.0), (5.0, 8.0), (-10.0, 5.0), (0.0, 5.0), (10.0, 5.0), (8.0, 5.0), (10.0, 10.0))
val points = Array((5.0, 5.0), (5.0, 8.0), (-10.0, 5.0), (0.0, 5.0), (10.0, 5.0), (8.0, 5.0), (10.0, 10.0))
println("points: " + points)
for (f <- figures) {
println("figure: " + f.name)
println(" " + f.edges)
println("result: " + (points map f.contains))
}
figures foreach { f =>
println("Is point inside figure " + f.name + '?')
points foreach { p => println(" " + p + ": " + contains(f, p)) }
println
}
private def raySegI(p: (Double, Double), e: ((Double, Double), (Double, Double))): Boolean = {
val epsilon = 0.00001
if (e._1._2 > e._2._2)
return raySegI(p, (e._2, e._1))
if (p._2 == e._1._2 || p._2 == e._2._2)
return raySegI((p._1, p._2 + epsilon), e)
if (p._2 > e._2._2 || p._2 < e._1._2 || p._1 > max(e._1._1, e._2._1))
return false
if (p._1 < min(e._1._1, e._2._1))
return true
val blue = if (abs(e._1._1 - p._1) > MinValue) (p._2 - e._1._2) / (p._1 - e._1._1) else MaxValue
val red = if (abs(e._1._1 - e._2._1) > MinValue) (e._2._2 - e._1._2) / (e._2._1 - e._1._1) else MaxValue
blue >= red
}
private def contains(f: Figure, p: (Double, Double)) = f.edges.count(raySegI(p, _)) % 2 != 0
private implicit def to_edge(p: ((Double, Double), (Double, Double))): Edge = Edge(p._1, p._2)
}