def test_addi_hole(): """Check all normal cases are correctly handled.""" o = addi_hole.sub_object(build_data=[10, 2], nb_source='complements_to_10') assert o.result == wrap_nb('10') o = addi_hole.sub_object(build_data=[21, 21]) assert o.transduration == 16 o = addi_hole.sub_object(build_data=[5, 7], hidden=1) assert '?' in o.nb1.printed o = addi_hole.sub_object(build_data=[5, 7], hidden=2) assert '?' in o.nb2.printed o = addi_hole.sub_object(build_data=[10, 20], nb_variant='decimal1') assert ((is_number(o.nb1) and not is_integer(o.nb1)) or (is_number(o.nb2) and not is_integer(o.nb2)) or (is_number(o.result_nb) and not is_integer(o.result_nb)))
def tikz_attributes(self, radius_coeff=1, do_label=True): if not is_number(radius_coeff): raise TypeError('radius_coeff must be a number, found {} instead.' .format(type(radius_coeff))) attributes = [] if do_label and self.label not in [None, 'default']: required.tikz_library['quotes'] = True attributes.append('"{}"'.format(self.label)) if self.eccentricity is not None: attributes.append('angle eccentricity={}' .format(self.eccentricity)) if self.variety is not None: if self.do_draw: attributes.append('draw') if self.arrow_tips is not None: attributes.append(self.arrow_tips) if self.thickness is not None: attributes.append(self.thickness) if self.radius is not None: attributes.append('angle radius = {}' .format((self.radius * radius_coeff) .rounded(Number('0.01')).uiprinted)) if self.hatchmark is not None: attributes.append(self.hatchmark) required.tikz_library['decorations.markings'] = True required.tikzset[self.hatchmark + '_hatchmark'] = True if (self.variety is not None or (do_label and self.label not in [None, 'default'])): if self.color is not None: attributes.append(self.color) return '[{}]'.format(', '.join(attributes))
def gap(self, value): if not (is_number(value) or value is None): raise TypeError('The gap value must be None or a number. ' 'Found {} instead (type: {}).' .format(repr(value), type(value))) if value is None: self._gap = None else: self._gap = Number(value)
def radius(self, value): if is_number(value): self._radius = Number(value) elif value is None: self._radius = None else: raise TypeError('Expected a number as radius. Got {} instead.' .format(str(type(value)))) if hasattr(self, '_eccentricity') and hasattr(self, '_gap'): self.eccentricity = 'automatic'
def eccentricity(self, value): if value == 'automatic': if self.gap is None: raise ValueError('Cannot calculate the eccentricity if gap ' 'is None.') value = (self.gap / self.radius + 1)\ .rounded(Number('0.01')).standardized() if not (value is None or is_number(value)): raise TypeError('The eccentricity of an AngleDecoration must be ' 'None or a Number. Found {} instead.' .format(type(value))) self._eccentricity = value
def DEFAULT_POSITION_PRECISION(self, value): if not is_number(value): raise TypeError('DEFAULT_POSITION_PRECISION must be a number, ' 'found {} instead.'.format(type(value))) self._DEFAULT_POSITION_PRECISION = value
def RATIO(self, value): if not is_number(value): raise TypeError('RATIO must be a number, found {} instead.'.format( repr(value))) self._RATIO = value
def RECEDING_AXIS_ANGLE(self, value): if not is_number(value): raise TypeError('RECEDING_AXIS_ANGLE must be a number, ' 'found {} instead.'.format(repr(value))) self._RECEDING_AXIS_ANGLE = value
def __init__(self, point, vertex, point_or_measure, decoration=None, mark_right=False, second_point_name='auto', label=None, color=None, thickness='thick', armspoints=None, label_vertex=False, draw_vertex=False, label_armspoints=False, draw_armspoints=False, label_endpoints=False, draw_endpoints=False, naming_mode='from_endpoints', decoration2=None): """ :param point: a Point of an arm of the Angle :type point: Point :param vertex: the Angle's vertex :type vertex: Point :param point_or_measure: either a Point of the other arm of the Angle, or the measure of the Angle :type point_or_measure: Point or number :param decoration: the decoration of the Angle :type decoration: None or AngleDecoration :param mark_right: to tell whether to mark the angle as a right angle :type mark_right: bool :param second_point_name: Only used if point_or_measure is a measure, this is the name of the 2d arm's Point. If set to 'auto', then the name of the first Point will be used, concatenated to a '. :type second_point_name: str :param thickness: the Angle's arms' thickness. Available values are TikZ's ones. :type thickness: str :param color: the color of the Angle's arms. :type color: str :param naming_mode: how to build the name. Possible modes are: 'from_endpoints', 'from_armspoints', 'from_vertex'. Note that if no armspoints are defined, then trying to get the Angle.name will raise an error :type naming_mode: str """ self.color = color self.thickness = thickness self.naming_mode = naming_mode self.decoration = decoration self.decoration2 = decoration2 # The label must be set *after* the possible decoration, because it # will actually be handled by self.decoration if (self.decoration is None or self.decoration.label in [None, 'default']): self.label = label else: if label is not None: raise ValueError('The label has been set twice, as Angle\'s ' 'keyword argument ({}) and as its ' 'AngleDecoration\'s keyword argument ({}).' .format(repr(label), repr(self.decoration.label_value))) self.mark_right = mark_right self.label_vertex = label_vertex self.label_endpoints = label_endpoints self.draw_endpoints = draw_endpoints self.label_armspoints = label_armspoints self.draw_armspoints = draw_armspoints self.draw_vertex = draw_vertex if not (isinstance(point, Point) and isinstance(vertex, Point) and (isinstance(point_or_measure, Point) or is_number(point_or_measure))): raise TypeError('Three Points, or two Points and the measure of ' 'the angle are required to build an Angle. ' 'Found instead: {}, {} and {}.' .format(type(point), type(vertex), type(point_or_measure))) self._points = [point, vertex] if isinstance(point_or_measure, Point): self._points.append(point_or_measure) else: self._points.append(point.rotate(vertex, point_or_measure, rename=second_point_name)) if any([p.three_dimensional for p in self._points]): self._three_dimensional = True else: self._three_dimensional = False # Measure of the angle: if self._three_dimensional: u = Vector(self.points[1], self.points[0]) v = Vector(self.points[1], self.points[2]) self._measure = Number(str(degrees(atan2(u.cross(v).length, u.dot(v))))) else: # 2D angles measure p0 = Point(self._points[0].x - self._points[1].x, self._points[0].y - self._points[1].y, None) p2 = Point(self._points[2].x - self._points[1].x, self._points[2].y - self._points[1].y, None) α0 = Number(str(degrees(atan2(p0.y, p0.x)))) α2 = Number(str(degrees(atan2(p2.y, p2.x)))) self._measure = α2 - α0 if self._measure < 0: self._measure += 360 # This is not like the matching Triangle! if shoelace_formula(*self.points) > 0: self.winding = 'clockwise' else: self.winding = 'anticlockwise' arm0 = Bipoint(self._points[1], self._points[0]) arm1 = Bipoint(self._points[1], self._points[2]) self._arms = [arm0, arm1] self.armspoints = armspoints # Only 2D: labels positioning if not self.three_dimensional: # Vertex' label positioning bisector = Vector(self._points[0], self.vertex)\ .bisector(Vector(self._points[2], self.vertex), new_endpoint_name=None) try: self._points[1].label_position = \ tikz_approx_position(bisector.slope360) except ZeroVector: self._points[1].label_position = \ tikz_approx_position( Bipoint(self.vertex, self._points[0].rotate(self.vertex, -90, rename=None) ).slope360) # Endpoints labels positioning direction = 1 if self.winding == 'anticlockwise' else -1 self.endpoints[0].label_position = \ tikz_approx_position(arm0.slope360 - direction * 55) self.endpoints[1].label_position = \ tikz_approx_position(arm1.slope360 + direction * 55)