def __init__(self, polygon: geometry_utils.Polygon, target_size: typing.Optional[float] = None): """Create a new Square. If the points don't form a valid square, raises a WrongShapeError. Args: polygon: The polygon to check. Points will be stored such that the first point stored is the first point in this polygon, but the rest of the polygon may be reversed to clockwise. target_size: If provided, will check against this size when checking side lengths. Otherwise, it will just make sure they are equal. """ if len(polygon) != 4: raise WrongShapeError("Incorrect number of points.") if not geometry_utils.all_approx_square(polygon): raise WrongShapeError("Corners are not square.") side_lengths = geometry_utils.calc_side_lengths(polygon) if not math_utils.all_approx_equal(side_lengths, target_size): raise WrongShapeError( "Side lengths are not equal or too far from target_size.") clockwise = geometry_utils.polygon_to_clockwise(polygon) if clockwise[0] is polygon[0]: self.polygon = clockwise else: self.polygon = list_utils.arrange_index_to_first( clockwise, len(clockwise) - 1) self.unit_length = math_utils.mean(side_lengths)
def __init__(self, polygon: geometry_utils.Polygon): """Create a new LMark. If the points don't form a valid LMark, raises a WrongShapeError.""" if len(polygon) != 6: raise WrongShapeError("Incorrect number of points.") if not geometry_utils.all_approx_square(polygon): raise WrongShapeError("Corners are not square.") clockwise_polygon = geometry_utils.polygon_to_clockwise(polygon) side_lengths = geometry_utils.calc_side_lengths(clockwise_polygon) longest_sides_indexes = list_utils.find_greatest_value_indexes( side_lengths, n=2) if not list_utils.is_adjacent_indexes(side_lengths, *longest_sides_indexes): raise WrongShapeError("Longest sides are not adjacent.") # The longest sides should be about twice the length of the other sides unit_lengths = math_utils.divide_some(side_lengths, longest_sides_indexes, 2) if not math_utils.all_approx_equal(unit_lengths): raise WrongShapeError( "Longest sides are not twice the length of the other sides.") self.polygon = list_utils.arrange_index_to_first( clockwise_polygon, list_utils.determine_which_is_next(polygon, *longest_sides_indexes)) self.unit_length = math_utils.mean(unit_lengths)
def score_results(results: data_exporting.OutputSheet, answer_keys: data_exporting.OutputSheet, num_questions: int) -> data_exporting.OutputSheet: answers = results.data keys = establish_key_dict(answer_keys) form_code_column_name = data_exporting.COLUMN_NAMES[ grid_info.Field.TEST_FORM_CODE] form_code_index = list_utils.find_index(answers[0], form_code_column_name) answers_start_index = list_utils.find_index( answers[0][form_code_index + 1:], "Q1") + form_code_index + 1 virtual_fields: tp.List[grid_info.RealOrVirtualField] = [ grid_info.VirtualField.SCORE, grid_info.VirtualField.POINTS ] columns = results.field_columns + virtual_fields scored_results = data_exporting.OutputSheet(columns, num_questions) for exam in answers[1:]: # Skip header row fields = { k: v for k, v in zip(results.field_columns, exam[:answers_start_index]) } form_code = exam[form_code_index] try: if "*" in keys: key = keys["*"] else: key = keys[form_code] except KeyError: fields[grid_info.VirtualField. SCORE] = data_exporting.KEY_NOT_FOUND_MESSAGE fields[grid_info.VirtualField. POINTS] = data_exporting.KEY_NOT_FOUND_MESSAGE scored_answers = [] else: scored_answers = [ int(actual == correct) for actual, correct in zip(exam[answers_start_index:], key) ] fields[grid_info.VirtualField.SCORE] = str( round(math_utils.mean(scored_answers) * 100, 2)) fields[grid_info.VirtualField.POINTS] = str(sum(scored_answers)) string_scored_answers = [str(s) for s in scored_answers] scored_results.add(fields, string_scored_answers) return scored_results
def guess_centroid(quadrilateral: Polygon) -> Point: """Guesses an approximate centroid. Works well for squares.""" xs = [p.x for p in quadrilateral] ys = [p.y for p in quadrilateral] return Point(math_utils.mean([max(xs), min(xs)]), math_utils.mean([max(ys), min(ys)]))