Files
@ 3798475f45c1
Branch filter:
Location: OneEye/src/analyzer/corners.py - annotation
3798475f45c1
3.5 KiB
text/x-python
refactoring: moving files around
3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 3798475f45c1 | from .epoint import EPoint
class Corners:
def __init__(self):
self.corners=[]
## Adds a new corner if there are less than four, replaces the closest otherwise.
def add(self,x,y):
a=EPoint(x,y)
# for i,c in enumerate(self.corners): # move an improperly placed point
# if a.dist(c)<20:
# self.corners[i]=a
# return
if len(self.corners)<4: # add a new corner
self.corners.append(a)
if len(self.corners)<4:
return
index,minDist=0,float('inf') # replace the corner closest to the clicked point
for i,c in enumerate(self.corners):
if a.dist(c)<minDist:
index,minDist=i,a.dist(c)
self.corners[index]=a
## Order the corners (0,1,2,3) so they make a quadrangle with vertices KLMN in counter-clockwise order, K being in the upper left.
#
# For four points ABCD, there are 24 possible permutations corresponding to the desired KLMN.
# When we relax the condition of K being the upper left one, we get six groups of four equivalent permutations. KLMN ~ LMNK ~ MNKL ~ NKLM.
#
# We determine which of the points' triplets are oriented clockwise and which counter-clockwise (minus/plus in the table below)
# and swap them so that all triangles turn counter-clockwise.
#
# xxxx -> KLMN | ABC | ABD | ACD | BCD | index | swap
# ------------ | :-: | :-: | :-: | :-: | ----: | ----
# A BCD | + | + | + | + | 15 | 0
# A BDC | + | + | - | - | 12 | CD
# A CBD | - | + | + | - | 6 | BC
# A CDB | - | - | + | + | 3 | AB
# A DBC | + | - | - | + | 9 | AD
# A DCB | - | - | - | - | 0 | BD
#
# For every non-degenerate quadrangle, there must be 1-3 edges going right-left (from a higher to a lower x coordinate).
# From these pick the one with the lowest slope (dy/dx) and declare its ending point the upper left corner. For the same slope pick the one further left.
#
# @return True for a convex quadrangle, False for concave and degenerate cases.
def canonizeOrder(self):
if len(self.corners)!=4: return False # erroneus call
a,b,c,d=self.corners
abc=doubleTriangleArea(a,b,c)
abd=doubleTriangleArea(a,b,d)
acd=doubleTriangleArea(a,c,d)
bcd=doubleTriangleArea(b,c,d)
if any(x==0 for x in (abc,abd,acd,bcd)): return False # collinear degenerate
swaps=[(1,3),(0,1),(1,2),(0,3),(2,3),(0,0)]
index=(8 if abc>0 else 0)|(4 if abd>0 else 0)|(2 if acd>0 else 0)|(1 if bcd>0 else 0)
if index%3!=0: return False # concave degenerate
swap=swaps[index//3]
self.corners[swap[0]], self.corners[swap[1]] = self.corners[swap[1]], self.corners[swap[0]] # counter-clockwise order
kIndex=None
lowestSlope=float("inf")
for i,corner in enumerate(self.corners): # find the NK edge: going right-left with the lowest slope, secondarily the one going down
ii=(i+1)%4
slope=abs(getSlope(corner,self.corners[ii]))
if corner.x>self.corners[ii].x and (slope<lowestSlope or (slope==lowestSlope and corner.y<self.corners[ii].y)):
kIndex=ii
lowestSlope=slope
self.corners=self.corners[kIndex:]+self.corners[:kIndex] # rotate the upper left corner to the first place
return True # success
def scale(self,scale):
self.corners=[c*scale for c in self.corners]
## Computes twice the area of the triangle formed by points a,b,c.
#
# @return positive value for points oriented counter-clockwise, negative for clockwise, zero for degenerate cases.
def doubleTriangleArea(a,b,c):
return (a.x-b.x)*(c.y-a.y)-(c.x-a.x)*(a.y-b.y)
def getSlope(a,b):
if(b.x==a.x): return float("inf")
return (b.y-a.y)/(b.x-a.x)
|