mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets with their answers 00004 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00005 00006 # This file is part of Mathmaker. 00007 00008 # Mathmaker is free software; you can redistribute it and/or modify 00009 # it under the terms of the GNU General Public License as published by 00010 # the Free Software Foundation; either version 3 of the License, or 00011 # any later version. 00012 00013 # Mathmaker is distributed in the hope that it will be useful, 00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 # GNU General Public License for more details. 00017 00018 # You should have received a copy of the GNU General Public License 00019 # along with Mathmaker; if not, write to the Free Software 00020 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00021 00022 # ------------------------------------------------------------------------------ 00023 # -------------------------------------------------------------------------- 00024 # ------------------------------------------------------------------------------ 00025 ## 00026 # @package core.base_geometry 00027 # @brief Mathematical elementary geometrical objects. 00028 00029 import math 00030 import locale 00031 from decimal import * 00032 from base import * 00033 from core.base_calculus import Value 00034 from lib import * 00035 from lib.maths_lib import * 00036 from maintenance import debug 00037 from lib.common import cfg 00038 00039 markup_choice = cfg.get_value_from_file('MARKUP', 'USE') 00040 00041 if markup_choice == 'latex': 00042 from lib.common.latex import MARKUP 00043 00044 if debug.ENABLED: 00045 from lib.common import latex 00046 import machine 00047 00048 try: 00049 locale.setlocale(locale.LC_ALL, default.LANGUAGE + '.' + default.ENCODING) 00050 except: 00051 locale.setlocale(locale.LC_ALL, '') 00052 00053 # the mark 'dashed' has been removed from the available list since it may 00054 # produce buggy results sometimes from euktopst 00055 AVAILABLE_ANGLE_MARKS = ['', 'simple', 'double', 'triple', 'right', 00056 'forth', 'back', 'dotted'] 00057 AVAILABLE_SEGMENT_MARKS = [] 00058 00059 # GLOBAL 00060 #expression_begins = True 00061 00062 00063 # ------------------------------------------------------------------------------ 00064 # -------------------------------------------------------------------------- 00065 # ------------------------------------------------------------------------------ 00066 ## 00067 # @class Point 00068 # @brief 00069 class Point(Drawable): 00070 00071 00072 00073 00074 00075 # -------------------------------------------------------------------------- 00076 ## 00077 # @brief Constructor. 00078 # @param arg : [String, (nb,nb)]|Point 00079 # Types details : 00080 # - 00081 # @param options 00082 # Options details : 00083 # - 00084 # @warning Might raise... 00085 def __init__(self, arg, **options): 00086 if not (type(arg) == list or isinstance(arg, Point)): 00087 raise error.WrongArgument(' list|Point ', str(type(arg))) 00088 00089 elif type(arg) == list: 00090 if not len(arg) == 2: 00091 raise error.WrongArgument(' a list of length 2 ', 00092 ' a list of length ' \ 00093 + str(len(arg)) 00094 ) 00095 00096 if not type(arg[0]) == str: 00097 raise error.WrongArgument(' a str ', str(type(arg[0]))) 00098 00099 if not (type(arg[1]) == tuple \ 00100 and len(arg[1]) == 2 \ 00101 and is_.a_number(arg[1][0]) \ 00102 and is_.a_number(arg[1][1])): 00103 #___ 00104 raise error.WrongArgument(' (x, y) ', str(arg)) 00105 00106 self._name = arg[0] 00107 self._x = Decimal(arg[1][0]) 00108 self._y = Decimal(arg[1][1]) 00109 00110 else: 00111 self._name = arg.name 00112 self._x = arg.x 00113 self._y = arg.y 00114 00115 00116 00117 00118 # -------------------------------------------------------------------------- 00119 ## 00120 # @brief Returns the abscissa of the Point, rounded up to the tenth 00121 def get_x(self): 00122 return round(Decimal(str(self._x)), 00123 Decimal('0.01'), 00124 rounding=ROUND_HALF_UP 00125 ) 00126 00127 00128 00129 00130 00131 # -------------------------------------------------------------------------- 00132 ## 00133 # @brief Returns the exact abscissa of the Point 00134 def get_x_exact(self): 00135 return self._x 00136 00137 00138 00139 00140 00141 # -------------------------------------------------------------------------- 00142 ## 00143 # @brief Returns the ordinate of the Point, rounded up to the tenth 00144 def get_y(self): 00145 return round(Decimal(str(self._y)), 00146 Decimal('0.01'), 00147 rounding=ROUND_HALF_UP 00148 ) 00149 00150 00151 00152 00153 00154 # -------------------------------------------------------------------------- 00155 ## 00156 # @brief Returns the exact ordinate of the Point 00157 def get_y_exact(self): 00158 return self._y 00159 00160 00161 00162 00163 00164 00165 x = property(get_x, doc = "Abscissa of the Point (rounded)") 00166 00167 x_exact = property(get_x_exact, doc = "Abscissa of the Point (exact)") 00168 00169 y = property(get_y, doc = "Ordinate of the Point (exact)") 00170 00171 y_exact = property(get_y_exact, doc = "Ordinate of the Point (rounded)") 00172 00173 00174 00175 00176 00177 00178 00179 00180 # -------------------------------------------------------------------------- 00181 ## 00182 # @brief Sets the abscissa of the Point 00183 def set_x(self, arg): 00184 if not is_.a_number(arg): 00185 raise error.WrongArgument(' a number ', str(arg)) 00186 00187 self._x = arg 00188 00189 00190 00191 00192 # -------------------------------------------------------------------------- 00193 ## 00194 # @brief Sets the ordinate of the Point 00195 def set_y(self, arg): 00196 if not is_.a_number(arg): 00197 raise error.WrongArgument(' a number ', str(arg)) 00198 00199 self._y = arg 00200 00201 00202 00203 00204 00205 # -------------------------------------------------------------------------- 00206 ## 00207 # @brief Returns a new Point after rotation of self 00208 def rotate(self, center, angle, **options): 00209 if not isinstance(center, Point): 00210 raise error.WrongArgument(' a Point ', str(type(center))) 00211 00212 if not is_.a_number(angle): 00213 raise error.WrongArgument(' a number ', str(type(angle))) 00214 00215 delta_x = self.x_exact - center.x_exact 00216 delta_y = self.y_exact - center.y_exact 00217 00218 rx = delta_x*Decimal(str(math.cos(deg_to_rad(angle)))) \ 00219 - delta_y*Decimal(str(math.sin(deg_to_rad(angle)))) \ 00220 + center.x_exact 00221 ry = delta_x*Decimal(str(math.sin(deg_to_rad(angle)))) \ 00222 + delta_y*Decimal(str(math.cos(deg_to_rad(angle)))) \ 00223 + center.y_exact 00224 00225 new_name = self.name + "'" 00226 00227 if 'keep_name' in options and options['keep_name'] == True: 00228 new_name = self.name 00229 00230 elif 'new_name' in options and type(options['new_name']) == str: 00231 new_name = options['new_name'] 00232 00233 return Point([new_name, (rx, ry)]) 00234 00235 00236 00237 00238 00239 00240 00241 00242 00243 00244 00245 # ------------------------------------------------------------------------------ 00246 # -------------------------------------------------------------------------- 00247 # ------------------------------------------------------------------------------ 00248 ## 00249 # @class Segment 00250 # @brief 00251 class Segment(Drawable): 00252 00253 00254 00255 00256 00257 # -------------------------------------------------------------------------- 00258 ## 00259 # @brief Constructor. 00260 # @param arg (Point, Point) 00261 # Types details : 00262 # - 00263 # @param options 00264 # Options details : 00265 # - 00266 # @warning Might raise... 00267 def __init__(self, arg, **options): 00268 if not (type(arg) == tuple or isinstance(arg, Segment)): 00269 raise error.WrongArgument(' tuple|Segment ', str(type(arg))) 00270 00271 elif type(arg) == tuple: 00272 if not (isinstance(arg[0], Point) and isinstance(arg[1], Point)): 00273 #___ 00274 raise error.WrongArgument(' (Point, Point) ', str(arg)) 00275 00276 self._points = (arg[0].clone(), arg[1].clone()) 00277 00278 self._label = None 00279 00280 if 'label' in options and type(options['label']) == str: 00281 self._label = options['label'] 00282 00283 else: 00284 self._points = (arg.points[0].clone(), 00285 arg.points[1].clone() 00286 ) 00287 self._label = arg.label 00288 00289 self._mark = None 00290 00291 self._name = "[" + self.points[0].name + self.points[1].name + "]" 00292 00293 00294 00295 00296 # -------------------------------------------------------------------------- 00297 ## 00298 # @brief Returns the two points 00299 def get_points(self): 00300 return self._points 00301 00302 00303 00304 00305 00306 # -------------------------------------------------------------------------- 00307 ## 00308 # @brief Returns the label of the Segment 00309 def get_label(self): 00310 return self._label 00311 00312 00313 00314 00315 00316 00317 # -------------------------------------------------------------------------- 00318 ## 00319 # @brief Returns the length of the Segment 00320 def get_length(self): 00321 x_delta = self.points[0].x - self.points[1].x 00322 y_delta = self.points[0].y - self.points[1].y 00323 return math.hypot(x_delta, y_delta) 00324 00325 00326 00327 00328 00329 # -------------------------------------------------------------------------- 00330 ## 00331 # @brief Returns the length name of the Segment 00332 def get_length_name(self): 00333 return self.points[0].name + self.points[1].name 00334 00335 00336 00337 00338 00339 # -------------------------------------------------------------------------- 00340 ## 00341 # @brief Returns the mark of the Segment 00342 def get_mark(self): 00343 return self._mark 00344 00345 00346 00347 00348 00349 00350 # -------------------------------------------------------------------------- 00351 points = property(get_points, 00352 doc = "The couple of points ending the segment") 00353 00354 00355 00356 00357 00358 label = property(get_label, 00359 doc = "Label of the Segment") 00360 00361 length = property(get_length, 00362 doc = "Name of the Segment") 00363 00364 length_name = property(get_length_name, 00365 doc = "Length's name of the Segment") 00366 00367 mark = property(get_mark, 00368 doc = "Mark of the Segment") 00369 00370 00371 00372 00373 00374 # -------------------------------------------------------------------------- 00375 ## 00376 # @brief Sets the label of the Segment 00377 def set_label(self, arg): 00378 if not type(arg) == Value: 00379 raise error.WrongArgument(' Value ', str(type(arg))) 00380 00381 self._label = arg 00382 00383 00384 00385 00386 00387 # -------------------------------------------------------------------------- 00388 ## 00389 # @brief Sets the mark of the Segment 00390 def set_mark(self, arg): 00391 if not type(arg) == str: 00392 raise error.WrongArgument(' str ', str(type(arg))) 00393 00394 if not arg in AVAILABLE_SEGMENT_MARKS: 00395 raise error.WrongArgument(arg, 'a string from this list : ' \ 00396 + str(AVAILABLE_SEGMENT_MARKS)) 00397 00398 self._mark = arg 00399 00400 00401 00402 00403 00404 # -------------------------------------------------------------------------- 00405 ## 00406 # @brief Sets the points of the Segment (is this useful at all ?) 00407 def set_point(self, nb, arg): 00408 if not is_.a_number(nb): 00409 raise error.WrongArgument(' a number ', str(nb)) 00410 if not isinstance(arg, Point): 00411 raise error.WrongArgument(' a Point ', str(arg)) 00412 00413 00414 self._points[nb] = arg.clone() 00415 00416 00417 00418 00419 # ------------------------------------------------------------------------------ 00420 # -------------------------------------------------------------------------- 00421 # ------------------------------------------------------------------------------ 00422 ## 00423 # @class Ray 00424 # @brief 00425 class Ray(Drawable): 00426 00427 00428 00429 00430 00431 # -------------------------------------------------------------------------- 00432 ## 00433 # @brief Constructor. 00434 # @param arg : (Point, Point) 00435 # @param options : label 00436 # Options details : 00437 # @warning Might raise... 00438 def __init__(self, arg, **options): 00439 self._point0 = None # the initial point 00440 self._point1 = None 00441 self._name = None 00442 00443 if isinstance(arg, tuple) and len(arg) == 2 \ 00444 and isinstance(arg[0], Point) \ 00445 and isinstance(arg[1], Point): 00446 #___ 00447 self._point0 = arg[0].clone() 00448 self._point1 = arg[1].clone() 00449 self._name = MARKUP['opening_square_bracket'] 00450 self._name += arg[0].name + arg[1].name 00451 self._name += MARKUP['closing_bracket'] 00452 00453 00454 00455 00456 00457 # ------------------------------------------------------------------------------ 00458 # -------------------------------------------------------------------------- 00459 # ------------------------------------------------------------------------------ 00460 ## 00461 # @class Angle 00462 # @brief 00463 class Angle(Drawable): 00464 00465 00466 00467 00468 00469 # -------------------------------------------------------------------------- 00470 ## 00471 # @brief Constructor. 00472 # @param arg : (Point, Point, Point) 00473 # @param options : label 00474 # Options details : 00475 # @warning Might raise... 00476 def __init__(self, arg, **options): 00477 self._ray0 = None 00478 self._ray1 = None 00479 self._points = None 00480 self._measure = None 00481 self._mark = "" 00482 self._label = Value("") 00483 self._name = None 00484 00485 if isinstance(arg, tuple) and len(arg) == 3 \ 00486 and isinstance(arg[0], Point) \ 00487 and isinstance(arg[1], Point) \ 00488 and isinstance(arg[2], Point): 00489 #___ 00490 self._ray0 = Ray((arg[1], arg[0])) 00491 self._ray1 = Ray((arg[1], arg[2])) 00492 self._points = [arg[0].clone(), 00493 arg[1].clone(), 00494 arg[2].clone()] 00495 00496 # Let's determine the measure of the angle : 00497 aux_side0 = Segment((self._points[0], self._points[1])) 00498 aux_side1 = Segment((self._points[1], self._points[2])) 00499 aux_side2 = Segment((self._points[2], self._points[0])) 00500 aux_num = aux_side0.length * aux_side0.length \ 00501 + aux_side1.length * aux_side1.length \ 00502 - aux_side2.length * aux_side2.length 00503 aux_denom = 2 * aux_side0.length * aux_side1.length 00504 aux_cos = aux_num / aux_denom 00505 self._measure = Decimal(str(math.degrees(math.acos(aux_cos)))) 00506 00507 if 'label' in options and type(options['label']) == str: 00508 self._label = options['label'] 00509 00510 if 'mark' in options and type(options['mark']) == str: 00511 self._mark = options['mark'] 00512 00513 self._name = MARKUP['opening_widehat'] 00514 self._name += arg[0].name + arg[1].name + arg[2].name 00515 self._name += MARKUP['closing_widehat'] 00516 00517 else: 00518 raise error.WrongArgument(' (Point, Point, Point) ', str(type(arg))) 00519 00520 self._label_display_angle = round(Decimal(str(self._measure)), 00521 Decimal('0.1'), 00522 rounding=ROUND_HALF_UP 00523 ) / 2 00524 00525 00526 00527 00528 00529 # -------------------------------------------------------------------------- 00530 ## 00531 # @brief Returns the measure of the angle 00532 def get_measure(self): 00533 return self._measure 00534 00535 00536 00537 00538 00539 # -------------------------------------------------------------------------- 00540 ## 00541 # @brief Returns the point0 of the angle 00542 def get_point0(self): 00543 return self._points[0] 00544 00545 00546 00547 00548 00549 00550 # -------------------------------------------------------------------------- 00551 ## 00552 # @brief Returns the vertex of the angle, as a Point 00553 def get_point1(self): 00554 return self._points[1] 00555 00556 00557 00558 00559 00560 # -------------------------------------------------------------------------- 00561 ## 00562 # @brief Returns the point2 of the angle 00563 def get_point2(self): 00564 return self._points[2] 00565 00566 00567 00568 00569 00570 # -------------------------------------------------------------------------- 00571 ## 00572 # @brief Returns the label of the angle 00573 def get_label(self): 00574 return self._label 00575 00576 00577 00578 00579 00580 # -------------------------------------------------------------------------- 00581 ## 00582 # @brief Returns the angle (for display) of label's angle 00583 def get_label_display_angle(self): 00584 return self._label_display_angle 00585 00586 00587 00588 00589 00590 # -------------------------------------------------------------------------- 00591 ## 00592 # @brief Returns the mark of the angle 00593 def get_mark(self): 00594 return self._mark 00595 00596 00597 00598 00599 00600 measure = property(get_measure, 00601 doc = "Measure of the angle") 00602 00603 point0 = property(get_point0, 00604 doc = "Point0 of the angle") 00605 00606 point1 = property(get_point1, 00607 doc = "Vertex of the angle") 00608 00609 point2 = property(get_point2, 00610 doc = "Point2 of the angle") 00611 00612 vertex = property(get_point1, 00613 doc = "Vertex of the angle") 00614 00615 label = property(get_label, 00616 doc = "Label of the angle") 00617 00618 label_display_angle = property(get_label_display_angle, 00619 doc = "Display angle of the label of the angle") 00620 00621 mark = property(get_mark, 00622 doc = "Mark of the angle") 00623 00624 00625 00626 00627 00628 # -------------------------------------------------------------------------- 00629 ## 00630 # @brief Sets the label of the angle 00631 def set_label(self, arg): 00632 if type(arg) == str or isinstance(arg, Value): 00633 self._label = Value(arg) 00634 00635 else: 00636 raise error.WrongArgument(arg, 'str or Value') 00637 00638 00639 00640 00641 00642 # -------------------------------------------------------------------------- 00643 ## 00644 # @brief Sets the angle (for display) of label's angle 00645 def set_label_display_angle(self, arg): 00646 if not is_.a_number(arg): 00647 raise error.WrongArgument(arg, ' a number ') 00648 else: 00649 self._label_display_angle = round(Decimal(str(arg)), 00650 Decimal('0.1'), 00651 rounding=ROUND_HALF_UP 00652 ) 00653 00654 00655 00656 00657 00658 # -------------------------------------------------------------------------- 00659 ## 00660 # @brief Sets the mark of the angle 00661 def set_mark(self, arg): 00662 if type(arg) == str: 00663 if arg in AVAILABLE_ANGLE_MARKS: 00664 self._mark = arg 00665 else: 00666 raise error.WrongArgument(arg, 'a string from this list : ' \ 00667 + str(AVAILABLE_ANGLE_MARKS)) 00668 00669 else: 00670 raise error.WrongArgument(arg, 'str') 00671 00672 00673 00674 00675 00676 00677 00678 00679