def get_point(self, llam, advised=True): """ Return the point on the curve for the value llam of the parameter. Add the attribute advised_mark_angle which gives the normal exterior angle at the given point. If you want to put a mark on the point P (obtained by get_point), you should consider to write P.put_mark(r,P.advised_mark_angle,text) The so build angle is somewhat "optimal" for a visual point of view. The attribute self.get_point(llam).advised_mark_angle is given in degree. The advised angle is given in degree. The optional boolean argument <advised> serves to avoid infinite loops because we use get_point in get_normal_vector. """ from yanntricks.src.AngleMeasure import AngleMeasure if isinstance(llam, AngleMeasure): llam = llam.radian P = Point(self.f1(llam), self.f2(llam)) if advised: P._advised_mark_angle = self.get_normal_vector(llam).angle() return P
def bounding_box(self, pspict=None): """ Return the bounding box of the interpolation curve EXAMPLES:: sage: from yanntricks import * sage: print InterpolationCurve([Point(0,0),Point(1,1)]).bounding_box() <BoundingBox xmin=0.0,xmax=1.0; ymin=0.0,ymax=1.0> sage: C=Circle(Point(0,0),1) sage: n=400 sage: print InterpolationCurve([C.get_point(i*SR(360)/n,advised=False) for i in range(n)]).bounding_box() <BoundingBox xmin=-1.0,xmax=1.0; ymin=-1.0,ymax=1.0> NOTE:: Since the bounding box is computed from the give points while the curve is an interpolation, this bounding box is incorrect to the extend that \pscurve does not remains in the convex hull of the given points. EXAMPLE: sage: F=InterpolationCurve([Point(-1,1),Point(1,1),Point(1,-1),Point(-1,-1)]) sage: print F.bounding_box() <BoundingBox xmin=-1.0,xmax=1.0; ymin=-1.0,ymax=1.0> """ from yanntricks.src.point import Point from yanntricks.src.BoundingBox import BoundingBox bb = BoundingBox(Point(self.xmin(), self.ymin()), Point(self.xmax(), self.ymax())) return bb
def __call__(self, a, b=None): """ return the affine vector at point (a,b). INPUT: - ``a,b`` - numbers. OUTPUT: an affine vector based on (a,b). EXAMPLES:: sage: from yanntricks import * sage: x,y=var('x,y') sage: F=VectorField(x**2,y**3) sage: print F(1,2) <vector I=<Point(1,2)> F=<Point(2,10)>> sage: P=Point(3,4) sage: print F(P) <vector I=<Point(3,4)> F=<Point(12,68)>> """ from yanntricks.src.affine_vector import AffineVector if b is not None: P = Point(a, b) else: P = a vx = self.fx(x=P.x, y=P.y) vy = self.fy(x=P.x, y=P.y) return AffineVector(P, Point(P.x + vx, P.y + vy))
def general_function_get_point(fun, x, advised=True): """ Return a point on the graph of the function. Return a point on the graph of the function with the given x, i.e. it returns the point (x,f(x)). Also set an attribute advised_mark_angle to the point. This angle is the normal exterior to the graph; visually this is usually the best place to put a mark. Typically you use this as P=f.get_point(3) P.mark(radius,P.advised_mark_angle,"$P$") NOTE: If you don't plan to put a mark on the point, you are invited to use advised=False in order to speed up the computations. """ from yanntricks.src.point import Point P = Point(float(x), fun(x)) if advised: try: ca = fun.derivative()(x) except TypeError: # Sage cannot derivate the function print("I'm not able to compute derivative of {0}.\ You should pass advised=False".format(fun)) else: angle_n = degree(atan(ca) + pi / 2) if fun.derivative(2)(x) > 0: angle_n = angle_n + 180 P._advised_mark_angle = angle_n # pylint:disable=protected-access return P
def __init__(self, bb=None): from yanntricks.src.BoundingBox import BoundingBox if bb is None: bb = BoundingBox() ObjectGraph.__init__(self, self) self.BB = bb self.separator_name = "GRID" # Default values, have to be integer. self.add_option({"Dx": 1, "Dy": 1}) self.Dx = self.options.DicoOptions["Dx"] self.Dy = self.options.DicoOptions["Dy"] self.num_subX = 2 self.num_subY = 2 self.draw_border = False self.draw_horizontal_grid = True self.draw_vertical_grid = True self.main_horizontal = Segment(Point(0, 1), Point(1, 1)) self.main_horizontal.parameters.color = "gray" self.main_horizontal.parameters.style = "solid" self.main_vertical = Segment(Point(0, 1), Point(1, 1)) self.main_vertical.parameters.color = "gray" self.main_vertical.parameters.style = "solid" self.sub_vertical = Segment(Point(0, 1), Point(1, 1)) self.sub_vertical.parameters.color = "gray" self.sub_vertical.parameters.style = "dotted" self.sub_horizontal = Segment(Point(0, 1), Point(1, 1)) self.sub_horizontal.parameters.color = "gray" self.sub_horizontal.parameters.style = "dotted" self.border = Segment(Point(0, 1), Point(1, 1)) self.border.parameters.color = "gray" self.border.parameters.style = "dotted"
def get_tangent_vector(self, x, advised=False, numerical=False): """Return a tangent vector at the point (x,f(x)).""" from yanntricks.src.point import Point ca = self.derivative()(x, numerical=numerical) v = Point(1, ca).normalize().origin(self.get_point(x, advised)) v.in_math_bounding_box = False return v
def __init__(self, op, P, a, b, c): from yanntricks.src.segment import Segment from yanntricks.src.point import Point ObjectGraph.__init__(self, self) self.op = op self.P = P self.Px = P[0] self.Py = P[1] self.a = a self.b = b self.c = c self.transparent = True self.A = [ Point(self.Px, self.Py + b), Point(self.Px + a, self.Py + b), Point(self.Px + a, self.Py), Point(self.Px, self.Py) ] # The points on the first and second rectangle self.c1 = [self.op.point(P.x, P.y, 0) for P in self.A] self.c2 = [self.op.point(P.x, P.y, self.c) for P in self.A] self.A = self.c1[0] self.B = self.c1[1] self.C = self.c1[2] self.D = self.c1[3] self.E = self.c2[0] self.F = self.c2[1] self.G = self.c2[2] self.H = self.c2[3] for P in self.c1: P.parameters.symbol = "" for P in self.c2: P.parameters.symbol = "" # The edges. self.segP = [ Segment(self.c1[i], self.c2[i]) for i in range(0, len(self.c1)) ] self.segc1 = [ Segment(self.c1[i], self.c1[(i + 1) % len(self.c1)]) for i in range(0, len(self.c1)) ] self.segc2 = [ Segment(self.c2[i], self.c2[(i + 1) % len(self.c2)]) for i in range(0, len(self.c2)) ] if op.alpha < 90: self.segP[3].parameters.style = "dashed" self.segc2[2].parameters.style = "dashed" self.segc2[3].parameters.style = "dashed" else: self.segP[2].parameters.style = "dashed" self.segc2[2].parameters.style = "dashed" self.segc2[1].parameters.style = "dashed"
def _bounding_box(self, pspict=None): from yanntricks.src.BoundingBox import BoundingBox xmin = self.xmin(self.llamI, self.llamF) xmax = self.xmax(self.llamI, self.llamF) ymin = self.ymin(self.llamI, self.llamF) ymax = self.ymax(self.llamI, self.llamF) bb = BoundingBox(Point(xmin, ymin), Point(xmax, ymax)) return bb
def getVertex(self, pos): from yanntricks.src.point import Point if pos == "NE": return Point(self.xmax, self.ymax) if pos == "NW": return Point(self.xmin, self.ymax) if pos == "SE": return Point(self.xmax, self.ymin) if pos == "SW": return Point(self.xmin, self.ymin)
def __init__(self, P, text, hide=True): from yanntricks.src.Constructors import Mark from yanntricks.src.Constructors import Rectangle ObjectGraph.__init__(self, self) self.P = P self.text = text self.mark = Mark(self, 0, 0, self.text) self.hide = hide # This is fake; just to have an object to act on. self.rectangle = Rectangle(Point(0, 0), Point(1, 1)) self.rectangle.parameters.filled() self.rectangle.parameters.fill.color = "white" self.rectangle.parameters.style = "none"
def test_vector_equality(): echo_function("test_vector_equality") A = Point(1, 1) B = Point(2, -3) vv = AffineVector(A, B) ww = AffineVector(A, B) echo_single_test("Two trivial equalities") assert_equal(vv, vv) assert_equal(ww, ww) echo_single_test("One less trivial equalities") assert_equal(vv, ww)
def polar_to_visual_polar(r, theta, pspict=None): """ From '(r,theta)', return the (s,alpha) such that the point (s,alpha) visually appears as (r,theta). """ P = visual_polar(Point(0, 0), r, theta, pspict) return P.polar_coordinates()
def PolarSegment(P, r, theta): """ return a segment on the base point P (class Point) of length r and angle theta (degree) """ alpha = radian(theta) return Segment(P, Point(P.x + r * cos(alpha), P.y + r * sin(alpha)))
def get_tangent_vector(self, llam, advised=False): """ returns the tangent vector to the curve for the value of the parameter given by llam. The vector is normed to 1. INPUT:: - ``llam`` - the value of the parameter on which we want the tangent. - ``advised`` - (default = False) if True, the initial point is returned with its advised_mark_angle. This takes quite a long time of computation (and creates infinite loops in some circumstances) EXAMPLES:: sage: from yanntricks import * sage: F=ParametricCurve(x,x**2) sage: print F.get_tangent_vector(0) <vector I=<Point(0,0)> F=<Point(1,0)>> sage: print F.get_tangent_vector(1) <vector I=<Point(1,1)> F=<Point(1/5*sqrt(5) + 1,2/5*sqrt(5) + 1)>> """ from yanntricks.src.Constructors import AffineVector initial = self.get_point(llam, advised) return AffineVector( initial, Point(initial.x + self.derivative().f1(llam), initial.y + self.derivative().f2(llam))).normalize()
def action_on_pspict(self, pspict): from yanntricks.src.SmallComputations import MainGridArray from yanntricks.src.SmallComputations import SubGridArray a = [] # ++++++++++++ Border ++++++++ if self.draw_border: # Right border if self.draw_vertical_grid: if self.BB.xmax != int(self.BB.xmax): S = self.BB.east_segment() S.merge_options(self.border) a.append(S) # Left border if self.draw_vertical_grid: if self.BB.xmin != int(self.BB.xmin): S = self.BB.west_segment() S.merge_options(self.border) a.append(S) # Upper border if self.draw_horizontal_grid: if self.BB.ymax != int(self.BB.ymax): S = self.BB.north_segment() S.merge_options(self.border) a.append(S) # Lower border if self.draw_horizontal_grid: if self.BB.ymin != int(self.BB.ymin): S = self.BB.south_segment() S.merge_options(self.border) a.append(S) if self.draw_vertical_grid: # ++++++++++++ Principal vertical lines ++++++++ for x in MainGridArray(self.BB.xmin, self.BB.xmax, self.Dx): S = Segment(Point(x, self.BB.ymin), Point(x, self.BB.ymax)) S.merge_options(self.main_vertical) a.append(S) # ++++++++++++ The vertical sub grid ++++++++ if self.num_subX != 0: for x in SubGridArray(self.BB.xmin, self.BB.xmax, self.Dx, self.num_subX): S = Segment(Point(x, self.BB.ymin), Point(x, self.BB.ymax)) S.merge_options(self.sub_vertical) a.append(S) if self.draw_horizontal_grid: # ++++++++++++ The horizontal sub grid ++++++++ if self.num_subY != 0: for y in SubGridArray(self.BB.ymin, self.BB.ymax, self.Dy, self.num_subY): S = Segment(Point(self.BB.xmin, y), Point(self.BB.xmax, y)) S.merge_options(self.sub_horizontal) a.append(S) # ++++++++++++ Principal horizontal lines ++++++++ for y in MainGridArray(self.BB.ymin, self.BB.ymax, self.Dy): S = Segment(Point(self.BB.xmin, y), Point(self.BB.xmax, y)) S.merge_options(self.main_horizontal) a.append(S) pspict.DrawGraphs(a, separator_name=self.separator_name)
def F(self): from yanntricks.src.point import Point if not self.do_cut_y: Mx = self.Mx else: Mx = self.pieces[0].Mx P = Point(Mx, self(Mx)) return P
def Vector(A, B=None): """ Return an affine vector from (0,0) to the given point. Vector(3,4) Vector(P) # If 'P' is a point Vector(t) # if 't' is a tuple of two numbers """ O = Point(0, 0) if isinstance(A, Point): return AffineVector(O, A) if isinstance(A, tuple): if len(A) != 2: raise TypeError(f"You can define a vector from a tuple " f"of length 2, not {len(A)}") return AffineVector(O, Point(A[0], A[1])) return AffineVector(Point(0, 0), Point(A, B))
def I(self): from yanntricks.src.point import Point if not self.do_cut_y: mx = self.mx else: mx = self.pieces[0].mx P = Point(mx, self(mx)) return P
def decomposition(self, seg): # we make the computations with vectors based at (0,0) # and then translate the answer. from yanntricks.src.Constructors import Vector s0 = self.fix_origin(Point(0, 0)) if isinstance(seg, AffineVector): v = seg.segment() seg0 = seg.fix_origin(Point(0, 0)) A = s0.F.projection(seg0) paral0 = Vector(A) perp0 = s0 - paral0 ans_paral = paral0.fix_origin(self.I) ans_perp = perp0.fix_origin(self.I) return ans_perp, ans_paral
def __neg__(self): """ return -self. That is an affine vector attached to the same point, but with the opposite direction. """ nx = self.I.x - self.Dx ny = self.I.y - self.Dy return AffineVector(self.I, Point(nx, ny))
def visual_angleIF(self, pspict): from yanntricks.src.point import Point from yanntricks.src.AngleMeasure import AngleMeasure aI1 = visual_polar_coordinates( Point(cos(self.angleI.radian), sin(self.angleI.radian)), pspict).measure aF1 = visual_polar_coordinates( Point(cos(self.angleF.radian), sin(self.angleF.radian)), pspict).measure a = numerical_approx(aI1.degree) b = numerical_approx(aF1.degree) if a > b: a = a - 360 aI2 = AngleMeasure(value_degree=a) else: aI2 = aI1 aF2 = aF1 return aI2, aF2
def get_osculating_circle(self, llam): """Return the osculating circle to the parametric curve.""" from yanntricks.src.Constructors import CircleOA P = self.get_point(llam) first = self.get_derivative(llam, 1) second = self.get_derivative(llam, 2) coefficient = (first.x**2+first.y**2) / \ (first.x*second.y-second.x*first.y) Ox = P.x - first.y * coefficient Oy = P.y + first.x * coefficient center = Point(Ox, Oy) return CircleOA(center, P)
def test_imaginary_part_point(P, epsilon=0.0001): """ Return the tuple '(isreal,P)'. The same description of 'test_imaginary_part' """ from yanntricks.src.point import Point realx, x = test_imaginary_part(P.x, epsilon) realy, y = test_imaginary_part(P.y, epsilon) on = False if realx and realy: on = True return on, Point(x, y)
def SurfaceUnderFunction(f, mx, Mx): """ Represent a surface under a function. This is a particular case of SurfaceBetweenFunctions when the second function is the y=0 axis. The function `f` becomes `self.f1` while self.f2 will be the function 0 (this is a consequence of inheritance). The function f will also be recorded as self.f. INPUT: - ``f`` - a function - ``mx,Mx`` - initial and final values EXAMPLES: .. literalinclude:: yanntricksSurfaceFunction.py .. image:: Picture_FIGLabelFigSurfaceFunctionPICTSurfaceFunction-for_eps.png .. literalinclude:: yanntricksChiSquaresQuantile.py .. image:: Picture_FIGLabelFigChiSquaresQuantilePICTChiSquaresQuantile-for_eps.png """ from yanntricks.src.NonAnalytic import NonAnalyticFunctionGraph from yanntricks.src.segment import Segment from yanntricks.src.SurfacesGraph import SurfaceBetweenLines if isinstance(f, NonAnalyticFunctionGraph): line1 = Segment(Point(mx, 0), Point(Mx, 0)) line2 = f.parametric_curve(mx, Mx) surf = SurfaceBetweenLines(line1, line2) return surf f2 = phyFunction(0) f2.nul_function = True # See 2252914222 return SurfaceBetweenFunctions(f, f2, mx=mx, Mx=Mx)
def visual_polar_coordinates(P, pspict=None): """ return the visual polar coordinates of 'P' """ if pspict is None: return P.polar_coordinates() if isinstance(pspict, list): xu = pspict[0].xunit yu = pspict[0].xunit xunits = [psp.xunit == xu for psp in pspict] yunits = [psp.yunit == yu for psp in pspict] if sum(xunits) == len(xunits) and sum(yunits) == len(yunits): xunit = xu yunit = yu else: print( "Probably more than one picture with different dilatations ..." ) raise ValueError else: xunit = pspict.xunit yunit = pspict.yunit Q = Point(xunit * P.x, yunit * P.y) return Q.polar_coordinates()
def PolarPoint(r, theta): """ return the point at polar coordinates (r,theta). INPUT: - ``r`` - the distance from origine - ``theta`` - the angle EXAMPLES:: sage: from yanntricks import * sage: print PolarPoint(2,45) <Point(sqrt(2),sqrt(2))> """ return Point(r * cos(radian(theta)), r * sin(radian(theta)))
def test_visual_length(): echo_function("test_visual_length") with SilentOutput(): pspict, fig = SinglePicture("ZZTHooTeGyMT") v = AffineVector(Point(0, 0), Point(0, sin(0.5 * pi))) w = visual_length(v, 0.1, pspict=pspict) ans = AffineVector(Point(0, 0), Point(0, 0.1)) echo_single_test("positive") assert_equal(w, ans) echo_single_test("negative") v = AffineVector(Point(0, 0), Point(0, -sin(0.5 * pi))) w = visual_length(v, 0.1, pspict=pspict) ans = AffineVector(Point(0, 0), Point(0, -0.1)) assert_equal(w, ans)
def graph(self, xvalues=None, yvalues=None, draw_points=None): """ return a graph of self with the given points INPUT: - ``xvalues`` - tuple (x,mx,My,n) interval and number of points with respect to X. - ``yvalues`` - tuple (y,my,My,n) interval and number of points with respect to Y. - ``draw_points`` - (defaulf : empty list) a list of points. If xvalues is given, then yvalues has to be given. OUTPUT: object VectorFieldGraph. EXAMPLES:: sage: from yanntricks.BasicGeometricObjects import * sage: x,y=var('x,y') sage: F=VectorField(x,y).graph(xvalues=(x,-2,2,3),yvalues=(y,-10,10,3),draw_points=[Point(100,100)]) sage: print F.draw_points[0] <Point(100,100)> sage: print len(F.draw_points) 10 """ if draw_points is None: draw_points = [] if xvalues is not None: mx = xvalues[1] Mx = xvalues[2] nx = xvalues[3] my = yvalues[1] My = yvalues[2] ny = yvalues[3] from numpy import linspace pos_x = linspace(mx, Mx, nx) pos_y = linspace(my, numerical_approx(My), ny) for xx in pos_x: for yy in pos_y: draw_points.append(Point(xx, yy)) return VectorFieldGraph(self, draw_points=draw_points)
def __init__(self, name="CAN_BE_A_PROBLEM_IF_TRY_TO_PRODUCE_EPS_OR_PDF"): r""" - `self.BB` is the bounding box for LaTeX purpose. - `self.math_BB` is the bounding box of objects that are "mathematically relevant". This bounding box does not take into account marks of points and thinks like that. This is the bounding box that is going to be used for the axes and the grid. When a graph object has a method math_bounding_box, this is the one taken into account in the math_BB here. """ from yanntricks.src.GridGraph import Grid from yanntricks.src.AxesGraph import Axes self.paths = PathsKeeper() self.name = name # for the intermediate files. # A comment. This is not used to create the picture; # the purpose is to remember a specific feature to be # tested when recompiling. self.comment = "" self.tikzfile = self.paths.create("sage_dir", f"tikz{self.name}") self.mother = None self.figure_mother = None self.language = "tikz" self.pstricks_code_list = [] self.newwriteDone = False # self.interWriteFile is redefined in MultiplePictures self.record_marks = [] self.record_bounding_box = [] self.record_draw_graph = [] self.record_draw_bb = [] self.record_force_math_bounding_box = [] # self.record_math_BB=[] # self.record_BB=[] self.counterDone = False self.newlengthDone = False self.listePoint = [] self.xunit = 1 self.yunit = 1 self.xsize = None self.ysize = None self.rotation_angle = None self.LabelSep = 1 self.BB = BoundingBox(mother=self) self.math_BB = BoundingBox(is_math=True) # self.BB and self.math_BB serve to add some objects by hand. # If you need the bounding box, use self.bounding_box() # or self.math_bounding_box() self.axes = Axes(Point(0, 0), BoundingBox(), pspict=self) self.single_axeX = self.axes.single_axeX self.single_axeY = self.axes.single_axeY self.single_axeX.pspict = self self.single_axeY.pspict = self self.draw_default_axes = False self.mx_acceptable_BB = -100 self.my_acceptable_BB = -100 self.Mx_acceptable_BB = 100 self.My_acceptable_BB = 100 self.grid = Grid(BoundingBox()) # The order of declaration is important, because # it is recorded in the Separator.number attribute. self.separator_list = init_picture_separator_list() self.entete_position = "ENTETE PSPICTURE" self.auxiliary_file = AuxFile(self.name, picture=self)
def get_point(self, x, advised=False): from yanntricks.src.point import Point return Point(self.f1(x), self.f2(x))