FlyRanch / figurefirst

A layout-first approach to figure making
http://flyranch.github.io/figurefirst
MIT License
80 stars 15 forks source link

support for axis rotation #64

Open neurochen opened 2 years ago

neurochen commented 2 years ago

Thanks for the amazing work on FigureFirst! I am wondering if it's possible to add support for rotation of axis. The original package gives rise to an error if the axis contains a rotation transform. I made some changes to handle the rotation parameter, but the SVG output is not as expected. So, it seemed that there are some other code change requirements somewhere else.

Note that the matrix transform implementation also doesn't work if we modify the matrix using the rotation matrix directly.

Please see below for amendments in svg_to_axes.py

def parse_transform(transform_str):
    """convert transforms into transformation matrix"""
    #print transform_str
    import re
    # regex for extracting numbers from transform string
    scanfloat =  r"[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?"
    tr = None;tr_pos = None
    mt = None;mt_pos = None
    sc = None;sc_pos = None
    rt = None;rt_pos = None
    ##################
    if 'translate' in transform_str:
        translate_str = re.findall(r'translate\(.*?\)',transform_str)[0]
        txy = re.findall(scanfloat,translate_str)
        if type(txy) is list and len(txy) == 2:
            tx,ty = txy
        else: # deal with rare situation of exact resizing
            tx = txy[0]
            ty = txy[0]
        tr = np.array([[1.,0,float(tx)],
                       [0,1.,float(ty)],
                       [0,0,1.        ]])
        tr_pos = transform_str.find('translate')
    ##################
    if 'scale' in transform_str:
        translate_str = re.findall(r'scale\(.*?\)',transform_str)[0]
        sx,sy = re.findall(scanfloat,translate_str)
        sc = np.array([[float(sx),0,         0],
                       [0,        float(sy), 0],
                       [0,        0,        1.]])
        sc_pos = transform_str.find('scale')
    ##################
    if 'matrix' in transform_str:
        matrix_str = re.findall(r'matrix\(.*?\)',transform_str)[0]
        a,b,c,d,e,f = re.findall(scanfloat,matrix_str)
        a,b,c,d,e,f = [float(s) for s in [a,b,c,d,e,f]]
        mt = np.array([[a,c,e],
                       [b,d,f],
                       [0,0,1.]])
        mt_pos = transform_str.find('matrix')
    ##################
    if 'rotate' in transform_str:
        rotation_str = re.findall(r'rotate\(.*?\)',transform_str)[0]
        theta = re.findall(scanfloat,rotation_str)
        theta = -int(theta[0])/180*np.pi
        cos, sin = np.cos(theta), np.sin(theta)
        rt = np.array([[cos, -sin, 0],
                       [sin,  cos, 0],
                       [  0,    0, 1.]])
        rt_pos = transform_str.find('rotate')
    ##################
    tr_pos = {False:tr_pos,True:0}[tr_pos is None]
    mt_pos = {False:mt_pos,True:0}[mt_pos is None]
    sc_pos = {False:sc_pos,True:0}[sc_pos is None]
    rt_pos = {False:rt_pos,True:0}[rt_pos is None]
    s = [tr_pos,mt_pos,sc_pos,rt_pos]
    def take0(x):
        return x[0]
    trnsfrms = [mtrx for pos,mtrx in sorted(zip(s,[tr,mt,sc,rt]),key =take0)]
    trnsfrms = [m for m in trnsfrms if not(m is None)]
    from numpy import dot
    from functools import reduce
    if len(trnsfrms) >= 1:
        mtrx = reduce(lambda x,y:dot(x,y.T),trnsfrms)
    return mtrx
florisvb commented 2 years ago

I'm glad you're finding the package useful!

Could you give an example image of the template file, what you are hoping the output would look like, and what it does look like? (if github does not allow uploading an image you can email me directly at @.*** )

I am guessing that perhaps you are rotating the rectangle in your template, would like the figure that gets plotted to also be rotated, but instead something else happens? If that is the case, I suspect that some changes would be needed in the code that sticks the matplotlib plot into the svg. Perhaps in the function append_figure_to_layer at line 1305?

On Wed, Jul 20, 2022 at 7:08 AM neurochen @.***> wrote:

Thanks for the amazing work on FigureFirst! I am wondering if it's possible to add support for rotation of axis. The original package gives rise to an error if the axis contains a rotation transform. I made some changes to handle the rotation parameter, but the SVG output is not as expected. So, it seemed that there are some other code change requirements somewhere else.

Please see below for svg_to_axes.py

def parse_transform(transform_str): """convert transforms into transformation matrix"""

print transform_str

import re
# regex for extracting numbers from transform string
scanfloat =  r"[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?"
tr = None;tr_pos = None
mt = None;mt_pos = None
sc = None;sc_pos = None
rt = None;rt_pos = None
##################
if 'translate' in transform_str:
    translate_str = re.findall(r'translate\(.*?\)',transform_str)[0]
    txy = re.findall(scanfloat,translate_str)
    if type(txy) is list and len(txy) == 2:
        tx,ty = txy
    else: # deal with rare situation of exact resizing
        tx = txy[0]
        ty = txy[0]
    tr = np.array([[1.,0,float(tx)],
                   [0,1.,float(ty)],
                   [0,0,1.        ]])
    tr_pos = transform_str.find('translate')
##################
if 'scale' in transform_str:
    translate_str = re.findall(r'scale\(.*?\)',transform_str)[0]
    sx,sy = re.findall(scanfloat,translate_str)
    sc = np.array([[float(sx),0,         0],
                   [0,        float(sy), 0],
                   [0,        0,        1.]])
    sc_pos = transform_str.find('scale')
##################
if 'matrix' in transform_str:
    matrix_str = re.findall(r'matrix\(.*?\)',transform_str)[0]
    a,b,c,d,e,f = re.findall(scanfloat,matrix_str)
    a,b,c,d,e,f = [float(s) for s in [a,b,c,d,e,f]]
    mt = np.array([[a,c,e],
                   [b,d,f],
                   [0,0,1.]])
    mt_pos = transform_str.find('matrix')
##################
if 'rotate' in transform_str:
    rotation_str = re.findall(r'rotate\(.*?\)',transform_str)[0]
    theta = re.findall(scanfloat,rotation_str)
    theta = -int(theta[0])/180*np.pi
    cos, sin = np.cos(theta), np.sin(theta)
    rt = np.array([[cos, -sin, 0],
                   [sin,  cos, 0],
                   [  0,    0, 1.]])
    rt_pos = transform_str.find('rotate')
##################
tr_pos = {False:tr_pos,True:0}[tr_pos is None]
mt_pos = {False:mt_pos,True:0}[mt_pos is None]
sc_pos = {False:sc_pos,True:0}[sc_pos is None]
rt_pos = {False:rt_pos,True:0}[rt_pos is None]
s = [tr_pos,mt_pos,sc_pos,rt_pos]
def take0(x):
    return x[0]
trnsfrms = [mtrx for pos,mtrx in sorted(zip(s,[tr,mt,sc,rt]),key =take0)]
trnsfrms = [m for m in trnsfrms if not(m is None)]
from numpy import dot
from functools import reduce
if len(trnsfrms) >= 1:
    mtrx = reduce(lambda x,y:dot(x,y.T),trnsfrms)
return mtrx

— Reply to this email directly, view it on GitHub https://github.com/FlyRanch/figurefirst/issues/64, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAB4EPCOLE7AIRRLHNUY2KDVVAB4TANCNFSM54DZ7DRA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- Floris van Breugel | http://www.florisvanbreugel.com Assistant Professor of Mechanical Engineering & Graduate Program for Neuroscience University of Nevada, Reno

Wildlife and Landscape Photography Galleries: http://www.ArtInNaturePhotography.com/ Blog: http://www.ArtInNaturePhotography.com/wordpress/