Source code for mathmaker.lib.tools.generators.anglessets

# -*- coding: utf-8 -*-

# Mathmaker creates automatically maths exercises sheets
# with their answers
# Copyright 2006-2017 Nicolas Hainaux <nh.techn@gmail.com>

# This file is part of Mathmaker.

# Mathmaker is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# any later version.

# Mathmaker is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Mathmaker; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

from mathmakerlib.calculus import Number
from mathmakerlib.geometry import Angle, AnglesSet, AngleDecoration, Point

from mathmaker.lib import shared
from mathmaker.lib.tools.generators import Generator

OFFSET = Number('0.6', unit='cm')
LAYER_THICKNESS = Number('0.1', unit='cm')


[docs]class AnglesSetGenerator(Generator):
[docs] def generate(self, codename=None, variant=None, labels=None, name=None, **kwargs): """ :param codename: the distcode, describing the type of anglesset and the measures distribution, grouped by equal measures batches, like 1_1_1, 3_2 etc. The letter 'r' may be suffixed to the distcode, then it concerns a series of variants containing one right angle. :type codename: str :param variant: the variant number :type variant: int :param labels: the labels to set, in the form of a list of couples (nb_of_measures, measure). This list will be checked at start. :type labels: list (of couples) :param name: the series of letters to use to name the AnglesSet's Points :type name: str (or None, then it will be set automatically) """ extra_deco = kwargs.pop('extra_deco', None) extra_deco2 = kwargs.pop('extra_deco2', None) subtr_shapes = kwargs.pop('subtr_shapes', False) thickness = kwargs.get('thickness', None) subvariant_nb = kwargs.get('subvariant_nb', None) self.check_args(distcode=codename, variant=variant, labels=labels, name=name) labels = sorted(labels, key=lambda x: x[0])[::-1] return getattr(self, '_' + codename)(variant=variant, labels=labels, name=name, extra_deco=extra_deco, thickness=thickness, extra_deco2=extra_deco2, subvariant_nb=subvariant_nb, subtr_shapes=subtr_shapes)
def _anglesset(self, shapes_source, subvariants, labels, name=None, extra_deco=None, subvariant_nb=None, thickness=None, rdeco=None, remove_labels=None, labels_dist=None, extra_deco2=None): # if extra_deco is None: # extra_deco = {} if extra_deco2 is None: extra_deco2 = {} if rdeco is None: rdeco = [] for deco_id in rdeco: if deco_id not in extra_deco: extra_deco[deco_id] = \ AngleDecoration(radius=Number('0.4', unit='cm'), label=None) if remove_labels is None: remove_labels = [False for _ in range(len(labels))] hatchmarks = {} if labels_dist is not None: # Then, labels_dist is a values list that tells the order of the # angles' measures (used if some are duplicates) # e.g. [0, 1, 0] means first label value, second, first again # Also, in that case, labels is given "raw", as list of tuples: # [(nb of occurences, value), ...] labels = [labels[k][1] for k in labels_dist][::-1] for k in labels_dist: if labels_dist.count(k) != 1 and k not in hatchmarks: hatchmarks[k] = next(shared.angle_decorations_source)[0:2] lbls = [] for i in range(len(remove_labels)): if remove_labels[i]: lbls.append(None) labels.pop() else: lbls.append(labels.pop()) angles = [] names = name if subvariant_nb is None: subvariant_nb = next(shapes_source)[0] subvariant_nb = int(subvariant_nb) build_data = subvariants[subvariant_nb] # right_angle_radius = build_data.pop('right_angle_radius', None) Ω = build_data.pop('center', Point(0, 0, name=names[0])) # Let an error be raised if no endpoints are defined endpoints = build_data.pop('endpoints') labels = [Number(lbl, unit=r'\textdegree') if lbl is not None else None for lbl in lbls] eccentricities = build_data.pop('eccentricities', ['automatic' for _ in range(len(endpoints) - 1)]) # Setup default decorations of all i, i+1 angles decorations = {} collected_radii = {} layer_nb = 0 for i in range(len(endpoints) - 1): r = OFFSET + layer_nb * LAYER_THICKNESS if labels_dist is not None: if labels_dist[i] in collected_radii: r = collected_radii[labels_dist[i]] else: collected_radii[labels_dist[i]] = r layer_nb += 1 else: layer_nb += 1 ad = AngleDecoration(label=labels[i], radius=r, eccentricity=eccentricities[i]) if labels_dist is not None and labels_dist[i] in hatchmarks: ad.variety = hatchmarks[labels_dist[i]][0] ad.hatchmark = hatchmarks[labels_dist[i]][1] decorations.update({'{}:{}'.format(i, i + 1): ad}) for d in extra_deco: if d in decorations: decorations[d].color = extra_deco[d].color decorations[d].thickness = extra_deco[d].thickness decorations[d].label = extra_deco[d].label decorations[d].radius = extra_deco[d].radius else: decorations.update({d: extra_deco[d]}) for p in endpoints: p.name += '1' for kn, key in enumerate(decorations): i, j = key.split(':') i, j = int(i), int(j) mark_right = True if key in rdeco else False armspoints = [(names[i + 1], ), (names[j + 1], )] θ = Angle(endpoints[i], Ω, endpoints[j], decoration=decorations[key], label_vertex=True, draw_vertex=True, label_armspoints=True, draw_armspoints=True, armspoints=armspoints, mark_right=mark_right, naming_mode='from_armspoints') if key in extra_deco2: θ.decoration2 = extra_deco2[key] angles.append(θ) anglesset = AnglesSet(*angles) anglesset.baseline = build_data.pop('baseline', None) anglesset.boundingbox = build_data.pop('boundingbox', None) return anglesset def _1_1(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant != 0: raise ValueError('variant must be 0 (not \'{}\')'.format(variant)) if extra_deco is None: extra_deco = {} subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point('0.5', '2.45')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '25pt'}, 2: {'endpoints': [Point(2, '1.5'), Point(0, '2.5'), Point('-2.3', 1)], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '25pt'}, 3: {'endpoints': [Point(1, '2.3'), Point('-1.5', 2), Point('-2.5', 0)], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '22pt'}, } shapes_source = shared.anglessets_1_1_source lbls = [labels[i][1] for i in range(len(labels))] return self._anglesset( shapes_source, subvariants, labels=lbls, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, extra_deco2=extra_deco2 ) def _1_1r(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant not in [0, 1]: raise ValueError('variant must be 0 or 1 (not \'{}\')' .format(variant)) if extra_deco is None: extra_deco = {} lbls = [labels[i][1] for i in range(len(labels))] if not subtr_shapes: lbls.remove(90) if subtr_shapes: remove_labels = [False, False] rdeco = ['0:2'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point('1.5', 2), Point(0, '2.5')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '25pt'}, 2: {'endpoints': [Point('2.29', 1), Point('1.25', '2.17'), Point(-1, '2.29')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '24pt'}, 3: {'endpoints': [Point('0.5', '2.45'), Point('-1.8', '1.73'), Point('-2.45', '0.5')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '25pt'}} elif variant == 0: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [True, False] # /!\ order of labels: from right to left, counterclockwise # (reversed order of remove_labels) lbls = lbls + [Number(90)] # Tells which angles will be marked as right rdeco = ['0:1'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(0, '2.5'), Point(-2, '1.5')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '25pt'}, 2: {'endpoints': [Point('1.5', 2), Point(-2, '1.5'), Point('-2.5', 0)], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '20pt'}, 3: {'endpoints': [Point('2.3', 1), Point(-1, '2.3'), Point('-2.45', '0.5')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '23pt'} } elif variant == 1: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, True] # /!\ order of labels: from right to left, counterclockwise # (reversed order of remove_labels) lbls = [Number(90)] + lbls # Tells which angles will be marked as right rdeco = ['1:2'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point('2.3', 1), Point(-1, '2.3')], 'eccentricities': [Number('2.1'), Number('1.6')], 'baseline': '24pt'}, 2: {'endpoints': [Point('2.5', 0), Point(1, '2.3'), Point('-2.3', 1)], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '23pt'}, 3: {'endpoints': [Point(2, '1.5'), Point('0.5', '2.45'), Point('-2.45', '0.5')], 'eccentricities': [Number('1.6'), Number('1.6')], 'baseline': '23pt'} } shapes_source = shared.anglessets_1_1r_source return self._anglesset( shapes_source, subvariants, labels=lbls, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, rdeco=rdeco, remove_labels=remove_labels, extra_deco2=extra_deco2 ) def _2(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant != 0: raise ValueError('variant must be 0 (not \'{}\')'.format(variant)) lbls_dist = [0, 0] remove_labels = [False, True] if extra_deco is None: extra_deco = {} subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point('0.7', '2.4')], 'eccentricities': [Number('1.8'), Number('1.6')], 'baseline': '24pt'}, 2: {'endpoints': [Point('2.5', 0), Point('1.5', 2), Point('-0.7', '2.4')], 'eccentricities': [Number('1.8'), Number('1.6')], 'baseline': '24pt'}, 3: {'endpoints': [Point('1.3', '2.14'), Point(-1, '2.3'), Point('-2.45', '0.5')], 'eccentricities': [Number('1.7'), Number('1.6')], 'baseline': '23pt'}, } shapes_source = shared.anglessets_2_source return self._anglesset( shapes_source, subvariants, labels=labels, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, extra_deco2=extra_deco2, remove_labels=remove_labels, labels_dist=lbls_dist ) def _1_1_1(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant != 0: raise ValueError('variant must be 0 (not \'{}\')'.format(variant)) if extra_deco is None: extra_deco = {} subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point(1, '2.3'), Point(-2, '1.5')], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '22pt'}, 2: {'endpoints': [Point('2.5', 0), Point('1.5', 2), Point(-1, '2.3'), Point('-2.5', '0.25')], 'eccentricities': [Number('1.6'), Number('1.5'), Number('1.4')], 'baseline': '22pt'}, 3: {'endpoints': [Point('1.6', '1.92'), Point(0, '2.5'), Point(-2, '1.5'), Point('-2.5', 0)], 'eccentricities': [Number('1.6'), Number('1.5'), Number('1.4')], 'baseline': '26pt'}, } shapes_source = shared.anglessets_1_1_1_source lbls = [labels[i][1] for i in range(len(labels))] return self._anglesset( shapes_source, subvariants, labels=lbls, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, extra_deco2=extra_deco2 ) def _1_1_1r(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant not in [0, 1, 2]: raise ValueError('variant must be in [0, 1, 2] (found \'{}\')' .format(variant)) if extra_deco is None: extra_deco = {} lbls = [labels[i][1] for i in range(len(labels))] lbls.remove(90) if variant == 0: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [True, False, False] # /!\ order of labels: from right to left, counterclockwise # (reversed order of remove_labels) lbls = lbls + [Number(90)] # Tells which angles will be marked as right rdeco = ['0:1'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(0, '2.5'), Point('-1.5', 2), Point('-2.4', '0.7')], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '26pt'} } elif variant == 1: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, True, False] lbls = [lbls[0], Number(90), lbls[1]] # Tells which angles will be marked as right rdeco = ['1:2'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point('-1.5', 2), Point('-2.4', '0.7')], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '18pt'} } elif variant == 2: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, False, True] lbls = [Number(90)] + lbls # Tells which angles will be marked as right rdeco = ['2:3'] subvariants = {1: {'endpoints': [Point('2.45', '0.5'), Point('1.5', 2), Point(0, '2.5'), Point('-2.5', 0)], 'eccentricities': [Number('1.6'), Number('1.6'), Number('1.5')], 'baseline': '26pt'} } shapes_source = shared.anglessets_1_1_1r_source return self._anglesset( shapes_source, subvariants, labels=lbls, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, rdeco=rdeco, remove_labels=remove_labels, extra_deco2=extra_deco2 ) def _2_1(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant not in [0, 1, 2]: raise ValueError('variant must be in [0, 1, 2] (found \'{}\')' .format(variant)) if extra_deco is None: extra_deco = {} if variant == 0: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, True, False] lbls_dist = [0, 0, 1] subvariants = {1: {'endpoints': [Point('2.5', 0), Point('1.93', '1.59'), Point('0.5', '2.45'), Point('-2.3', 1)], 'eccentricities': [Number('1.7'), Number('1.8'), Number('1.6')], 'baseline': '24pt'} } elif variant == 1: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, False, True] lbls_dist = [0, 1, 0] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point(-1, '2.3'), Point('-2.2', '1.2')], 'eccentricities': [Number('1.7'), Number('1.8'), Number('1.6')], 'baseline': '23pt'} } elif variant == 2: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, False, True] lbls_dist = [1, 0, 0] subvariants = {1: {'endpoints': [Point('2.5', 0), Point('0.6', '2.43'), Point('-1.1', '2.24'), Point('-2.3', 1)], 'eccentricities': [Number('1.7'), Number('1.5'), Number('1.6')], 'baseline': '25pt'} } shapes_source = shared.anglessets_2_1_source return self._anglesset( shapes_source, subvariants, labels=labels, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, extra_deco2=extra_deco2, remove_labels=remove_labels, labels_dist=lbls_dist ) def _2_1r(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant not in [0, 1, 2]: raise ValueError('variant must be in [0, 1, 2] (found \'{}\')' .format(variant)) if extra_deco is None: extra_deco = {} if variant == 0: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [True, True, False] lbls_dist = [1, 0, 0] rdeco = ['0:1'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(0, '2.5'), Point('-1.5', 2), Point('-2.4', '0.7')], 'eccentricities': [Number('1.7'), Number('1.8'), Number('1.5')], 'baseline': '25pt'} } elif variant == 1: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, True, True] lbls_dist = [0, 1, 0] rdeco = ['1:2'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point('-1.5', 2), Point('-2.4', '0.7')], 'eccentricities': [Number('1.7'), Number('1.8'), Number('1.5')], 'baseline': '24pt'} } elif variant == 2: # Tells which angles shouldn't have any label (e.g. right angles) remove_labels = [False, True, True] lbls_dist = [0, 0, 1] rdeco = ['2:3'] subvariants = {1: {'endpoints': [Point('2.5', 0), Point('1.94', '1.58'), Point('0.5', '2.45'), Point('-2.45', '0.5')], 'eccentricities': [Number('1.7'), Number('1.8'), Number('1.5')], 'baseline': '24pt'} } shapes_source = shared.anglessets_2_1r_source return self._anglesset( shapes_source, subvariants, labels=labels, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, rdeco=rdeco, remove_labels=remove_labels, labels_dist=lbls_dist, extra_deco2=extra_deco2 ) def _3(self, variant=None, labels=None, name=None, extra_deco=None, subvariant_nb=None, thickness=None, extra_deco2=None, subtr_shapes=False): if variant != 0: raise ValueError('variant must be 0 (not \'{}\')'.format(variant)) lbls_dist = [0, 0, 0] remove_labels = [False, True, True] if extra_deco is None: extra_deco = {} subvariants = {1: {'endpoints': [Point('2.5', 0), Point(2, '1.5'), Point('0.7', '2.4'), Point('-0.88', '2.34')], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '24pt'}, 2: {'endpoints': [Point('2.5', 0), Point('1.5', 2), Point('-0.7', '2.4'), Point('-2.34', '0.88')], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '24pt'}, 3: {'endpoints': [Point(1, '2.3'), Point('-0.61', '2.42'), Point('-1.97', '1.54'), Point('-2.5', 0)], 'eccentricities': [Number('1.6'), Number('1.8'), Number('1.4')], 'baseline': '24pt'}, } shapes_source = shared.anglessets_3_source return self._anglesset( shapes_source, subvariants, labels=labels, name=name, extra_deco=extra_deco, thickness=thickness, subvariant_nb=subvariant_nb, extra_deco2=extra_deco2, remove_labels=remove_labels, labels_dist=lbls_dist )