Files @ f1f8a2421f92
Branch filter:

Location: OneEye/src/analyzer/corners.py

Laman
updated readme
import logging

from .epoint import EPoint

log=logging.getLogger(__name__)


class Corners:
	def __init__(self,cornerList=[]):
		self._corners=list(cornerList)
		self._is_canon=False
		self._canonizeOrder()

	## 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
		self._canonizeOrder()

	## 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):
		def false():
			self._is_canon=False
			return False

		if len(self._corners)!=4: return false()

		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

		self._is_canon=True # success
		return True

	def scale(self,scale):
		self._corners=[c * scale for c in self._corners]

	def __iter__(self):
		return iter(self._corners)

	def __len__(self):
		return len(self._corners)

	def is_canon(self):
		return self._is_canon


## 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)