From dfe2d16d6c7ffc5e4be72ed59f38f91eed2d7d5d Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 4 Feb 2026 16:32:37 -0800 Subject: [PATCH] incorporate cylinders with gradient into main class --- cylinder_drawing_example.py | 59 ++++++++++++++++++++++++++- topologytool.py | 81 ++++++++++++++++++++++++------------- 2 files changed, 111 insertions(+), 29 deletions(-) diff --git a/cylinder_drawing_example.py b/cylinder_drawing_example.py index f6134c1..93455f1 100644 --- a/cylinder_drawing_example.py +++ b/cylinder_drawing_example.py @@ -1,6 +1,51 @@ import cairocffi as cairo import math +def draw_cylinder_at_xy(ctx: cairo.Context, x: int, y: int, cyl_width:int, cyl_height: int, top_ellipse_height: int, top_ellipse_color=(1,1,1)): + pat = cairo.LinearGradient(x, y, x + cyl_width, y) + pat.add_color_stop_rgb(0, 1, 1, 1) # White top + pat.add_color_stop_rgb(1, 0.2, 0.2, 1) # Blue bottom + + # 1. Draw Bottom Ellipse (White) + ctx.set_line_width(1.0) + ctx.save() + ctx.translate(x + cyl_width / 2, y) + ctx.scale(1, top_ellipse_height / cyl_width ) # Ellipse scale + ctx.arc(0, 0, cyl_width / 2, 0, 2 * math.pi) + ctx.restore() + + #ctx.set_source_rgb(1, 1, 1) # White + ctx.set_line_width(0.0) + ctx.set_source(pat) + ctx.fill_preserve() + #ctx.set_source_rgb(0, 0, 0) + #ctx.set_line_width(1) + ctx.stroke() + + # 2. Draw the rectangle body + ctx.set_line_width(1.0) + ctx.rectangle(x, y, cyl_width, -cyl_height) + ctx.set_source(pat) + ctx.fill() + ctx.stroke() + + # 3. Draw top Ellipse (White) + if True: + ctx.set_line_width(1.0) + ctx.save() + ctx.translate(x + cyl_width / 2, y-cyl_height) + ctx.scale(1, top_ellipse_height / (cyl_width / 2) * 0.5) # Ellipse scale + ctx.arc(0, 0, cyl_width / 2, 0, 2 * math.pi) + ctx.restore() + + ctx.set_source_rgb(top_ellipse_color[0], top_ellipse_color[1], top_ellipse_color[2]) + ctx.fill_preserve() + + # Outline for better visibility + ctx.set_source_rgb(0, 0, 0) + ctx.set_line_width(1) + ctx.stroke() + def draw_cylinders(width, height, filename): # Setup surface and context @@ -25,7 +70,7 @@ def draw_cylinders(width, height, filename): x = start_x + i * (cyl_width + spacing) # 1. Draw Side Gradient (Rect + Arc) - # Create a vertical linear gradient: White (top) to Blue (bottom) + # Create a horizontal linear gradient: White (top) to Blue (bottom) pat = cairo.LinearGradient(x, y, x+cyl_width, y) pat.add_color_stop_rgb(0, 1, 1, 1) # White top pat.add_color_stop_rgb(1, 0.2, 0.2, 1) # Blue bottom @@ -56,4 +101,14 @@ def draw_cylinders(width, height, filename): if __name__ == "__main__": - draw_cylinders(450, 300, "cylinders.png") + surface = cairo.SVGSurface("cylinder.svg", 800, 800) + ctx = cairo.Context(surface) + ctx.set_source_rgb(0, 1, 0) + ctx.paint() + + + draw_cylinder_at_xy(ctx, 100,300, 50,150,10) + draw_cylinder_at_xy(ctx, 300,300, 60,110,10, top_ellipse_color=(0.9, 0.4, 0.7)) + surface.write_to_png("cylinder.png") + print("Saved cylinder.png") + surface.finish() diff --git a/topologytool.py b/topologytool.py index f3a7678..b801f45 100644 --- a/topologytool.py +++ b/topologytool.py @@ -12,6 +12,9 @@ import pandas as pd import cairocffi as cairo import math +from cylinder_drawing_example import draw_cylinder_at_xy + + class Topology: def __init__(self, country=None, @@ -162,22 +165,55 @@ class Topology: values[r, c] = np.mean(this_array[this_array != 0]) return values + def draw_cylinder_at_xy(self, ctx: cairo.Context, x: int, y: int, cyl_width: int, cyl_height: int, + top_ellipse_height: int, top_ellipse_color=(1, 1, 1)): + pat = cairo.LinearGradient(x, y, x + cyl_width, y) + pat.add_color_stop_rgb(0, 1, 1, 1) # White top + pat.add_color_stop_rgb(1, 0.2, 0.2, 1) # Blue bottom + + # 1. Draw Bottom Ellipse (White) + ctx.set_line_width(1.0) + ctx.save() + ctx.translate(x + cyl_width / 2, y) + ctx.scale(1, top_ellipse_height / cyl_width) # Ellipse scale + ctx.arc(0, 0, cyl_width / 2, 0, 2 * math.pi) + ctx.restore() + + # ctx.set_source_rgb(1, 1, 1) # White + ctx.set_line_width(0.0) + ctx.set_source(pat) + ctx.fill_preserve() + # ctx.set_source_rgb(0, 0, 0) + # ctx.set_line_width(1) + ctx.stroke() + + # 2. Draw the rectangle body + ctx.set_line_width(1.0) + ctx.rectangle(x, y, cyl_width, -cyl_height) + ctx.set_source(pat) + ctx.fill() + ctx.stroke() + + # 3. Draw top Ellipse (White) + if True: + ctx.set_line_width(1.0) + ctx.save() + ctx.translate(x + cyl_width / 2, y - cyl_height) + ctx.scale(1, top_ellipse_height / (cyl_width / 2) * 0.5) # Ellipse scale + ctx.arc(0, 0, cyl_width / 2, 0, 2 * math.pi) + ctx.restore() + + ctx.set_source_rgb(top_ellipse_color[0], top_ellipse_color[1], top_ellipse_color[2]) + ctx.fill_preserve() + + # Outline for better visibility + ctx.set_source_rgb(0, 0, 0) + ctx.set_line_width(1) + ctx.stroke() + def create_cylinder_svg_file(self, rotation=None,file_index=None): # subfunction to draw an array, options are line y/n, fill y/n, line and fill color - def svg_draw_cylinder(svg_context: cairo.Context, x1,y1,x2,y2,gradient=None): - circlew = self.linemap_xscale / 3 - halfcirclew = circlew / 2 - svg_context.set_source_rgb(1, 1, 1) - svg_context.arc(x1,y1,circlew,0,2*math.pi) - svg_context.stroke() - - svg_context.arc(x2,y2,circlew, 0,2*math.pi) - svg_context.stroke() - - svg_context.move_to(x1-1,y1-1) - svg_context.rectangle(x1-1,y1-1,x1+1,y2+1) - svg_context.stroke() # if no refetch, check if file exists if os.path.exists(self.elevation_filename): print("Elevation file exists") @@ -220,7 +256,7 @@ class Topology: # Projects (x,y,z) -> (x+z/2, y+z/2) # max height should be no more than 3 times the distance between rows, this could be a setting def project(x, y, z): - return (x * self.linemap_xscale, y * self.linemap_yscale - (z if z > 0 else (-1.5 * self.linemap_yscale))) + return (x * self.linemap_xscale, y * self.linemap_yscale ) # return (offset[0] + x * scale + z * 2, offset[1] + y * scale + z * 2) rows, cols = values.shape @@ -229,13 +265,6 @@ class Topology: # Normalize data for visualization height (0-100) -- max is no more than three row heights norm_data = (values / max_val) * self.linemap_xscale * 3 - # Draw Lines along rows - cr.set_source_rgb(0, 0, 0) # Black lines - cr.set_line_width(self.linemap_width) - cr.set_line_cap(cairo.LINE_CAP_ROUND) - cr.set_line_join(cairo.LINE_JOIN_ROUND) - x = 0 - y = 0 if True: for r in range(rows): @@ -243,9 +272,7 @@ class Topology: for c in range(cols): z = norm_data[r,c] x,y = project(c, r, z) # now need to make array of x,y vals and pass to drawing func - draw_list.append((x,y)) - svg_draw_cylinder(cr,x,y,x,y-z) - print("***" + str(draw_list)) + draw_cylinder_at_xy(cr,x,y,self.linemap_xscale,z,self.linemap_yscale/2) #svg_draw_list(cr, draw_list, line_color="Black", fill_shape=True) @@ -376,9 +403,9 @@ def main(): print(topology.get_boundingbox()) #topology.create_svg_file() topology.create_cylinder_svg_file() - #for r in range(4*360): - #print("Rotating by [{}] degrees".format(r/4.0)) - #topology.create_svg_file(rotation=(r / 4.0), file_index=r) + for r in range(4*360): + print("Rotating by [{}] degrees".format(r/4.0)) + topology.create_cylinder_svg_file(rotation=(r / 4.0), file_index=r) sys.exit(0)