mathmaker  0.6(alpha)
mamk_misc/doc/mathmaker4doxygen/core/base_geometry.py
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