def trimSides(self, newWidth): # type: (int) -> Image widthToCutOutLeft = int((self.width() - newWidth) / 2) areaToCut = Box(Point(widthToCutOutLeft, 0), Point(widthToCutOutLeft + newWidth, self.height())) return self.subImage(areaToCut)
def scan_from_last(board, last_points, player): for point in last_points: x, y = point # check 8 directions and start backtracking. for dir in DIRECTIONS: dx, dy = dir nx, ny = x + dx, y + dy if is_outta_range(nx, ny): continue if board[ny][nx] == board[y][x]: debug.log('Direction {}'.format(dir)) debug.log('Start at {}'.format(Point(x, y))) # to check properly, go to the end of direction while board[ny][nx] == board[y][x]: nx += dx ny += dy if is_outta_range(nx, ny): break dx, dy = reverse_of(dir) debug.log('End of direction : {}'.format(Point(nx, ny))) is_end = track(board, nx, ny, reverse_of(dir)) if is_end: # returns player who won. return board[ny][nx] debug.stop()
def __init__(self, point, keycap=None, offset=None, shift=None, rotation_center=None): # vertex location in 1x keycap_size units if isinstance(point, list): self.point = Point(*point) else: self.point = copy.deepcopy(point) # vertex offset from location in mm if not offset: self.offset = Point(0, 0) elif isinstance(offset, list): self.offset = Point(*offset) else: self.offset = copy.deepcopy(offset) # keycap size in 1x keycap_size units self.keycap = keycap or [1, 1] if not shift: self.shift = None elif isinstance(shift, list): self.shift = Point(*shift) else: self.shift = copy.deepcopy(shift) if not rotation_center: self.rotation_center = None elif isinstance(rotation_center, list): self.rotation_center = Point(*rotation_center) else: self.rotation_center = copy.deepcopy(rotation_center)
def __findSubImage(self, image, subImage): if subImage is None: return None # Algorithm is described here: https: // www.geeksforgeeks.org / template - matching - using - opencv - in -python / # Convert image and subImage to grayscale img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) subImage_gray = cv2.cvtColor(subImage, cv2.COLOR_BGR2GRAY) # Perform match operations. res = cv2.matchTemplate(img_gray, subImage_gray, cv2.TM_CCOEFF_NORMED) #determine which rechtangle on the image is the best fit for subImage (has the highest correlation) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) self.__correlation = max_val if max_val < self.__threshold_for_matching: # If the best matching box still has correlation below the "threshold" then declare defeat -> we could not find a match for subImage on this image return None # get w and h, so that we can reconstruct the box d, w, h = subImage.shape[::-1] topLeft = Point(max_loc[0], max_loc[1]) Point(topLeft.x + w, topLeft.y + h) return topLeft
def __boundingBoxAroundContour(self, contour): Xmin = int(np.min(contour[:, 1])) Xmax = int(np.max(contour[:, 1])) Ymin = int(np.min(contour[:, 0])) Ymax = int(np.max(contour[:, 0])) box = Box(Point(Xmin, Ymin), Point(Xmax, Ymax)) return box
def determine(self) -> int: """ Determine who won. :return: player number who won. None if there's no winner (game isn't finished). """ board = self.board x, y = self.last_move # check 8 directions and start backtracking. for dir_func in DIRECTIONS: nx, ny = dir_func(x, y) if is_outta_range(nx, ny): continue if board[ny][nx] == board[y][x]: debug.log('Direction : ' + repr_direction(dir_func)) debug.log('Start at {}'.format(Point(x, y))) # to check properly, go to the end of direction while board[ny][nx] == board[y][x]: nx, ny = dir_func(nx, ny) if is_outta_range(nx, ny): break reverse_dir_func = reverse_of(dir_func) nx, ny = reverse_dir_func(nx, ny) # one step back. debug.log('End of direction : {}'.format(Point(nx, ny))) is_end = self._track(nx, ny, reverse_dir_func) if is_end: # returns player who won. return board[ny][nx] debug.stop()
def parse( log_pathname, label_pathname, tp_pathname=None, fp_pathname=None, fn_pathname=None, threshold=squirrel_weight, loss_method=loss): log_dict = defaultdict(list) label_dict = defaultdict(list) tp_count, fp_count, fn_count = 0, 0, 0 log_file = log_pathname.open() label_file = label_pathname.open() tp_file = _unwrap_or_tempfile(tp_pathname, 'w') fp_file = _unwrap_or_tempfile(fp_pathname, 'w') fn_file = _unwrap_or_tempfile(fn_pathname, 'w') try: for line in log_file: frame_filename_str, points_strs = line.rstrip('\n').split(' ', 1) log_lane = [ Point(point_str) for point_str in points_strs.split()] if all([point != zero_point for point in log_lane]): log_dict[frame_filename_str].append(log_lane) for line in label_file: frame_filename_str, points_strs = line.rstrip('\n').split(' ', 1) label_lane = [ Point(point_str) for point_str in points_strs.split()] label_dict[frame_filename_str].append(label_lane) try: label_dict = OrderedDict(sorted( label_dict.items(), key=lambda key: int(key[0].split('.')[0]))) except ValueError: pass for frame_filename_str in label_dict: tp, fp, fn, losses = _compare( log_dict[frame_filename_str], label_dict[frame_filename_str], threshold, loss_method) logging.info('{}: {}'.format(frame_filename_str, losses)) _log_pos_samples(tp_file, frame_filename_str, tp) _log_pos_samples(fp_file, frame_filename_str, fp) _log_pos_samples(fn_file, frame_filename_str, fn) tp_count += len(tp) fp_count += len(fp) fn_count += len(fn) finally: log_file.close() label_file.close() tp_file.close() fp_file.close() fn_file.close() return tp_count, fp_count, fn_count
def recognize(self, input_img): # Consider ROI only input_roi_img = input_img[self._roi.y:self._roi.y + self._roi.h, self._roi.x:self._roi.x + self._roi.w, :] # For each of R, G, B star_h, star_w, _ = self._star_img.shape scores = np.zeros((self._roi.h - star_h + 1, self._roi.w - star_w + 1)) for channel in range(3): result = cv2.matchTemplate(input_roi_img[:, :, channel], self._star_img[:, :, channel], self._method, mask=self._star_mask_img[:, :, channel]) scores = np.add(scores, np.square(result)) # Filter by the threshold, take x-axis only xys = np.where(scores <= self._threshold) ys = xys[0] xs = xys[1] # Prevent count a star twice xs.sort() last_x = -999999 star_groups = [] for x in xs: if x - last_x >= self._min_interval: last_x = x star_groups.append([]) star_groups[-1].append(x) if len(star_groups) == 0 or len(star_groups) > 8: return (0, Point()) best_offset = Point() best_score = 0 for candidate in star_groups[0]: dx = candidate - self._precise_star_xs[len(star_groups) - 1][0] score = 0 for g in range(len(star_groups)): for x in star_groups[g]: if x - self._precise_star_xs[len(star_groups) - 1][g] == dx: score += 1 if score > best_score: best_score = score best_offset.x = dx ys = ys.tolist() best_offset.y = max(set(ys), key=ys.count) - self._precise_star_y return (len(star_groups), best_offset)
class NavigationSystem: ship: Ship start: Point = Point(0, 0) current: Point = Point(0, 0) def __post_init__(self): self.current = self.start def update(self, instruction): if instruction.type == "move": self.ship.move(instruction.code, instruction.value) elif instruction.type == "rotate": self.ship.rotate(instruction.code, instruction.value) def distance_to_start(self): return self.ship.position.manhattan(self.start)
def __init__(self, x, y, hp, speed, shoot_interval): self.pos = Point(x, y) self.hp = hp self.speed = speed self.shoot_interval = shoot_interval self.frame_count = 0 self.is_alive = True
def getplotvals(self, datafile=None): # How many y and x values will we need? ## The plot width - needs to be stored as property for the plot function to work. self.plot_width = self.bottomrightpoint.longitude - self.upperleftpoint.longitude ## The plot height - needs to be stored as a property for the plot function to work. self.plot_height = self.upperleftpoint.latitude - self.bottomrightpoint.latitude ## The number of x points we retrieved. Stored as a property for the plot function to work. if (self.xsteps): self.num_x = int(self.xsteps) else: self.num_x = int(math.ceil(self.plot_width / self.spacing)) + 1 ## The number of y points we retrieved. Stored as a property for the plot function to work. if (self.ysteps): self.num_y = int(self.ysteps) else: self.num_y = int(math.ceil(self.plot_height / self.spacing)) + 1 ## The 2D array of retrieved material properties self.materialproperties = [[ MaterialProperties(-1, -1, -1) for x in xrange(self.num_x) ] for x in xrange(self.num_y)] u = UCVM() # Generate a list of points to pass to UCVM. ucvmpoints = [] for y in xrange(0, self.num_y): for x in xrange(0, self.num_x): ucvmpoints.append(Point(self.upperleftpoint.longitude + x * self.spacing, \ self.bottomrightpoint.latitude + y * self.spacing, \ self.upperleftpoint.depth)) self.ucvm_query_results = u.map_grid(ucvmpoints, self.cvm)
def __wasClicked(self, event, x, y): # check to see if the left mouse button was released #print ("__wasClicked",event) if event == cv2.EVENT_LBUTTONDOWN: self.featureCoordiate = Point(x, y) self.__mouseButtomWasClicked = True # just pretend a key button 'a' was pressed, so that cv2 framework returns from cv2.waitKeyEx() function press(chr(self.KEY_MOUSE_CLICK_EVENT)) if event == cv2.EVENT_RBUTTONDOWN: self.featureCoordiate = Point(x, y) self.__mouseButtomWasClicked = True # just pretend a key button 'a' was pressed, so that cv2 framework returns from cv2.waitKeyEx() function press(chr(self.KEY_RIGHT_MOUSE_CLICK_EVENT))
def execute(wire_1, wire_2): start_point = Point(0, 0) path_1 = process_wire(wire_1) path_2 = process_wire(wire_2) common_points = (set(path_1) & set(path_2)) - set([start_point]) return min([p.manhattan_distance(start_point) for p in common_points])
def __init__(self, parent: 'SquareRegion' = None, subregion_number: int = None, top: int = None, right: int = None, bottom: int = None, left: int = None, depth=None): if parent is None: self.parent = None self.top = top self.bottom = bottom self.left = left self.right = right self.remaining_depth = depth else: self.parent = parent self.remaining_depth = parent.remaining_depth - 1 self._update_coords(subregion_number) self.middle = Point(ceil((self.left + self.right) / 2), ceil((self.top + self.bottom) / 2)) if parent is not None: self._update_coords(subregion_number) if self.remaining_depth > 0: self.subregions = (SquareRegion(parent=self, subregion_number=index) for index in range(4))
def __boxAroundFeatureForFrame(self, frameID): topLeftPoint = self.__getTopLeftForFrame(frameID) box = Box( topLeftPoint, Point(topLeftPoint.x + self.__startingBox.width(), topLeftPoint.y + self.__startingBox.hight())) return box
def get_move(self, snake, world): moves = [] head = snake.head() w, h = world.shape if head.x > 1: moves.append(Point(-1, 0)) if head.x < w - 1: moves.append(Point(1, 0)) if head.y > 1: moves.append(Point(0, -1)) if head.y < h - 1: moves.append(Point(0, 1)) next_seg = snake.pieces[1] - head if next_seg in moves: moves.remove(next_seg) return random.choice(moves)
def getColorPoints(self): color2points = {pb.Color.BLACK: set(), pb.Color.WHITE: set()} liveStones, _ = parseStones(self.getStones()) nowStones = [ stone for stone in liveStones if stone not in self.willDeadStones ] for stone in nowStones: t1 = self.selectPoints( self.getPoints(nowStones, stone, True, False)) t2 = self.selectPoints(self.getPoints(nowStones, stone, True, True)) t3 = self.selectPoints( self.getPoints(nowStones, stone, False, False)) t4 = self.selectPoints( self.getPoints(nowStones, stone, False, True)) for x, y, hasStone, color in itertools.chain(t1, t2, t3, t4): color2points[stone.color].add(Point(x, y)) print(self.colorPointsStr(color2points)) return color2points
def rotateWaypoint(refPoint: com.Point, angleDegs: SupportsFloat, point: com.Point): # flip the angle so that it rotates clockwise instead of ccw angleDegs *= -1 sine = sin(radians(angleDegs)) cosine = cos(radians(angleDegs)) # translate point back to refPoint point.move(-refPoint.x, -refPoint.y) # rotate point newX = point.x * cosine - point.y * sine newY = point.x * sine + point.y * cosine # translate point back point.moveTo(int(round(newX + refPoint.x)), int(round(newY + refPoint.y)))
def getplotvals(self, mproperty="vs"): # How many y and x values will we need? ## The plot width - needs to be stored as property for the plot function to work. self.plot_width = self.bottomrightpoint.longitude - self.upperleftpoint.longitude ## The plot height - needs to be stored as a property for the plot function to work. self.plot_height = self.upperleftpoint.latitude - self.bottomrightpoint.latitude ## The number of x points we retrieved. Stored as a property for the plot function to work. if (self.xsteps is not None): self.num_x = int(self.xsteps) else: self.num_x = int(math.ceil(self.plot_width / self.spacing)) + 1 ## The number of y points we retrieved. Stored as a property for the plot function to work. if (self.ysteps is not None): self.num_y = int(self.ysteps) else: self.num_y = int(math.ceil(self.plot_height / self.spacing)) + 1 ## Maximum depth encountered. self.max_val = 0 ## Minimum depth (always 0). self.min_val = 0 ## The 2D array of retrieved Vs30 values. self.materialproperties = [[ MaterialProperties(-1, -1, -1) for x in xrange(self.num_x) ] for x in xrange(self.num_y)] u = UCVM(install_dir=self.installdir, config_file=self.configfile) ### MEI if (self.datafile != None): # print "\nUsing --> "+datafile data = u.import_binary(self.datafile, self.num_x, self.num_y) # print "Total points imported is ", len(data), "for ", self.num_x, " and ", self.num_y else: # Generate a list of points to pass to UCVM. ucvmpoints = [] for y in xrange(0, self.num_y): for x in xrange(0, self.num_x): ucvmpoints.append(Point(self.upperleftpoint.longitude + x * self.spacing, \ self.bottomrightpoint.latitude + y * self.spacing, \ self.upperleftpoint.depth)) # print "Total points extracted is ", len(ucvmpoints), "for ", self.num_x, " and ", self.num_y data = u.basin_depth(ucvmpoints, self.cvm, self.vs_threshold) i = 0 j = 0 for matprop in data: self.materialproperties[i][j].vs = matprop if matprop > self.max_val: self.max_val = matprop j = j + 1 if j >= self.num_x: j = 0 i = i + 1
def execute(wire_1, wire_2): start_point = Point(0, 0) path_1 = process_wire(wire_1) path_2 = process_wire(wire_2) common_points = (set(path_1) & set(path_2)) - set([start_point]) return min([path_1.index(p) + path_2.index(p) for p in common_points])
def get_position(self, move_origin=True): if self.shift: x, y = switch_center([self.point.x, self.point.y], [1, 1]) else: x, y = switch_center([self.point.x, self.point.y], [self.keycap[0], self.keycap[1]]) p = Point(x, y, orientation=self.point.orientation) if not self.shift: p += self.offset p.y = -p.y # reverse also orientation? if move_origin: origin = self.keyboard.origin else: origin = Point(0, 0) if move_origin: p = p + origin if self.shift: rotation_center = origin + self.rotation_center.mirror_y() v = p - rotation_center angle_r = math.atan2(v.y, v.x) angle = math.degrees(angle_r) xxx = (self.keycap[1] - 1) * (KEYCAP_SIZE + 1) / 2 row_shift = self.shift.y * (KEYCAP_SIZE + 1) row_shift += xxx p = p.translate2(angle, row_shift) p.orientation += angle + 90 w = (KEYCAP_SIZE + 1) / 2 + 1 h = math.hypot(v.x, v.y) / 2 a = math.degrees(math.atan2(w, h)) * 1.05 a = a * self.shift.x p = p.rotate(a, rotation_center) # diode and wire placement vector = self.offset.mirror_y() angle_r = math.radians(p.orientation) x = vector.x * math.cos(angle_r) - vector.y * math.sin(angle_r) y = vector.y * math.cos(angle_r) + vector.x * math.sin(angle_r) p.x += x p.y += y if "left" in self.section: p.x -= self.keyboard.separator p = p.rotate(+self.keyboard.angle, origin) else: p.x += self.keyboard.separator p = p.rotate(-self.keyboard.angle, origin) return p
def __init__(self, length=3, start_pos: Point = None, world: np.array = None): if start_pos is None: start_pos = Point(10, 10) self.pieces = deque([start_pos] * length) self.world = world self.health = 100 world[start_pos.x, start_pos.y] += length self.alive = True
def __init__(self, centerPt=Point(0, 0, 'center'), dimension=1, max_points=1, max_depth=4): self.max_points = max_points self.max_depth = max_depth self.root = TreeNode(centerPt, dimension, max_points, max_depth, 0)
def count_trees(lines, slope): pos = Point(0, 0) total = 0 while pos.y < len(lines): if lines[pos.y][pos.x % len(lines[pos.y])] == '#': total += 1 pos += slope return total
def __updateRedDotsSearchArea(self, boxAroundRedDots): dotsShift = int(self.__distanceBetweenRedPoints() / 2) bottomRightLimit_x = self.__getImage().width() - 200 bottomRightLimit_y = self.__getImage().height() - 200 topLeftX = min(max(boxAroundRedDots.topLeft.x - dotsShift, 1), bottomRightLimit_x - 100) topLeftY = min(max(boxAroundRedDots.topLeft.y - dotsShift, 1), bottomRightLimit_y - 100) bottomRightX = min(boxAroundRedDots.bottomRight.x + dotsShift, bottomRightLimit_x) bottomRightY = min(boxAroundRedDots.bottomRight.y + dotsShift, bottomRightLimit_y) redDotsSearchArea = Box(Point(topLeftX, topLeftY), Point(bottomRightX, bottomRightY)) return redDotsSearchArea
def getplotvals(self, mproperty="vs"): # How many y and x values will we need? ## The plot width - needs to be stored as property for the plot function to work. self.plot_width = self.bottomrightpoint.longitude - self.upperleftpoint.longitude ## The plot height - needs to be stored as a property for the plot function to work. self.plot_height = self.upperleftpoint.latitude - self.bottomrightpoint.latitude ## The number of x points we retrieved. Stored as a property for the plot function to work. if (self.xsteps): self.num_x = int(self.xsteps) else: self.num_x = int(math.ceil(self.plot_width / self.spacing)) + 1 ## The number of y points we retrieved. Stored as a property for the plot function to work. if (self.ysteps): self.num_y = int(self.ysteps) else: self.num_y = int(math.ceil(self.plot_height / self.spacing)) + 1 ## The 2D array of retrieved material properties. self.materialproperties = [[ MaterialProperties(-1, -1, -1) for x in xrange(self.num_x) ] for x in xrange(self.num_y)] u = UCVM(install_dir=self.installdir, config_file=self.configfile) ### MEI if (self.datafile != None): data = u.import_binary(self.datafile, self.num_x, self.num_y) print "\nUsing --> " + self.datafile print "expecting x ", self.num_x, " y ", self.num_y else: # Generate a list of points to pass to UCVM. ucvmpoints = [] for y in xrange(0, self.num_y): for x in xrange(0, self.num_x): ucvmpoints.append(Point(self.upperleftpoint.longitude + x * self.spacing, \ self.bottomrightpoint.latitude + y * self.spacing, \ self.upperleftpoint.depth)) data = u.query(ucvmpoints, self.cvm) i = 0 j = 0 isfloat = 0 if (self.datafile != None): isfloat = 1 for matprop in data: if isfloat: self.materialproperties[i][j].setProperty(mproperty, matprop) else: self.materialproperties[i][j] = matprop j = j + 1 if j >= self.num_x: j = 0 i = i + 1
def expand_search_space(self, p: Point): # expand search space for dx, dy in SEARCH_SPACE_DIRECTIONS: nx, ny = p.x+dx, p.y+dy if self.search_space_mark[ny][nx] == 0: self.search_space.append(Point(nx, ny)) self.search_space_mark[ny][nx] = 1 # Cannot place in already placed area. self.search_space_mark[p.y][p.x] = -1
class Region: def __init__(self, latitude, longitude, radius, id_): self.center = Point(latitude, longitude) self.radius = radius self.id = id_ def __contains__(self, point: 'Point') -> bool: return self.center.distance_to(point) <= self.radius def __repr__(self): return f'Region(id:{self.id}center:{self.center},radius:{self.radius})'
def FindClosestBuilding(self, point): """ Returns a (Point, Building) representing the closest building from point. If no building is present, return ((0, 0), None) """ closest, nearest_b, min_dist = Point(0, 0), None, 1e6 for ul, b in self.buildings: dist = ul.dist(point) if (min_dist > dist): closest = ul nearest_b = b return closest, nearest_b
def _decompose(self, boundary, depth, parent): if depth == self.max_depth: return x, y = boundary.center dm = boundary.dimension / 2 index0 = 4 * parent + NORTH_WEST index1 = 4 * parent + NORTH_EAST index2 = 4 * parent + SOUTH_EAST index3 = 4 * parent + SOUTH_WEST self._quadrants[index0] = (Boundary(Point(x - dm, y + dm), dm), set()) self._quadrants[index1] = (Boundary(Point(x + dm, y + dm), dm), set()) self._quadrants[index2] = (Boundary(Point(x + dm, y - dm), dm), set()) self._quadrants[index3] = (Boundary(Point(x - dm, y - dm), dm), set()) self._decompose(self._quadrants[index0][BOUNDARY], depth + 1, index0) self._decompose(self._quadrants[index1][BOUNDARY], depth + 1, index1) self._decompose(self._quadrants[index2][BOUNDARY], depth + 1, index2) self._decompose(self._quadrants[index3][BOUNDARY], depth + 1, index3)
def QFA_KDE(input_dir, result_dir, divide_deep, granularity): result = open(result_dir, 'a+') result.write('ID,x,y,h\n') lines = open(input_dir) x = [] y = [] points = {} for line in lines: attr = line.split('\n')[0].split(",") if attr[1] != 'x': if attr[0] not in points: points[attr[0]] = [float(attr[1])] points[attr[0]].append(float(attr[2])) x.append(float(attr[1])) y.append(float(attr[2])) import matplotlib.pyplot as plt plt.plot(x, y, 'bo') x = np.array(x) y = np.array(y) x_max = np.max(x) x_min = np.min(x) y_max = np.max(y) y_min = np.min(y) x_center = (x_max - x_min) / 2 + x_min y_center = (y_max - y_min) / 2 + y_min dimension = max((x_max - x_min) / 2, (y_max - y_min) / 2) n = len(x) Npt = pow(n, 1 / 2) qt = DynamicQuadTree(centerPt=Point(x_center, y_center, 'center'), dimension=dimension, max_points=Npt / granularity, max_depth=divide_deep) for pt in points: qt.insert(Point(points[pt][0], points[pt][1], pt)) plot_partitions(qt.root, result, plt) plt.show() lines.close() result.close()
class Vertex(object): def __init__(self, point, keycap=None, offset=None, shift=None, rotation_center=None): # vertex location in 1x keycap_size units if isinstance(point, list): self.point = Point(*point) else: self.point = copy.deepcopy(point) # vertex offset from location in mm if not offset: self.offset = Point(0, 0) elif isinstance(offset, list): self.offset = Point(*offset) else: self.offset = copy.deepcopy(offset) # keycap size in 1x keycap_size units self.keycap = keycap or [1, 1] if not shift: self.shift = None elif isinstance(shift, list): self.shift = Point(*shift) else: self.shift = copy.deepcopy(shift) if not rotation_center: self.rotation_center = None elif isinstance(rotation_center, list): self.rotation_center = Point(*rotation_center) else: self.rotation_center = copy.deepcopy(rotation_center) @classmethod def copy_from(cls, other, **kwargs): instance = cls(point=other.point, keycap=other.keycap[:], offset=other.offset, shift=other.shift, rotation_center=other.rotation_center, **kwargs) instance.keyboard = other.keyboard instance.section = other.section return instance @property def position(self): return self.get_position() def get_position(self, move_origin=True): if self.shift: x, y = switch_center([self.point.x, self.point.y], [1, 1]) else: x, y = switch_center([self.point.x, self.point.y], [self.keycap[0], self.keycap[1]]) p = Point(x, y, orientation=self.point.orientation) if not self.shift: p += self.offset p.y = -p.y # reverse also orientation? if move_origin: origin = self.keyboard.origin else: origin = Point(0, 0) if move_origin: p = p + origin if self.shift: rotation_center = origin + self.rotation_center.mirror_y() v = p - rotation_center angle_r = math.atan2(v.y, v.x) angle = math.degrees(angle_r) xxx = (self.keycap[1] - 1) * (KEYCAP_SIZE + 1) / 2 row_shift = self.shift.y * (KEYCAP_SIZE + 1) row_shift += xxx p = p.translate2(angle, row_shift) p.orientation += angle + 90 w = (KEYCAP_SIZE + 1) / 2 + 1 h = math.hypot(v.x, v.y) / 2 a = math.degrees(math.atan2(w, h)) * 1.05 a = a * self.shift.x p = p.rotate(a, rotation_center) # diode and wire placement vector = self.offset.mirror_y() angle_r = math.radians(p.orientation) x = vector.x * math.cos(angle_r) - vector.y * math.sin(angle_r) y = vector.y * math.cos(angle_r) + vector.x * math.sin(angle_r) p.x += x p.y += y if "left" in self.section: p.x -= self.keyboard.separator p = p.rotate(+self.keyboard.angle, origin) else: p.x += self.keyboard.separator p = p.rotate(-self.keyboard.angle, origin) return p