def __init__(self, curve1, curve2): """ Give the graph of the surface between the two lines. The lines are needed to have a starting and ending point that will be joined by straight lines. """ from yanntricks.src.segment import Segment # By convention, the first line goes from left to right and the second one to right to left. ObjectGraph.__init__(self, self) if curve1.I.x > curve1.F.x: curve1 = curve1.reverse() if curve2.I.x > curve2.F.x: curve2 = curve2.reverse() self.curve1 = curve1 self.curve2 = curve2 self.I1 = curve1.I self.I2 = curve2.I self.F1 = curve1.F self.F2 = curve2.F self.Isegment = Segment(self.I1, self.I2) self.Fsegment = Segment(self.F1, self.F2)
def projection(self, seg, direction=None, advised=False): """ Return the projection of the point on the given segment. INPUT: - ``seg`` - a segment - ``direction`` - (default=None) a vector. If given, we use a projection parallel to `vector` instead of the orthogonal projection. OUTPUT: a point. """ from yanntricks.src.SingleAxeGraph import SingleAxe from yanntricks.src.affine_vector import AffineVector from yanntricks.src.segment import Segment from yanntricks.src.Utilities import Intersection if isinstance(seg, AffineVector): seg = seg.segment if isinstance(seg, SingleAxe): seg = seg.segment() if direction is None: if seg.is_vertical: direction = Segment(self, self + (1, 0)) elif seg.is_horizontal: direction = Segment(self, self + (0, 1)) else: direction = Segment(self, self + (1, -1 / seg.slope)) P = Intersection(seg, direction)[0] if advised: P._advised_mark_angle = seg.angle().degree + 90 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 __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 CircularSector(center, radius, a, b): from yanntricks.src.segment import Segment circle = Circle(center, radius) P = circle.get_point(a) Q = circle.get_point(b) l1 = Segment(circle.center, P) l2 = circle.graph(a, b) l3 = Segment(Q, circle.center) return CustomSurface(l1, l2, l3)
def getEdge(self, pos): from yanntricks.src.segment import Segment if pos == "NORTH": return Segment(self.getVertex("NW"), self.getVertex("NE")) if pos == "SOUTH": return Segment(self.getVertex("SW"), self.getVertex("SE")) if pos == "EAST": return Segment(self.getVertex("NE"), self.getVertex("SE")) if pos == "WEST": return Segment(self.getVertex("NW"), self.getVertex("SW"))
def __init__(self, seg, dist=0.1): try: self.segment = seg.segment except AttributeError: self.segment = seg self.dist = dist self.delta = seg.rotation(-90).fix_size(self.dist) self.mseg = seg.translate(self.delta) Segment.__init__(self, self.mseg.I, self.mseg.F) self.mI = self.mseg.I self.mF = self.mseg.F
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 CircleAB(A, B): """ return a circle with diameter [AB] """ from yanntricks.src.segment import Segment center = Segment(A, B).midpoint() return CircleOA(center, A)
def get_tangent_segment(self, llam): """ Return a tangent segment of length 2 centred at the given point. It is essentially two times get_tangent_vector. """ from yanntricks.src.segment import Segment v = self.get_tangent_vector(llam) mv = -v return Segment(mv.F, v.F)
def __init__(self, curve1, curve2, interval1=None, interval2=None, reverse1=False, reverse2=True): from yanntricks.src.segment import Segment # TODO: I think that the parameters reverse1 and reverse2 are no more useful # since I enforce the condition curve1 : left -> right by hand. ObjectGraph.__init__(self, self) self.curve1 = curve1 self.curve2 = curve2 #self.f1=self.curve1 # TODO: Soon or later, one will have to fusion these two #self.f2=self.curve2 self.mx1 = interval1[0] self.mx2 = interval1[1] self.Mx1 = interval2[0] self.Mx2 = interval2[1] for attr in [self.mx1, self.mx2, self.Mx1, self.Mx2]: if attr == None: raise TypeError( "At this point, initial and final values have to be already chosen" ) self.curve1.llamI = self.mx1 self.curve1.llamF = self.Mx1 self.curve2.llamI = self.mx2 self.curve2.llamF = self.Mx2 self.draw_Isegment = True self.draw_Fsegment = True self.Isegment = Segment(self.curve2.get_point(self.mx2, advised=False), self.curve1.get_point(self.mx1, advised=False)) self.Fsegment = Segment(self.curve1.get_point(self.Mx1, advised=False), self.curve2.get_point(self.Mx2, advised=False)) self.add_option("fillstyle=vlines") self.parameters.color = None
def get_tangent_segment(self, theta): """ Return a tangent segment at point (x,f(x)). The difference with self.get_tangent_vector is that self.get_tangent_segment returns a segment that will be symmetric. The point (x,f(x)) is the center of self.get_tangent_segment. """ from yanntricks.src.segment import Segment v = self.get_tangent_vector(theta) mv = -v return Segment(mv.F, v.F)
def segment(self, projection=False, pspict=None): # pylint:disable=unused-argument if self.mx == 0 and self.Mx == 0: # I think that we only pass here in order either to do # a projection either to create an initial bounding box. # If xunit or yunit are very low, then returning something like # Segment(self.C-self.base.visual_length(1,pspict=pspict), # self.C+self.base.visual_length(1,pspict=pspict)) # causes bounding box to be too large. # This is why I return a small segment. if projection: return Segment(self.C, self.C.translate(self.base)) pt_I = self.C.translate(-self.base.normalize(1)) pt_F = self.C.translate(self.base.normalize(1)) return Segment(pt_I, pt_F) # The axes have to cross at (0,0) if self.mx > 0: self.mx = 0 return Segment(self.C.translate(self.mx*self.base), self.C.translate(self.Mx*self.base))
def norm(self): """ Return the norm of the segment between (0,0) and self. This is the radial component in polar coordinates. EXAMPLES:: sage: from yanntricks import * sage: Point(1,1).norm() sqrt(2) sage: Point(-pi,sqrt(2)).norm() sqrt(pi^2 + 2) """ from yanntricks.src.segment import Segment return Segment(Point(0, 0), self).length
def action_on_pspict(self, pspict): from yanntricks.src.Constructors import Circle from yanntricks.src.segment import Segment if self.denominator == self.numerator: cs = Circle(self.center, self.radius) cs.parameters.filled() cs.parameters.fill.color = "lightgray" l = [cs] else: import numpy l = [self.circular_sector()] for k in numpy.linspace(0, 360, self.denominator, endpoint=False): s = Segment(self.circle.get_point(k), self.center) s.parameters.style = "dashed" l.append(s) l.append(self.circle) pspict.DrawGraphs(l)
def action_on_pspict(self, pspict): from yanntricks.src.segment import Segment # self.intersection is the point where the angle is located. P1 = self.inter_point(self.intersection, self.d1.F, self.n1, pspict) P2 = self.inter_point(self.intersection, self.d2.F, self.n2, pspict) Q = P1 + P2 - self.intersection l1 = Segment(Q, P1) l2 = Segment(Q, P2) l1.parameters = self.parameters.copy() l2.parameters = self.parameters.copy() pspict.DrawGraphs(l1, l2)
def graduation_bars(self, pspict): """ Return the list of bars that makes the graduation of the axes By default, it is one at each multiple of self.base. If an user-defined axes_unit is given, then self.base is modified. This function also enlarges the axe by half a *visual* centimeter. """ # bars_list contains in the same time marks # (for the numbering) and segments (for the bars itself) if not self.graduation: return [] bars_list = [] bar_angle = SR(self.mark_angle).n(digits=7) # Latex does not accept # too much digits. for x, symbol in self.axes_unit.place_list(self.mx, self.Mx, self.Dx, self.mark_origin): P = (x*self.base).F if self.numbering: mark_angle = self.mark_angle if self.segment().is_horizontal: position = "N" mark_angle = None if self.segment().is_vertical: position = "E" mark_angle = None # The 0.2 here is hard coded in Histogram, see 71011299 m = P.get_mark(0.2, mark_angle, symbol, pspict=pspict, position=position) bars_list.append(m) a = visual_polar(P, 0.1, bar_angle, pspict) b = visual_polar(P, 0.1, bar_angle+180, pspict) seg = Segment(a, b) bars_list.append(seg) return bars_list
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 __init__(self, points_list): ObjectGraph.__init__(self, self) self.edges = [] self.vertices = points_list self.points_list = self.vertices for i in range(0, len(self.points_list)): segment = Segment( self.points_list[i], self.points_list[(i + 1) % len(self.points_list)]) self.edges.append(segment) self.draw_edges = True self.independent_edge = False self.parameters = None from yanntricks.src.parameters.Parameters import Parameters from yanntricks.src.parameters.HatchParameters import HatchParameters from yanntricks.src.parameters.FillParameters import FillParameters self.edges_parameters = Parameters(self) self.hatch_parameters = HatchParameters() self.fill_parameters = FillParameters() self._hatched = False self._filled = False
def action_on_pspict(self, pspict=None): from yanntricks.src.segment import Segment from yanntricks.src.Constructors import CustomSurface from yanntricks.src.Exceptions import ShouldNotHappenException c1 = self.curve1.graph(self.mx1, self.Mx1) c2 = self.curve2.graph(self.mx2, self.Mx2) # By convention, the first line goes from left to right # and the second one to right to left. # The same is followed in SurfaceBetweenLines if c1.I.x > c1.F.x: c1 = c1.reverse() if c2.I.x < c2.F.x: c2 = c2.reverse() reIsegment = Segment(c2.F, c1.I) reFsegment = Segment(c1.F, c2.I) reIsegment.parameters = self.Isegment.parameters reFsegment.parameters = self.Fsegment.parameters if self.parameters._filled or self.parameters._hatched: custom = CustomSurface(c1, reFsegment, c2, reIsegment) custom.parameters = self.parameters.copy() pspict.DrawGraphs(custom) else: raise ShouldNotHappenException( "You are speaking of a surface but you don't want neither to fill it neither to hatch it ?" ) if self.parameters.color != None: self.Isegment.parameters.color = self.parameters.color self.Fsegment.parameters.color = self.parameters.color self.curve1.parameters.color = self.parameters.color self.curve2.parameters.color = self.parameters.color pspict.DrawGraphs(self.curve1, self.curve2) if self.draw_Isegment: pspict.DrawGraphs(reIsegment) if self.draw_Fsegment: pspict.DrawGraphs(reFsegment)
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 RightAngleAOB(A, O, B, n1=0, n2=1, r=0.3): """ return the right angle between Segment(A,O) and Segment(O,B) """ from yanntricks.src.segment import Segment return RightAngle(Segment(A, O), Segment(O, B), n1, n2, r)
def S(self): from yanntricks.src.segment import Segment return Segment(self.getVertex("SW"), self.getVertex("SE")).midpoint()
class AffineVector(ObjectGraph): """ Describe an affine vector. An affine vector is a vector whose origin is not specifically (0,0). """ def __init__(self, I, F): ObjectGraph.__init__(self, self) self.I = I self.F = F self.segment = Segment(self.I, self.F) @lazy_attribute def Dx(self): return self.F.x - self.I.x @lazy_attribute def Dy(self): return self.F.y - self.I.y @lazy_attribute def is_horizontal(self): return self.segment.is_horizontal @lazy_attribute def is_vertical(self): return self.segment.is_vertical @lazy_attribute def slope(self): return self.segment.slope @lazy_attribute def length(self): """ Return a numerical approximation of the length. """ return self.segment.length def fix_visual_size(self, l, xunit=None, yunit=None, pspict=None): s = self.segment.fix_visual_size(l, xunit, yunit, pspict) return AffineVector(s.I, s.F) def exact_length(self): return self.segment.exact_length def angle(self): return self.segment.angle() def numerical_approx(self): from yanntricks.src.Constructors import AffineVector I = Point(numerical_approx(self.I.x), numerical_approx(self.I.y)) F = Point(numerical_approx(self.F.x), numerical_approx(self.F.y)) return AffineVector(I, F) def orthogonal(self): from yanntricks.src.Constructors import AffineVector ortho_seg = self.segment.orthogonal() I = ortho_seg.I F = ortho_seg.F return AffineVector(I, F) def rotation(self, angle): from yanntricks.src.Constructors import AffineVector s = self.segment.rotation(angle) return AffineVector(s.I, s.F) def projection(self, seg): """ Return the projection of 'self' on the segment 'seg' (you can also pass a vector). """ from yanntricks.src.Constructors import AffineVector I = self.I.projection(seg) F = self.F.projection(seg) return AffineVector(I, F) ## # Return the decomposition of `self` into a `v`-component and # a normal (to v) component. # # INPUT: # # - ``v`` - a segment or a vector # # OUTPUT: # # A tuple : # - the first element is the orthogonale component # - the second element is the parallel component # # NOTE: # # The result does not depend on `v`, but only on the direction of `v`. 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 inner_product(self, other): from yanntricks.src.Utilities import inner_product return inner_product(self, other) def copy(self): return AffineVector(self.I, self.F) def translate(self, v): return AffineVector(self.I.translate(v), self.F.translate(v)) def advised_mark_angle(self, pspict=None): return self.segment.advised_mark_angle(pspict) def midpoint(self, advised=True): return self.segment.midpoint(advised) @copy_parameters def fix_origin(self, P): """ Return the affine vector that is equal to 'self' but attached to point P. """ return AffineVector(P, Point(P.x + self.Dx, P.y + self.Dy)) def normalize(self, l=1): """ Return a new affine vector with - same starting point - same direction (if 'l' is negative, change the direction) - size l By default, normalize to 1. """ L = self.length if L < 0.001: # epsilon print("This vector is too small to normalize. I return a copy.") return self.copy() return (l * self).__div__(L) def _bounding_box(self, pspict): return self.segment.bounding_box(pspict=pspict) def __str__(self): return "<AffineVectorGraph I=%s F=%s>" % (str(self.I), str(self.F)) # \brief put the vector `other` on the end of `self` # # This is the usual vector sum, but here we accept two vector that have # not the same initial point. # # \return a vector with same initial point as `self`. def extend(self, other): I = self.I F = self.F.translate(other.Dx, other.Dy) return AffineVector(I, F) # \brief add two vectors based on the same point. # # The function checks if the vectors `self` and `other` are based # on the same point. Sometimes, it causes strange problems. When # one of the coordinates has the form # a=3.00000000000000*cos(0.111111111111111*pi) # Checking equalities with that kind of numbers causes # OverflowError: Python int too large to convert to C long # # If you know that `v1` and `v2` have the same origin and want to sum them # you can also do `v1.extend(v2)` def __add__(self, other): from yanntricks.src.Constructors import Vector if isinstance(other, tuple): if len(other) == 2: I = self.I Dx = self.Dx + other[0] Dy = self.Dy + other[1] return AffineVector(self.I, self.I.translate(Vector(Dx, Dy))) if other.I != self.I: raise OperationNotPermitedException("You can only add vectors\ with same base point.") I = self.I Dx = self.Dx + other.Dx Dy = self.Dy + other.Dy return AffineVector(self.I, self.I.translate(Vector(Dx, Dy))) def __sub__(self, other): return self + (-other) def __mul__(self, coef): I = self.I nx = self.I.x + self.Dx * coef ny = self.I.y + self.Dy * coef F = Point(nx, ny) return AffineVector(I, F) def __rmul__(self, coef): return self * coef def __truediv__(self, coef): return self * (1 / coef) def __div__(self, coef): # The real division is __truedev__. This one is # for legacy. return self / coef def __rdiv__(self, coef): return self * (1 / coef) 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 __eq__(self, other): if self.I != other.I: return False if self.F != other.F: return False return True def is_almost_equal(self, other): if not self.I.is_almost_equal(other.I): return False if not self.F.is_almost_equal(other.F): return False return True def __ne__(self, other): return not self == other def mark_point(self, pspict=None): return self.F.copy() def tikz_code(self, pspict=None): params = self.params(language="tikz") params = params + ",->,>=latex" I_coord = self.I.coordinates(digits=5, pspict=pspict) F_coord = self.F.coordinates(digits=5, pspict=pspict) a = "\draw [{0}] {1} -- {2};".format(params, I_coord, F_coord, pspict=pspict) return a def latex_code(self, language=None, pspict=None): if self.parameters.style == "none": return "" if language == "tikz": return self.tikz_code(pspict=pspict)
def __init__(self, I, F): ObjectGraph.__init__(self, self) self.I = I self.F = F self.segment = Segment(self.I, self.F)
class SurfaceBetweenLines(ObjectGraph): def __init__(self, curve1, curve2): """ Give the graph of the surface between the two lines. The lines are needed to have a starting and ending point that will be joined by straight lines. """ from yanntricks.src.segment import Segment # By convention, the first line goes from left to right and the second one to right to left. ObjectGraph.__init__(self, self) if curve1.I.x > curve1.F.x: curve1 = curve1.reverse() if curve2.I.x > curve2.F.x: curve2 = curve2.reverse() self.curve1 = curve1 self.curve2 = curve2 self.I1 = curve1.I self.I2 = curve2.I self.F1 = curve1.F self.F2 = curve2.F self.Isegment = Segment(self.I1, self.I2) self.Fsegment = Segment(self.F1, self.F2) def _bounding_box(self, pspict=None): bb = BoundingBox() bb.append(self.curve1, pspict) bb.append(self.curve2, pspict) return bb def _math_bounding_box(self, pspict): return self.bounding_box(pspict) def latex_code(self, language=None, pspict=None): from yanntricks.src.segment import Segment a = [] c1 = self.curve1 c2 = self.curve2.reverse() custom = CustomSurface(c1, self.Fsegment, c2, self.Isegment) self.parameters.add_to( custom.parameters ) # This curve is essentially dedicated to the colors custom.options = self.options a.append("%--- begin of Surface between lines ---") a.append("% Custom surface") a.append(custom.latex_code(language=language, pspict=pspict)) a.append("% Curve 1") a.append(self.curve1.latex_code(language=language, pspict=pspict)) a.append("% Curve 2") a.append(self.curve2.latex_code(language=language, pspict=pspict)) a.append("% Isegment") a.append(self.Isegment.latex_code(language=language, pspict=pspict)) a.append("% Fsegment") a.append(self.Fsegment.latex_code(language=language, pspict=pspict)) a.append("%--- end of Surface between lines ---") return "\n".join(a)
def point_to_box_intersection(P, box, pspict=None): """ The intersection between the line from the given point and the center of the given box. P : a point box : a box, which means a duck which has attributes `xmin`, `xmax`, `ymin`, `ymax` Consider the line from `P` to the center of the box and return the intersection points. return a list of `Point`. - The list always contains exactly 2 points - They are sorted by order of distance to `P` """ from yanntricks.src.point import Point from yanntricks.src.segment import Segment A = Point(box.xmin, box.ymin) B = Point(box.xmax, box.ymin) C = Point(box.xmax, box.ymax) D = Point(box.xmin, box.ymax) center = (A + B + C + D) / 4 line = Segment(P, center) edges = [Segment(A, B), Segment(B, C), Segment(C, D), Segment(D, A)] inter = [] for ed in edges: c = Intersection(line, ed) if len(c) > 0: S = c[0] # We deal with the case in which the line travers the corner. # In this case, the line passes trough the other one. if S == A: inter = [A, C] if S == B: inter = [B, D] if S == C: inter = [A, C] if S == D: inter = [B, D] # The last two tests are to know if S lies # between ed.I and ed.F # We use numerical approximations in order to avoid some # OverflowError: Python int too large to convert to C long elif numerical_approx((S.x - ed.I.x) * (S.x - ed.F.x)) < 0: inter.append(S) elif numerical_approx((S.y - ed.I.y) * (S.y - ed.F.y)) < 0: inter.append(S) if len(inter) == 2: inter.sort(key=lambda Q: distance_sq(Q, P, numerical=True)) if pspict: for i, S in enumerate(inter): S.put_mark(0.2, angle=None, added_angle=0, text=str(i), pspict=pspict) pspict.DrawGraphs(inter, line, center, box) return inter