mathmaker
0.4(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 exact abscissa of the Point 00121 def get_x_exact(self): 00122 return self._x 00123 # -------------------------------------------------------------------------- 00124 x_exact = property(get_x_exact, doc = "Abscissa of the Point (exact)") 00125 00126 00127 00128 00129 00130 # -------------------------------------------------------------------------- 00131 ## 00132 # @brief Returns the abscissa of the Point, rounded up to the tenth 00133 def get_x(self): 00134 return round(Decimal(str(self._x)), 00135 Decimal('0.01'), 00136 rounding=ROUND_HALF_UP 00137 ) 00138 # -------------------------------------------------------------------------- 00139 x = property(get_x, doc = "Abscissa of the Point (rounded)") 00140 00141 00142 00143 00144 00145 # -------------------------------------------------------------------------- 00146 ## 00147 # @brief Sets the abscissa of the Point 00148 def set_x(self, arg): 00149 if not is_.a_number(arg): 00150 raise error.WrongArgument(' a number ', str(arg)) 00151 00152 self._x = arg 00153 00154 00155 00156 00157 # -------------------------------------------------------------------------- 00158 ## 00159 # @brief Returns the exact ordinate of the Point 00160 def get_y_exact(self): 00161 return self._y 00162 # -------------------------------------------------------------------------- 00163 y_exact = property(get_y_exact, doc = "Ordinate of the Point (rounded)") 00164 00165 00166 00167 00168 00169 # -------------------------------------------------------------------------- 00170 ## 00171 # @brief Returns the ordinate of the Point, rounded up to the tenth 00172 def get_y(self): 00173 return round(Decimal(str(self._y)), 00174 Decimal('0.01'), 00175 rounding=ROUND_HALF_UP 00176 ) 00177 # -------------------------------------------------------------------------- 00178 y = property(get_y, doc = "Ordinate of the Point (exact)") 00179 00180 00181 00182 00183 00184 # -------------------------------------------------------------------------- 00185 ## 00186 # @brief Sets the ordinate of the Point 00187 def set_y(self, arg): 00188 if not is_.a_number(arg): 00189 raise error.WrongArgument(' a number ', str(arg)) 00190 00191 self._y = arg 00192 00193 00194 00195 00196 00197 # -------------------------------------------------------------------------- 00198 ## 00199 # @brief Returns the name of the Point 00200 def get_name(self): 00201 return self._name 00202 # -------------------------------------------------------------------------- 00203 name = property(get_name, 00204 doc = "Name of the Point") 00205 00206 00207 00208 00209 00210 # -------------------------------------------------------------------------- 00211 ## 00212 # @brief Sets the name of the Point 00213 def set_name(self, arg): 00214 if not is_.a_string(arg): 00215 raise error.WrongArgument(' a string ', str(arg)) 00216 00217 self._name = arg 00218 00219 00220 00221 00222 00223 # -------------------------------------------------------------------------- 00224 ## 00225 # @brief Returns a new Point after rotation of self 00226 def rotate(self, center, angle, **options): 00227 if not isinstance(center, Point): 00228 raise error.WrongArgument(' a Point ', str(type(center))) 00229 00230 if not is_.a_number(angle): 00231 raise error.WrongArgument(' a number ', str(type(angle))) 00232 00233 delta_x = self.x_exact - center.x_exact 00234 delta_y = self.y_exact - center.y_exact 00235 00236 rx = delta_x*Decimal(str(math.cos(deg_to_rad(angle)))) \ 00237 - delta_y*Decimal(str(math.sin(deg_to_rad(angle)))) \ 00238 + center.x_exact 00239 ry = delta_x*Decimal(str(math.sin(deg_to_rad(angle)))) \ 00240 + delta_y*Decimal(str(math.cos(deg_to_rad(angle)))) \ 00241 + center.y_exact 00242 00243 new_name = self.name + "'" 00244 00245 if 'keep_name' in options and options['keep_name'] == True: 00246 new_name = self.name 00247 00248 elif 'new_name' in options and type(options['new_name']) == str: 00249 new_name = options['new_name'] 00250 00251 return Point([new_name, (rx, ry)]) 00252 00253 00254 00255 00256 00257 00258 00259 00260 00261 00262 00263 # ------------------------------------------------------------------------------ 00264 # -------------------------------------------------------------------------- 00265 # ------------------------------------------------------------------------------ 00266 ## 00267 # @class Segment 00268 # @brief 00269 class Segment(Drawable): 00270 00271 00272 00273 00274 00275 # -------------------------------------------------------------------------- 00276 ## 00277 # @brief Constructor. 00278 # @param arg (Point, Point) 00279 # Types details : 00280 # - 00281 # @param options 00282 # Options details : 00283 # - 00284 # @warning Might raise... 00285 def __init__(self, arg, **options): 00286 if not (type(arg) == tuple or isinstance(arg, Segment)): 00287 raise error.WrongArgument(' tuple|Segment ', str(type(arg))) 00288 00289 elif type(arg) == tuple: 00290 if not (isinstance(arg[0], Point) and isinstance(arg[1], Point)): 00291 #___ 00292 raise error.WrongArgument(' (Point, Point) ', str(arg)) 00293 00294 self._points = (arg[0].clone(), arg[1].clone()) 00295 00296 self._label = None 00297 00298 if 'label' in options and type(options['label']) == str: 00299 self._label = options['label'] 00300 00301 else: 00302 self._points = (arg.points[0].clone(), 00303 arg.points[1].clone() 00304 ) 00305 self._label = arg.label 00306 00307 self._mark = None 00308 00309 00310 00311 00312 00313 # -------------------------------------------------------------------------- 00314 ## 00315 # @brief Returns the two points 00316 def get_points(self): 00317 return self._points 00318 # -------------------------------------------------------------------------- 00319 points = property(get_points, 00320 doc = "The couple of points ending the segment") 00321 00322 00323 00324 00325 00326 # -------------------------------------------------------------------------- 00327 ## 00328 # @brief Sets the points of the Segment (is this useful at all ?) 00329 def set_point(self, nb, arg): 00330 if not is_.a_number(nb): 00331 raise error.WrongArgument(' a number ', str(nb)) 00332 if not isinstance(arg, Point): 00333 raise error.WrongArgument(' a Point ', str(arg)) 00334 00335 00336 self._points[nb] = arg.clone() 00337 00338 00339 00340 00341 # -------------------------------------------------------------------------- 00342 ## 00343 # @brief Returns the name of the Segment 00344 def get_name(self): 00345 return "[" + self.points[0].name + self.points[1].name + "]" 00346 # -------------------------------------------------------------------------- 00347 name = property(get_name, 00348 doc = "Name of the Segment") 00349 00350 00351 00352 00353 00354 # -------------------------------------------------------------------------- 00355 ## 00356 # @brief Returns the length name of the Segment 00357 def get_length_name(self): 00358 return self.points[0].name + self.points[1].name 00359 # -------------------------------------------------------------------------- 00360 length_name = property(get_length_name, 00361 doc = "Length's name of the Segment") 00362 00363 00364 00365 00366 00367 # -------------------------------------------------------------------------- 00368 ## 00369 # @brief Sets the label of the Segment 00370 def set_label(self, arg): 00371 if not type(arg) == Value: 00372 raise error.WrongArgument(' Value ', str(type(arg))) 00373 00374 self._label = arg 00375 00376 00377 00378 00379 00380 # -------------------------------------------------------------------------- 00381 ## 00382 # @brief Returns the label of the Segment 00383 def get_label(self): 00384 return self._label 00385 # -------------------------------------------------------------------------- 00386 label = property(get_label, 00387 doc = "Label of the Segment") 00388 00389 00390 00391 00392 00393 # -------------------------------------------------------------------------- 00394 ## 00395 # @brief Sets the mark of the Segment 00396 def set_mark(self, arg): 00397 if not type(arg) == str: 00398 raise error.WrongArgument(' str ', str(type(arg))) 00399 00400 if not arg in AVAILABLE_SEGMENT_MARKS: 00401 raise error.WrongArgument(arg, 'a string from this list : ' \ 00402 + str(AVAILABLE_SEGMENT_MARKS)) 00403 00404 self._mark = arg 00405 00406 00407 00408 00409 00410 # -------------------------------------------------------------------------- 00411 ## 00412 # @brief Returns the mark of the Segment 00413 def get_mark(self): 00414 return self._mark 00415 # -------------------------------------------------------------------------- 00416 mark = property(get_mark, 00417 doc = "Mark of the Segment") 00418 00419 00420 00421 00422 00423 # -------------------------------------------------------------------------- 00424 ## 00425 # @brief Returns the length of the Segment 00426 def get_length(self): 00427 x_delta = self.points[0].x - self.points[1].x 00428 y_delta = self.points[0].y - self.points[1].y 00429 return math.hypot(x_delta, y_delta) 00430 00431 # -------------------------------------------------------------------------- 00432 length = property(get_length, 00433 doc = "Name of the Segment") 00434 00435 00436 00437 00438 00439 00440 # ------------------------------------------------------------------------------ 00441 # -------------------------------------------------------------------------- 00442 # ------------------------------------------------------------------------------ 00443 ## 00444 # @class Ray 00445 # @brief 00446 class Ray(Drawable): 00447 00448 00449 00450 00451 00452 # -------------------------------------------------------------------------- 00453 ## 00454 # @brief Constructor. 00455 # @param arg : (Point, Point) 00456 # @param options : label 00457 # Options details : 00458 # @warning Might raise... 00459 def __init__(self, arg, **options): 00460 self._point0 = None # the initial point 00461 self._point1 = None 00462 self._name = None 00463 00464 if isinstance(arg, tuple) and len(arg) == 2 \ 00465 and isinstance(arg[0], Point) \ 00466 and isinstance(arg[1], Point): 00467 #___ 00468 self._point0 = arg[0].clone() 00469 self._point1 = arg[1].clone() 00470 self._name = MARKUP['opening_square_bracket'] 00471 self._name += arg[0].name + arg[1].name 00472 self._name += MARKUP['closing_bracket'] 00473 00474 00475 00476 00477 00478 # ------------------------------------------------------------------------------ 00479 # -------------------------------------------------------------------------- 00480 # ------------------------------------------------------------------------------ 00481 ## 00482 # @class Angle 00483 # @brief 00484 class Angle(Drawable): 00485 00486 00487 00488 00489 00490 # -------------------------------------------------------------------------- 00491 ## 00492 # @brief Constructor. 00493 # @param arg : (Point, Point, Point) 00494 # @param options : label 00495 # Options details : 00496 # @warning Might raise... 00497 def __init__(self, arg, **options): 00498 self._ray0 = None 00499 self._ray1 = None 00500 self._points = None 00501 self._measure = None 00502 self._mark = "" 00503 self._label = Value("") 00504 self._name = None 00505 00506 if isinstance(arg, tuple) and len(arg) == 3 \ 00507 and isinstance(arg[0], Point) \ 00508 and isinstance(arg[1], Point) \ 00509 and isinstance(arg[2], Point): 00510 #___ 00511 self._ray0 = Ray((arg[1], arg[0])) 00512 self._ray1 = Ray((arg[1], arg[2])) 00513 self._points = [arg[0].clone(), 00514 arg[1].clone(), 00515 arg[2].clone()] 00516 00517 # Let's determine the measure of the angle : 00518 aux_side0 = Segment((self._points[0], self._points[1])) 00519 aux_side1 = Segment((self._points[1], self._points[2])) 00520 aux_side2 = Segment((self._points[2], self._points[0])) 00521 aux_num = aux_side0.length * aux_side0.length \ 00522 + aux_side1.length * aux_side1.length \ 00523 - aux_side2.length * aux_side2.length 00524 aux_denom = 2 * aux_side0.length * aux_side1.length 00525 aux_cos = aux_num / aux_denom 00526 self._measure = Decimal(str(math.degrees(math.acos(aux_cos)))) 00527 00528 if 'label' in options and type(options['label']) == str: 00529 self._label = options['label'] 00530 00531 if 'mark' in options and type(options['mark']) == str: 00532 self._mark = options['mark'] 00533 00534 self._name = MARKUP['opening_widehat'] 00535 self._name += arg[0].name + arg[1].name + arg[2].name 00536 self._name += MARKUP['closing_widehat'] 00537 00538 else: 00539 raise error.WrongArgument(' (Point, Point, Point) ', str(type(arg))) 00540 00541 self._label_display_angle = round(Decimal(str(self._measure)), 00542 Decimal('0.1'), 00543 rounding=ROUND_HALF_UP 00544 ) / 2 00545 00546 00547 00548 00549 00550 # -------------------------------------------------------------------------- 00551 ## 00552 # @brief Returns the name of the angle 00553 def get_name(self): 00554 return self._name 00555 # -------------------------------------------------------------------------- 00556 name = property(get_name, 00557 doc = "Name of the angle") 00558 00559 00560 00561 00562 00563 # -------------------------------------------------------------------------- 00564 ## 00565 # @brief Returns the measure of the angle 00566 def get_measure(self): 00567 return self._measure 00568 # -------------------------------------------------------------------------- 00569 measure = property(get_measure, 00570 doc = "Measure of the angle") 00571 00572 00573 00574 00575 00576 # -------------------------------------------------------------------------- 00577 ## 00578 # @brief Returns the point0 of the angle 00579 def get_point0(self): 00580 return self._points[0] 00581 # -------------------------------------------------------------------------- 00582 point0 = property(get_point0, 00583 doc = "Point0 of the angle") 00584 00585 00586 00587 00588 # -------------------------------------------------------------------------- 00589 ## 00590 # @brief Returns the vertex of the angle, as a Point 00591 def get_point1(self): 00592 return self._points[1] 00593 # -------------------------------------------------------------------------- 00594 point1 = property(get_point1, 00595 doc = "Vertex of the angle") 00596 vertex = property(get_point1, 00597 doc = "Vertex of the angle") 00598 00599 00600 00601 # -------------------------------------------------------------------------- 00602 ## 00603 # @brief Returns the point2 of the angle 00604 def get_point2(self): 00605 return self._points[2] 00606 # -------------------------------------------------------------------------- 00607 point2 = property(get_point2, 00608 doc = "Point2 of the angle") 00609 00610 00611 00612 # -------------------------------------------------------------------------- 00613 ## 00614 # @brief Returns the label of the angle 00615 def get_label(self): 00616 return self._label 00617 # -------------------------------------------------------------------------- 00618 label = property(get_label, 00619 doc = "Label of the angle") 00620 00621 00622 00623 00624 00625 # -------------------------------------------------------------------------- 00626 ## 00627 # @brief Returns the angle (for display) of label's angle 00628 def get_label_display_angle(self): 00629 return self._label_display_angle 00630 # -------------------------------------------------------------------------- 00631 label_display_angle = property(get_label_display_angle, 00632 doc = "Display angle of the label of the angle") 00633 00634 00635 00636 00637 00638 # -------------------------------------------------------------------------- 00639 ## 00640 # @brief Returns the mark of the angle 00641 def get_mark(self): 00642 return self._mark 00643 # -------------------------------------------------------------------------- 00644 mark = property(get_mark, 00645 doc = "Mark of the angle") 00646 00647 00648 00649 00650 00651 # -------------------------------------------------------------------------- 00652 ## 00653 # @brief Sets the label of the angle 00654 def set_label(self, arg): 00655 if type(arg) == str or isinstance(arg, Value): 00656 self._label = Value(arg) 00657 00658 else: 00659 raise error.WrongArgument(arg, 'str or Value') 00660 00661 00662 00663 00664 00665 # -------------------------------------------------------------------------- 00666 ## 00667 # @brief Sets the angle (for display) of label's angle 00668 def set_label_display_angle(self, arg): 00669 if not is_.a_number(arg): 00670 raise error.WrongArgument(arg, ' a number ') 00671 else: 00672 self._label_display_angle = round(Decimal(str(arg)), 00673 Decimal('0.1'), 00674 rounding=ROUND_HALF_UP 00675 ) 00676 00677 00678 00679 00680 00681 # -------------------------------------------------------------------------- 00682 ## 00683 # @brief Sets the mark of the angle 00684 def set_mark(self, arg): 00685 if type(arg) == str: 00686 if arg in AVAILABLE_ANGLE_MARKS: 00687 self._mark = arg 00688 else: 00689 raise error.WrongArgument(arg, 'a string from this list : ' \ 00690 + str(AVAILABLE_ANGLE_MARKS)) 00691 00692 else: 00693 raise error.WrongArgument(arg, 'str') 00694 00695 00696 00697 00698 00699 00700 00701 00702