Indigo icon indicating copy to clipboard operation
Indigo copied to clipboard

suggestion of calculating aabb of fontsDrawText when estimating size

Open lch32111 opened this issue 2 years ago • 1 comments

Hi. I have a suggestion of calculating Axis-Aligned Bounding Box(AABB) in the function of RenderContext::fontDrawText. I don't know if this would be useful for this project or not because I'm working on the feature that Indigo does not support now. I've got to know this problem while working on my private project with this Indigo library. If you think this problem doesn't matter, then you can skip this.

I started to find that Indigo does not calculate correctly the dimension of rendering image in the estimateSize() logic when I started to put the feature of rotating atom labels. I put the code that changes the cairo's current transform matrix with my rotation code in MoleculeRenderInternal::_drawAtom(const AtomDesc& desc) before drawing textitems. I rotates the label around its AtomDesc's pos variable like this:

cairo_translate(_cr, posx, posy);
cairo_rotate(_cr, angle_radian);
cairo_translate(_cr, -posx, -posy);

The calculated objSize in RenderSingle was often wrong to render, so some texts were clipped. So I changed the code in fontsDrawText like this and it works:

void RenderContext::fontsDrawText(const TextItem& ti, const Vec3f& color, bool bold, bool idle)
{
    /*
     * cairo treats all surfaces as bounded and drops glyphs from a rendering
     * path if they don't belong to the surface yet, making it difficult to
     * calculate a desired surface size, which would include all elements of
     * a chemical structure being rendered.
     * Due to this limitation, we cannot calculate a size of the surface for
     * "real" rendering.
     *
     * If idle, we don't render glyphs for the text item. Rather, we calculate
     * the bounding rectangle for the text item and add it to the cairo path.
     * Later, glyphs will be rendered in a usual way during a "real" stage.
     *
     * This also saves resources, as we don't make heavyweight glyph rendering
     * twice.
     */
    if (idle)
    {
        /*
        cairo_move_to(_cr, ti.bbp.x, ti.bbp.y);
        cairo_rectangle(_cr, ti.bbp.x, ti.bbp.y, ti.bbsz.x, ti.bbsz.y);
        bbIncludePath(false);
        */

        /*
        * @chanhaeng.lee
        * This code calculates more precise AABB with the transform which has rotation elements.
        * I think cairo_rectangle might be also dropped when it's outside of a surface,
        * because indigo can't render correctly the image with rotated atom labels in my case.
        * I update bbmin/bbmax here to estimate the correct dimension
        * 
        * The logic is :
        * get each rectangle position, transform it, and get min/max.
        * finally update the bbmin/bbmax code.
        */
        double coords[4][2];
        coords[0][0] = ti.bbp.x;
        coords[0][1] = ti.bbp.y;
        coords[1][0] = coords[0][0] + ti.bbsz.x;
        coords[1][1] = coords[0][1];
        coords[2][0] = coords[0][0];
        coords[2][1] = coords[0][1] + ti.bbsz.y;
        coords[3][0] = coords[1][0];
        coords[3][1] = coords[2][1];

        double minX = FLT_MAX;
        double minY = FLT_MAX;
        double maxX = -FLT_MAX;
        double maxY = -FLT_MAX;

        cairo_matrix_t m;
        cairo_get_matrix(_cr, &m);

        for (int ci = 0; ci < 4; ++ci)
        {
            cairo_matrix_transform_point(&m, &coords[ci][0], &coords[ci][1]);

            if (coords[ci][0] < minX)
                minX = coords[ci][0];
            if (coords[ci][0] > maxX)
                maxX = coords[ci][0];

            if (coords[ci][1] < minY)
                minY = coords[ci][1];
            if (coords[ci][1] > maxY)
                maxY = coords[ci][1];
        }
        
        Vec2f minv(minX, minY);
        Vec2f maxv(maxX, maxY);
        bbmin.min(minv);
        bbmax.max(maxv);

        return;
    }
    ...

I checked by looking through the cairo implementation that cairo_path_extents() used in bbIncludePath() returns the coordinates transformed with the cairo's current transform matrix, however I guess that it returns only coordinates within the surface of the previous path object because the rendering was not correct with the previous code. I am not sure whether cairo_rectangle() is clipped or not, but I think it would be clipped by looking at the results in my project.

lch32111 avatar Jun 20 '22 12:06 lch32111

Could you provide some mol-file to highlight the issue?

even1024 avatar Jun 29 '22 16:06 even1024