def CalcScale(self, points, h, v, hittest=False): """Returns the proportion by which the xform needs to be scaled to make the hypot pass through the point. If hittest is set to true, this func doubles as a hittest, and checks if the point is inside the line's hitbox.""" xf = Xform(points=points) a,d,b,e,c,f = xf.coefs # Get angle of the hypothenuse angle = polar(((b-a), (e-d)))[1] # create a rotated triangle and (c,f)->(h,v) vector. This way, the # hypothenuse is guaranteed to be horizontal, which makes everything # easier. xf.rotate(-angle) l, theta = polar(((h-c), (v-f))) width,height = rect((l, theta - angle)) # return the result. # Note that xf.d and xf.e are guaranteed to be equal. if hittest: return xf.a < width < xf.b and \ abs(height - xf.d) < self.circle_radius return height / xf.d
def VertexHitTest(self, x, y): a,d,b,e,c,f = self.coefs if polar((x - c, y - f))[0] < self.parent.circle_radius: return 'o' elif polar((x - a - c, y - d - f))[0] < self.parent.circle_radius: return 'x' elif polar((x - b - c, y - e - f))[0] < self.parent.circle_radius: return 'y'
def VertexHitTest(self, mousepos): """Checks if the given point is on top of a vertex.""" for xform in self.IterXforms(): x, y, o = xform.points if polar(mousepos - o)[0] < self.circle_radius: cb = (partial(setattr, xform, "pos") if config["Lock-Axes"] else partial(setattr, xform, "o")) return o, xform, cb elif polar(mousepos - x)[0] < self.circle_radius: return x, xform, partial(setattr, xform, "x") elif polar(mousepos - y)[0] < self.circle_radius: return y, xform, partial(setattr, xform, "y") return None, None, None
def VertexHitTest(self,x,y): """Checks if the given point is on top of a vertex.""" for xform in self.IterXforms(): xf = xform if not config['Edit-Post-Xform'] else xform.post a,d,b,e,c,f = xf.coefs if polar((x - c, y - f))[0] < self.circle_radius: return (xform, partial(setattr, xf, "pos") if config["Lock-Axes"] else partial(setattr, xf, "o")) elif polar((x - a - c, y - d - f))[0] < self.circle_radius: return xform, partial(setattr, xf, "x") elif polar((x - b - c, y - e - f))[0] < self.circle_radius: return xform, partial(setattr, xf, "y") return None, None
def VertexHitTest(self,x,y): """Checks if the given point is on top of a vertex.""" for xform in self.IterXforms(): a,d,b,e,c,f = xform.coefs if polar((x - c, y - f))[0] < self.circle_radius: cb = (partial(setattr, xform, "pos") if config["Lock-Axes"] else partial(setattr, xform, "o")) return xform.o, xform, cb elif polar((x - a - c, y - d - f))[0] < self.circle_radius: return xform.x, xform, partial(setattr, xform, "x") elif polar((x - b - c, y - e - f))[0] < self.circle_radius: return xform.y, xform, partial(setattr, xform, "y") return None, None, None
def SideHitTest(self, mousepos): """Checks if the given point is near one of the triangle sides or corners.""" for xform in self.IterXforms(): xf = xform # TODO:refactor x, y, o = xf.points for points, func in (((x, y, o), 'scale'), ((x, o, y), 'rotate_x'), ((y, o, x), 'rotate_y')): if self.CalcScale(points, mousepos, hittest=True): return (points[:2], xform, self.side_helper(xf, func, mousepos)) # TODO: detect the actual lines. Right now, it just checks a radius # from the middle point. radius = self.circle_radius * 3 # better too big than too small. for i, j, k in (self._cornerpoints): if polar(mousepos - j)[0] < radius: if config["Edit-Post-Xform"]: xform = self.parent.ActiveXform.post else: xform = self.parent.ActiveXform return (i, j, k), xform, self.side_helper(xform, 'rotate', mousepos) return None, None, None
def side_helper(self, xform, funcname, mousepos): """Takes the result of SideHitTest and builds a proper callback.""" if funcname == 'scale': def cb(pos): return xform.scale(self.CalcScale(xform.points, pos)) return cb if funcname == "rotate": pivot = xform.o func = partial(xform.rotate, pivot=pivot) elif config["Lock-Axes"]: pivot = (0, 0) if config["World-Pivot"] else xform.o func = partial(xform.rotate, pivot=pivot) else: pivot = xform.o func = getattr(xform, funcname) def cb(pos): angle = polar(pos - pivot)[1] func(angle - cb.prev_angle) cb.prev_angle = angle cb.prev_angle = polar(mousepos - pivot)[1] return cb
def SideHitTest(self, h, v): """Checks if the given point is near one of the triangle sides or corners.""" for xform in self.IterXforms(): xf = xform # TODO:refactor x,y,o = xf.points for points,func in (((x,y,o), 'scale'), ((x,o,y), 'rotate_x'), ((y,o,x), 'rotate_y')): if self.CalcScale(points, h, v, hittest=True): return (points[:2], xform, self.side_helper(xf, func, h,v)) # TODO: detect the actual lines. Right now, it just checks a radius # from the middle point. radius = self.circle_radius * 3 # better too big than too small. for i,j,k in (self._cornerpoints): if polar((h - j[0], v - j[1]))[0] < radius: if config["Edit-Post-Xform"]: xform = self.parent.ActiveXform.post else: xform = self.parent.ActiveXform return ((i,j,k), xform, self.side_helper(xform, 'rotate', h,v)) return None, None, None
def CalcScale(self, points, mousepos, hittest=False): """Returns the proportion by which the xform needs to be scaled to make the hypot pass through the point. If hittest is set to true, this func doubles as a hittest, and checks if the point is inside the line's hitbox.""" x, y, o = points # The idea is simple. Recenter the system around x, and rotate it so # the side under test runs along the x-axis. l, theta = polar(y - x) angle = N.array((0, theta)) width, height = rect(polar(mousepos - x) - angle) # return the result. if hittest: return 0 < width < l and abs(height) < self.circle_radius else: ref_height = rect(polar(o - x) - angle)[1] return (ref_height - height) / ref_height
def angle_helper(*points): """Given 3 vectors with the same origin, checks if the first falls between the other 2.""" itr = (polar(i)[1] for i in points) vect = itr.next() # vector being checked low, high = sorted(itr) # the 2 triangle legs. if high - low > 180: low, high = high - 360, low if vect > high: vect -= 360 return high > vect > low
def angle_helper(*points): """Given 3 vectors with the same origin, checks if the first falls between the other 2.""" itr = (polar(i)[1] for i in points) vect = itr.next() # vector being checked low, high = sorted(itr) # the 2 triangle legs. if high - low > 180: low, high = high-360, low if vect > high: vect -= 360 return high > vect > low
def side_helper(self, xform, funcname, h, v): """Takes the result of SideHitTest and builds a proper callback.""" if funcname == 'scale': def cb((h,v)): return xform.scale(self.CalcScale(xform.points, h, v)) return cb if funcname == "rotate" or config["Lock-Axes"]: pivot = (0,0) if config["World-Pivot"] else xform.o func = partial(xform.rotate, pivot=pivot) else: pivot = xform.o func = getattr(xform, funcname) def cb((h, v)): angle = polar((h - pivot[0], v - pivot[1]))[1] func(angle - cb.prev_angle) cb.prev_angle = angle cb.prev_angle = polar((h - pivot[0], v - pivot[1]))[1] return cb
def cb(pos): angle = polar(pos - pivot)[1] func(angle - cb.prev_angle) cb.prev_angle = angle
def cb((h, v)): angle = polar((h - pivot[0], v - pivot[1]))[1] func(angle - cb.prev_angle) cb.prev_angle = angle