def draw_ground_gradient(): """Base ground gradient (debug, would be removed lately).""" global ctx ctx_old = ctx ctx = ctx_ground ctx.set_operator(cairo.Operator.SOURCE) pattern = cairo.LinearGradient(0, 0, WIDTH, 0) pattern.add_color_stop_rgba(0, *BASE_COLORS[0]) pattern.add_color_stop_rgba(0.6, *blend(BASE_COLORS[0], BASE_COLORS[1], 0.3)) pattern.add_color_stop_rgba(1, *BASE_COLORS[1]) ctx.set_source(pattern) ctx.rectangle(0, HEIGHT * 0.8, WIDTH, HEIGHT) ctx.fill() ctx.set_operator(cairo.Operator.DEST_IN) pattern = cairo.LinearGradient(0, HEIGHT * 0.8, 0, HEIGHT) pattern.add_color_stop_rgba(0, 0, 0, 0, 0) pattern.add_color_stop_rgba(0.5, 1, 1, 1, 0.3) pattern.add_color_stop_rgba(1, 1, 1, 1, 1) ctx.set_source(pattern) ctx.rectangle(0, HEIGHT * 0.8, WIDTH, HEIGHT) ctx.fill() ctx = ctx_old ctx.set_source_surface(surface_ground) ctx.rectangle(0, 0, WIDTH, HEIGHT) ctx.fill()
def draw_sky(): """Draw the sky background with the sun.""" # horizon gradient pattern = cairo.LinearGradient(0, 0, 0, HEIGHT) pattern.add_color_stop_rgba(0, *lighten(BASE_COLORS[3], 0.05, 1)) pattern.add_color_stop_rgba(0.4, *BASE_COLORS[3]) pattern.add_color_stop_rgba(1, *lighten(BASE_COLORS[2], 0)) ctx.set_source(pattern) ctx.rectangle(0, 0, WIDTH, HEIGHT) ctx.fill() # right side gradient color_from = blend(BASE_COLORS[0], BASE_COLORS[1], 0.5) pattern = cairo.LinearGradient(0, HEIGHT, WIDTH * (2 / 3), 0) pattern.add_color_stop_rgba(0, *opaque(color_from, -0.2)) pattern.add_color_stop_rgba(1, *opaque(color_from, -1)) ctx.set_source(pattern) ctx.rectangle(0, 0, WIDTH, HEIGHT) ctx.fill() # the sun sun_x, sun_y = WIDTH * 0.5, HEIGHT * 0.33 sun_color = lighten(BASE_COLORS[4], 0.5) r = HEIGHT pattern = cairo.RadialGradient(sun_x, sun_y, 0, sun_x, sun_y, r) pattern.add_color_stop_rgba(0, *lighten(BASE_COLORS[4], 0.5)) for stop, dark in np.linspace((0, 0), (1, -1)): stop = stop ** (stop * 4.6 + 1) pattern.add_color_stop_rgba(stop, *opaque(sun_color, dark)) ctx.set_source(pattern) ctx.rectangle(0, 0, WIDTH, HEIGHT) ctx.fill()
def draw_ground(): """Draw the ground with buildings on it.""" draw_ground_gradient() cam_y = HEIGHT * 0.5 num_x, num_y = 50, 50 density = 0.8 points = generate_ground_points(WIDTH * 1.5, HEIGHT * 3, num_x, num_y, cam_y) color_magic = lighten(saturate(BASE_COLORS[0], 0.1), 0.8) for i, (x, y) in enumerate(points): # calculate colors from x, y coordinate blend_x = (i // num_x) / num_x blend_y = (i % num_y) / num_y * 2 - 1 blend_y = min(0.5, max(-0.5, blend_y)) + 0.5 color_l = blend(color_magic, BASE_COLORS[0], blend_x) color_r = ( blend(BASE_COLORS[2], BASE_COLORS[1], blend_x / 0.6) if blend_x < 0.6 else blend(BASE_COLORS[1], BASE_COLORS[0], blend_x / 0.6 - 1) ) color = blend(color_r, color_l, blend_y) color = opaque(color, (blend_x - 1) * 0.8 + 0.2) if i // num_y < num_y - 2 and i % num_x < num_x - 2: # draw solid ground face_coords = ( points[i], points[i + 1], points[i + num_x + 1], points[i + num_x] ) draw_poly(ctx, face_coords, color, outline_darken=-0.1) random_height = abs(random.gauss(0.5, 0.2) - 0.5) random_width = abs(random.gauss(0.5, 0.1)) # draw a building if random.random() < density * max(0.3, 1 - blend_x): w, h = perspective_point( -WIDTH * random_width / 7, -HEIGHT * random_height * 0.8, 0, focal_l * WIDTH * 1.5, (1 - blend_x) * HEIGHT * 15 ) draw_house(*points[i], w, h, color)
def draw_wall(x, y, width, height, num_horiz, num_vert, color_main, color_dark=None): """Draw a wall of bricks.""" color_dark = color_dark or color_main # build a grid brick_width = width / num_horiz / 2 brick_height = height / num_vert x_coords = [ x + i * brick_width + (random.random() - 0.5) * brick_width * 0.2 for i in range(num_horiz * 2 + 2) ] y_coords = [ y + i * brick_height + (random.random() - 0.5) * brick_height * 0.01 for i in range(num_vert + 1) ] # draw bricks for i, (y1, y2) in enumerate(zip(y_coords[:-1], y_coords[1:])): for j in range(num_horiz): x1, x2 = x_coords[j * 2 + i % 2], x_coords[(j + 1) * 2 + i % 2] coords = ((x1, y1), (x2 - 1, y1), (x2 - 1, y2 - 1), (x1, y2 - 1)) blend_ratio = abs(random.gauss(0.5, 0.2) - 0.5) cur_color = blend(color_main, color_dark, blend_ratio) draw_poly(ctx, coords, cur_color, outline_darken=0.2)
def draw_altar(x_p, y_p, w_p, h_p): """Draw far wall with an "altar". """ # inner arc color = lighten(saturate(BASE_COLORS[1], -0.1), 0.1) ctx.set_source_rgba(*color) ctx.rectangle(x_p + w_p / 2, y_p + h_p * 2, -w_p, -h_p) ctx.fill() # top brick rays rev_ang = (h_p > 0) * math.pi rev_blend = 1 + (h_p > 0) * 1.5 rays_color = blend(BASE_COLORS[1], BASE_COLORS[3], 0.5 / rev_blend) ctx.set_source_rgba(*rays_color) ctx.set_line_width(1.5) ctx.arc(x_p, y_p + h_p, abs(w_p) / 32, -math.pi + rev_ang, 0 - rev_ang) ctx.stroke() for i in range(13): phi = i / 12 * math.pi x1, y1 = polar2vec(-w_p / 20, phi + rev_ang) x2, y2 = polar2vec(-w_p / 4, phi + rev_ang) ctx.move_to(x1 + x_p, y1 + y_p + h_p) ctx.line_to(x2 + x_p, y2 + y_p + h_p) ctx.set_line_width(1) ctx.stroke() # top altar shadow pattern = cairo.RadialGradient(x_p, y_p + h_p, w_p * 0.13, x_p, y_p + h_p, w_p / 4) pattern.add_color_stop_rgba(0, 0, 0, 0, 0) pattern.add_color_stop_rgba(1, 0, 0, 0, 0.9) pattern.add_color_stop_rgba(1, 0, 0, 0, 0) ctx.set_source(pattern) ctx.rectangle(x_p + w_p / 2, y_p + h_p * 2, -w_p, -h_p) ctx.fill() br = 0.405 # outer arc color = lighten(BASE_COLORS[2], 0) prev_paths_l, prev_paths_r = draw_verona_arc( x_p, y_p, w_p, h_p, saturate(lighten(color, 0.12), -0.1), saturate(lighten(color, -0.5), -0.1), 0.042, (0, 1), (0.23, 1.78), brick_ratio=1 - br, brick_depth=0, ) # far wall draw_wall( x_p - w_p / 2, y_p - max(0, -h_p), w_p, abs(h_p), 7, 8, lighten(saturate(BASE_COLORS[2], -0.42), -0.08), BASE_COLORS[1], ) # bottom brick color = lighten(saturate(BASE_COLORS[1], -0.1), 0.1) ctx.set_source_rgba(*color) ctx.rectangle(x_p + w_p * br / 2, y_p + h_p - h_p / 2, -w_p * br, -h_p / 8) ctx.fill() # side altar shadows pattern = cairo.LinearGradient(x_p - w_p * br / 2, y_p, x_p + w_p * br / 2, y_p) pattern.add_color_stop_rgba(0, 0, 0, 0, 0) pattern.add_color_stop_rgba(0, 0, 0, 0, 0.7) pattern.add_color_stop_rgba(0.2, 0, 0, 0, 0) pattern.add_color_stop_rgba(0.8, 0, 0, 0, 0) pattern.add_color_stop_rgba(1, 0, 0, 0, 0.7) pattern.add_color_stop_rgba(1, 0, 0, 0, 0) ctx.set_source(pattern) ctx.rectangle(x_p + w_p / 2, y_p + h_p * 1.005, -w_p, -h_p * 0.63) ctx.fill()
def draw_verona_arc(x, y, width, height, color_main, color_dark, rand_amount=0.25, grad_main=(0, 0), grad_depth=(-0.18, -0.18), brick_ratio=0.27, brick_depth=0.5, prev_paths=(None, None), prev_colors=(None, None)): """Draw a single arc in Verona style.""" paths_r = list(zip(*generate_arc_paths(x, y, width / 2, height, 8, 4, 3, brick_ratio))) paths_l = list(zip(*generate_arc_paths(x, y, -width / 2, height, 8, 4, 3, brick_ratio))) # ceiling to the previous arc prev_color_main, prev_color_dark = prev_colors draw_ceiling( prev_color_main, prev_color_dark, paths_l, paths_r, *prev_paths ) # a lamp draw_lamp(x, y + height * 1.56, width / 16, -height * 0.5, prev_color_main, prev_color_dark) for paths in (paths_r, paths_l): # left and right half-arcs for i, coords in enumerate(zip(paths[:-2], paths[1:-1])): (((x1o, y1o), (x1i, y1i), (x1d, y1d)), ((x2o, y2o), (x2i, y2i), (x2d, y2d))) = coords coords = [(x1o, y1o), (x2o, y2o), (x2i, y2i), (x2d, y2d), (x1d, y1d), (x1i, y1i)] if brick_depth == 0: del coords[3:5] grad_ratio = (grad_main[1] - grad_main[0]) * i / (len(paths) - 2) + grad_main[0] rand_ratio = random.gauss(grad_ratio, rand_amount) blend_ratio = max(0, min(1, (rand_ratio))) cur_color = blend(color_main, color_dark, blend_ratio) draw_poly(ctx, coords, cur_color, outline_darken=0.29) # brick depth if brick_depth > 0: coords = ((x1i, y1i), (x2i, y2i), (x2d, y2d), (x1d, y1d)) shadow_ratio = (grad_depth[1] - grad_depth[0]) * i / (len(paths) - 2) + grad_depth[0] cur_color = lighten(cur_color, shadow_ratio) draw_poly(ctx, coords, cur_color, outline_darken=0.5) # top brick coords = ( paths_r[-1][0], paths_r[-2][0], paths_r[-2][1], paths_r[-2][2], (paths_r[-1][0][0], paths_r[-2][2][1] + (paths_r[-2][2][1] - paths_r[0][2][1]) * 0.02), paths_l[-2][2], paths_l[-2][1], paths_l[-2][0], ) cur_color = blend(color_main, color_dark, grad_main[1]) draw_poly(ctx, coords, cur_color, outline_darken=0.29) # top brick depth coords = ( (paths_r[-1][0][0], paths_r[-2][1][1] + (paths_r[-2][1][1] - paths_r[0][1][1]) * 0.02), paths_r[-2][1], paths_r[-2][2], (paths_r[-1][0][0], paths_r[-2][2][1] + (paths_r[-2][2][1] - paths_r[0][2][1]) * 0.02), paths_l[-2][2], paths_l[-2][1], ) if brick_depth > 0: draw_poly(ctx, coords, lighten(cur_color, grad_depth[1]), outline_darken=0.29) return paths_l, paths_r