def fill( self, ctx: cairo.Context, rng: Generator, x1: float, y1: float, x2: float, y2: float, ): with source(ctx, self.stops[-1].to_pattern()): ctx.fill_preserve() # Orient the canvas so that our gradient goes straight in direction of +Y. gradient_angle = angle((x1, y1), (x2, y2)) - (pi / 2) with translation(ctx, x1, y1), rotation(ctx, gradient_angle), source( ctx, self.stops[0].to_pattern()): # We translated and rotated the canvas, so our gradient control # vector is now from (0, 0) straight up: grad_control_y = distance((x1, y1), (x2, y2)) # Get a bounding box of what needs to be filled: start_x, start_y, end_x, end_y = ctx.fill_extents() ctx.new_path() for cx, cy, cr in self.pattern.func( rng, (start_x, start_y), (end_x, end_y), (0, 0), (0, grad_control_y), self.dot_radius, ): ctx.arc(cx, cy, cr, 0, tau) ctx.fill()