Files @ 29f28718a69b
Branch filter:

Location: OneEye/src/analyzer/corners.py - annotation

Laman
transitional data processing
a601f040413e
a601f040413e
a601f040413e
a601f040413e
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
002858d37174
c96bd773ec28
f125391c937d
f125391c937d
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
a601f040413e
a601f040413e
3798475f45c1
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
a601f040413e
f125391c937d
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
f125391c937d
28ca21b89e43
28ca21b89e43
28ca21b89e43
28ca21b89e43
28ca21b89e43
3798475f45c1
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
28ca21b89e43
3798475f45c1
3798475f45c1
3798475f45c1
28ca21b89e43
3798475f45c1
3798475f45c1
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
a601f040413e
3798475f45c1
a601f040413e
a601f040413e
3798475f45c1
3798475f45c1
3798475f45c1
a601f040413e
3798475f45c1
f125391c937d
28ca21b89e43
3798475f45c1
3798475f45c1
a601f040413e
a601f040413e
a601f040413e
a601f040413e
a601f040413e
a601f040413e
a601f040413e
3798475f45c1
f125391c937d
f125391c937d
f125391c937d
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
3798475f45c1
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)