cpmech / plotpy

Rust plotting library using Python (Matplotlib)
MIT License
65 stars 6 forks source link

Rectangle support #62

Closed lobneroO closed 2 months ago

lobneroO commented 2 months ago

In the example in issue #56 I had used rectangles in the original matplotlib. I don't think they are part of plotpy yet. In the issue's answer there is some kind of grid, but that would not satisfy my use case, as I need to position the rectangles based on the input times (y axis in the example) and stages they are associated with (x axis, string based segmented values).

Can these also be added?

cpmech commented 2 months ago

Hi, we may be able to add this. Can you share the Python code so we can see how to do this?

lobneroO commented 2 months ago

Sure. The entire repository is here: https://github.com/lobneroO/PersonalRunningOrderTool

The relevant part is in the utils.py file:

band_rectangle = patches.Rectangle([stage_names.index(stage) + x_offset, start], rectangle_width,
                                               end - start, color=col, linewidth=0.5)
axis_bl.add_patch(band_rectangle)

stage_names is an array of strings, stage is a specific string contained therein. start is a start time (i.e. a datetime type), and end is the end time accordingly. rectangle_width is a floating point value (I think these are meant to be in [0, 1], where 1 marks the entire width of a segmented x value). x_offset is also a floating point value, meant to give a slight offset between rectangles (you can see in the screenshot in #56 that the rectangles have some room to the left and right).

axis_bl is axes system of the bottom and the left axis, which are part of a (sub)plot.

cpmech commented 2 months ago

This is now available in version 1.5.0. Luckily, I already had the Canvas implemented with much of the Matplotlib Patches code.

In Canvas, we already have two text configurations: the normal text and the alternative text. Both can be independently configured. But we could also use the Text struct directly.

The syntax is a little verbose (as intended in Plotpy because I could never remember Matplotlib syntax...), but you can get it done with something like the code below (see figure also).

If necessary, you may intercalate set_... commands between draw_... commands.

// canvas
let mut canvas = Canvas::new();

// configurations
canvas
    .set_face_color("#de3163")
    .set_line_style("None")
    .set_text_color("white")
    .set_alt_text_rotation(0.0)
    .set_alt_text_color("white")
    .set_alt_text_fontsize(14.0)
    .set_text_fontsize(20.0);

// draw rectangles and text
canvas
    .draw_rectangle(0.5, 0.5, 2.0, 1.0)
    .draw_text(1.5, 1.0, "HELLO")
    .set_alt_text_align_vertical("top")
    .set_alt_text_align_horizontal("left")
    .draw_alt_text(0.5, 1.5, "123")
    .set_alt_text_align_vertical("bottom")
    .set_alt_text_align_horizontal("right")
    .draw_alt_text(2.5, 0.5, "456");

// add canvas to plot
let mut plot = Plot::new();
plot.add(&canvas);

The output:

integ_canvas_rectangle_and_text

lobneroO commented 2 months ago

Boy, this was fast. Thank you so much!