# -*- 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
)