Blend Overlaps¶
In the blends chapter this notebook generates the venn diagram charts for various blend modes. You can plug in your own colors to generate your own data.
In [1]:
import json
import brushcue
from pathlib import Path
painter_dimensions = brushcue.vector2f_constant(256, 256)
stroke_width = 2
working_color_profile = brushcue.color_profile_s_r_g_b_linear()
output_color_profile = brushcue.color_profile_s_r_g_b()
ctx = brushcue.Context()
HOME = "/home/dito/dev/monorepo/writing/graphics/chapters/blend/assets"
def make_image(foreground: brushcue.Graph, background: brushcue.Graph, blend_fn) -> brushcue.Graph:
center_1 = brushcue.point2f_from_components(82, 0)
center_2 = brushcue.point2f_from_components(0, 0)
def make_blend_circles() -> brushcue.Graph:
painter_1 = brushcue.painter_new(working_color_profile)
painter_2 = brushcue.painter_new(working_color_profile)
render_style_1 = brushcue.render_style_fill_only(
brushcue.fill_solid(background)
)
render_style_2 = brushcue.render_style_fill_only(
brushcue.fill_solid(foreground)
)
instances = brushcue.transform2_to_list(brushcue.transform2_identity())
painter_1 = brushcue.painter_add_ellipse_with_render_style(painter_1, center_1, painter_dimensions, 0, render_style_1, instances)
painter_2 = brushcue.painter_add_ellipse_with_render_style(painter_2, center_2, painter_dimensions, 0, render_style_2, instances)
composition_1 = brushcue.composition_painter(painter_1)
composition_2 = brushcue.composition_painter(painter_2)
return blend_fn(composition_2,composition_1, brushcue.transform2_identity())
def make_circle_outlines() -> brushcue.Graph:
brush = brushcue.brush_solid(
brushcue.r_g_b_a_color_constant(0, 0, 0, 1),
stroke_width
)
painter_1 = brushcue.painter_new(working_color_profile)
painter_2 = brushcue.painter_new(working_color_profile)
render_style = brushcue.render_style_brush_only(brush)
instances = brushcue.transform2_to_list(brushcue.transform2_identity())
painter_1 = brushcue.painter_add_ellipse_with_render_style(painter_1, center_1, painter_dimensions, 0, render_style, instances)
painter_2 = brushcue.painter_add_ellipse_with_render_style(painter_2, center_2, painter_dimensions, 0, render_style, instances)
composition_1 = brushcue.composition_painter(painter_1)
composition_2 = brushcue.composition_painter(painter_2)
return brushcue.composition_blend_alpha(composition_1, composition_2, brushcue.transform2_identity())
blend_circles = make_blend_circles()
outlines = make_circle_outlines()
blended = brushcue.composition_blend_alpha(outlines, blend_circles, brushcue.transform2_identity())
return brushcue.composition_color_convert(blended, output_color_profile)
In [2]:
colors = [
brushcue.r_g_b_a_color_constant(1, 1, 1, 1),
brushcue.r_g_b_a_color_constant(0, 0, 0, 1),
brushcue.r_g_b_a_color_constant(1, 0, 0, 1),
brushcue.r_g_b_a_color_constant(0, 1, 1, 1),
brushcue.r_g_b_a_color_constant(0.6, 0.9, 0.4, 1),
brushcue.r_g_b_a_color_constant(0.1, 0.2, 0.4, 1)
]
def clip_channel(value):
return min(max(value, 0), 1)
def format_channel(value):
return f"{clip_channel(value):.2f}".rstrip("0").rstrip(".")
def format_channels(color):
return [format_channel(color[0]), format_channel(color[1]), format_channel(color[2])]
def math_add(c1, c2):
return format_channels((c1[0] + c2[0], c1[1] + c2[1], c1[2] + c2[2]))
def math_subtract(c1, c2):
return format_channels((c1[0] - c2[0], c1[1] - c2[1], c1[2] - c2[2]))
def math_multiply(c1, c2):
return format_channels((c1[0] * c2[0], c1[1] * c2[1], c1[2] * c2[2]))
def math_max(c1, c2):
return format_channels((max(c1[0], c2[0]), max(c1[1], c2[1]), max(c1[2], c2[2])))
def math_min(c1, c2):
return format_channels((min(c1[0], c2[0]), min(c1[1], c2[1]), min(c1[2], c2[2])))
blend_fns = [
("add", brushcue.composition_blend_add, math_add),
("subtract", brushcue.composition_blend_subtract, math_subtract),
("multiply", brushcue.composition_blend_multiply, math_multiply),
("max", brushcue.composition_blend_max, math_max),
("min", brushcue.composition_blend_min, math_min),
]
In [ ]:
# Generates the Venn Diagrams
for (foreground_idx, foreground) in enumerate(colors):
for (background_idx, background) in enumerate(colors):
for (name, blend_fn, math_fn) in blend_fns:
composition = make_image(
foreground,
background,
blend_fn
)
result = composition.execute(ctx)
output_bytes = result.as_composition().to_image_bytes(ctx)
path = Path(f"{HOME}/{name}/{foreground_idx}/{background_idx}.png")
foreground_color = foreground.execute(ctx).as_rgba_color()
background_color = background.execute(ctx).as_rgba_color()
computation = {
"foreground": format_channels(foreground_color),
"background": format_channels(background_color),
"result": math_fn(foreground_color, background_color),
}
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "wb") as f:
f.write(bytes(output_bytes))
with open(f"{HOME}/{name}/{foreground_idx}/{background_idx}.json", "w") as f:
json.dump(computation, f)
new pipeline: painter_Elliptical_fill_solid_no_brush_sample_true new pipeline: blend_Add_rgba16float_compute_bg_false_fg_false new pipeline: painter_Elliptical_no_fill_brush_solid_sample_true new pipeline: blend_Alpha_rgba16float_compute_bg_false_fg_false new pipeline: color_transformer_shader_rgba16float_compute_13509697929820575703_inarr_false new pipeline: blend_Subtract_rgba16float_compute_bg_false_fg_false new pipeline: blend_Multiply_rgba16float_compute_bg_false_fg_false new pipeline: blend_Max_rgba16float_compute_bg_false_fg_false new pipeline: blend_Min_rgba16float_compute_bg_false_fg_false
In [ ]:
# Generates the Individual Swatches
for (idx, color) in enumerate(colors):
painter = brushcue.painter_new(working_color_profile)
render_style = brushcue.render_style_brush_and_fill(
brushcue.brush_solid(brushcue.r_g_b_a_color_constant(0, 0, 0, 1), stroke_width),
brushcue.fill_solid(color)
)
instances = brushcue.transform2_to_list(brushcue.transform2_identity())
painter = brushcue.painter_add_ellipse_with_render_style(painter, brushcue.point2f_from_components(0, 0), painter_dimensions, 0, render_style, instances)
composition = brushcue.composition_painter(painter)
composition = brushcue.composition_color_convert(composition, output_color_profile)
result = composition.execute(ctx)
output_bytes = result.as_composition().to_image_bytes(ctx)
path = Path(f"{HOME}/swatches/color_{idx}.png")
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "wb") as f:
f.write(bytes(output_bytes))
rgba = color.execute(ctx).as_rgba_color()
color_json = {
"color": format_channels(rgba)
}
with open(f"{HOME}/swatches/color_{idx}.json", "w") as f:
json.dump(color_json, f)
In [ ]: