This beam is based on the equations in the beam equation notebook. These equations are from the Euler beam equations. The drawing of the beam such that it is a continous SVG path which can be filled is based on information found here.


In [13]:
from SVG_lib import point, Line
from CurveManip import rotate, mirror
from New_Spline import Spline
import os
from numpy import ceil, log10
from Supports import pinned, roller
from collections import namedtuple
Corners = namedtuple("Corners","left top width height")

def adjust_viewbox(corners, support_points, deformed_beam):
    S = support_points
    bb=deformed_beam
    alt_S = bb.support_points()
    dx = alt_S.S0[0] - S.S0[0]
    dy = alt_S.S0[1] - S.S0[1]
#     print('dx,dy = {}'.format((dx,dy)))
    X = corners.left + dx
    Y = corners.top + dy
    new_corners = Corners(X,Y,corners.width, corners.height)
    bb.corners = new_corners

class beam(object):
    "For displaying a beam failure"
    def __init__(self):
        self.L_CL = point(10,0)  # Left hand end of the centerline
        self.R_CL = point(610,0) # Right hand end of the centerline
        self._sag = 0
        self.thickness = 60
        off = self._t/2
        # Beam centerline
        self.centerline = Spline(self.L_CL.List(),self.R_CL.List())
        self.centerline.centerline=True # For display
        # Top of the beam
        top = Spline(self.L_CL.offset_y(-off).List(),self.R_CL.offset_y(-off).List())
        self.top = [top]
        # Bottom of the beam
        bottom = Spline(self.L_CL.offset_y(off).List(),self.R_CL.offset_y(off).List())
        self.bottom = [bottom]
        self.line_width=1
        # Beam ends
        Left = Line(self.L_CL.offset_y(-off),self.L_CL.offset_y(off))
        Left.width = 1
        self.L_end_orig = [Left] # Need to make Line object
        Right = Line(self.R_CL.offset_y(-off),self.R_CL.offset_y(off))
        Right.width = 1
        self.R_end_orig = [Right] # Need to make Line object
        # The beam ends are copy of the orginal ends incase they need to be redrawn
        self.L_end = self.L_end_orig.copy()
        self.R_end = self.R_end_orig.copy()
        
        self._filename = None
        self._path = None
        self._extension = None

        self._warned = False # for setting the maximum file number in save method
        
        # For displaying ends that are broken due to shear failure
        self.manual_ends = False
        
        self.splits = []
        self.show_center_line = True
        self.break_line='' # Obsolete?
        self.standard_support = True # Supports are always at the bottom corners of the beam
        self.manual_viewbox = False # For making images all the same size
        
    
    @property
    def line_width(self):
        return self._line_width
    @line_width.setter
    def line_width(self, val):
        for top in self.top:
            top.width = str(val)
        for bottom in self.bottom:
            bottom.width = str(val)
        self._line_width = val
    
    @property
    def thickness(self):
        return self._t
    @thickness.setter
    def thickness(self,val):
        self._t = val
    
    @property
    def display_points(self):
        return self._display_points
    @display_points.setter
    def display_points(self,val):
        if type(val)!=type(True):
            raise(ValueError('Must be True or False only!'))
        self._display_points = val
        self.centerline.points = val
        for top in self.top:
            top.points = val
        for bottom in self.bottom:
            bottom.points = val
        for split in self.splits:
            split.points = val
    
    def _set_poly_origin(self,poly_list,end_point):
        x, y = end_point
        poly_list[0].P0 = [x,y]
    
    def _set_poly_end(self,poly_list,end_point):
        x, y = end_point
        poly_list[-1].P3 = [x,y]
    
    @property
    def sag(self):
        return self._sag
    @sag.setter
    def sag(self,val):
        self._sag = val
        self.centerline.sag = val
        angs = self.centerline.end_angles()
        x0_base,y0_base = self.centerline.P0

        if not self.manual_ends:
            L_end =[]
            for line in self.L_end_orig:
                x0, y0 = line.p0.List()
                x0_, y0_ = rotate([x0,y0],base=[x0_base,y0_base],angle=angs.θ_0)
                x0 = float(x0_)
                y0 = float(y0_)
                x1, y1 = line.p1.List()
                x1_, y1_ = rotate([x1,y1],base=[x0_base,y0_base],angle=angs.θ_0)
                x1 = float(x1_)
                y1 = float(y1_)
                new_Line = Line(point(x0,y0), point(x1,y1))
                new_Line.width = self.line_width
                L_end.append(new_Line)
            self.L_end = L_end
        self._set_poly_origin(self.bottom,self.L_end[-1].p1.List())
        self._set_poly_origin(self.top,self.L_end[0].p0.List())
        
        x0_base,y0_base = self.R_CL.List()
        if not self.manual_ends:
            R_end =[]
            for line in self.R_end_orig:
                x0, y0 = line.p0.List()
                x0_, y0_ = rotate([x0,y0],base=[x0_base,y0_base],angle=angs.θ_1)
                x0 = float(x0_)
                y0 = float(y0_)
                x1, y1 = line.p1.List()
                x1_, y1_ = rotate([x1,y1],base=[x0_base,y0_base],angle=angs.θ_1)
                x1 = float(x1_)
                y1 = float(y1_)
                new_Line = Line(point(x0,y0), point(x1,y1))
                new_Line.width = self.line_width
                R_end.append(new_Line)
            self.R_end = R_end
        self._set_poly_end(self.bottom,self.R_end[-1].p1.List())
        self._set_poly_end(self.top,self.R_end[0].p0.List())
        
        if len(self.top)==1:
            self.top[0].sag = val
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        if len(self.bottom)==1:
            self.bottom[0].sag = val
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        
        
    def mirror_end(self, source='left'):
        source = source.lower().strip()
        if len(self.top)==1:
            a = [float(p) for p in np.array(self.top.P1)-np.array(self.top.P0)]
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        if len(self.bottom)==1:
            b = [float(p) for p in np.array(self.bottom.P1)-np.array(self.bottom.P0)]
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        if source == 'left':
            R_end=[]
            for line in self.L_end:
                p0 = line.p0.List()
                p1 = line.p1.List()
                p0 = mirror(p0,[a,b])
                p1 = mirror(p1,[a,b])
                R_end.append(Line(point(*p0),point(*p1)))
            self.R_end = R_end
        elif source == 'right':
            L_end = []
            for line in self.R_end:
                p0 = line.p0.List()
                p1 = line.p1.List()
                p0 = mirror(p0,[a,b])
                p1 = mirror(p1,[a,b])
                L_end.append(Line(point(*p0),point(*p1)))
            self.L_end = L_end
        else:
            er= "The only sources that may be specified are left or right."
            raise(ValueError(er))
        
    def __repr__(self):
        txt =  'sag = {}\n'.format(self.sag)
        txt += 'Origin = {}\n'.format(self.L_CL)
        txt += 'End = {}\n'.format(self.R_CL)
        txt += 'd = {}\n'.format(self.thickness)
        return txt
    
    def svg_alt(self):
        path = ('<path\n'
                'style="fill:#CF9B42;fill-rule:evenodd;stroke:{};stroke-width:{}px;stroke-'
                'linecap:square;stroke-linejoin:miter;stroke-opacity:1"\n'
                'd="M {} Z"\n'
                'inkscape:connector-curvature="0" />\n')
        txt = ''
        top = self.top[0]
        C = top.control_points()
        txt += '{},{} C {},{} {},{} {},{} \n'.format(C.P0[0],
                                                  C.P0[1],
                                                  C.P1[0],
                                                  C.P1[1],
                                                  C.P2[0],
                                                  C.P2[1],
                                                  C.P3[0],
                                                  C.P3[1])
        for line in self.R_end:
            txt+= 'L {},{} '.format(line.p1.x,line.p1.y)+'\n'
        
        if len(self.bottom)==1:
            bot = self.bottom[0]
            C = bot.control_points()
            txt += '{},{} C {},{} {},{} {},{} \n'.format(C.P3[0],
                                                         C.P3[1],
                                                         C.P2[0],
                                                         C.P2[1],
                                                         C.P1[0],
                                                         C.P1[1],
                                                         C.P0[0],
                                                         C.P0[1])
        else:
            bot = self.bottom[-1]
            C = bot.control_points()
            txt += '{},{} C {},{} {},{} {},{} \n'.format(C.P3[0],
                                                         C.P3[1],
                                                         C.P2[0],
                                                         C.P2[1],
                                                         C.P1[0],
                                                         C.P1[1],
                                                         C.P0[0],
                                                         C.P0[1])
            for line in self.break_line:
                txt+= 'L {},{} '.format(line.p1.x,line.p1.y)+'\n'
            bot = self.bottom[0]
            C = bot.control_points()
            txt += '{},{} C {},{} {},{} {},{} \n'.format(C.P3[0],
                                                         C.P3[1],
                                                         C.P2[0],
                                                         C.P2[1],
                                                         C.P1[0],
                                                         C.P1[1],
                                                         C.P0[0],
                                                         C.P0[1])
        for line in self.L_end[::-1]:
            txt+= 'L {},{} '.format(line.p0.x,line.p0.y)+'\n'
        path_txt = path.format(top.color,top.width,txt)
        txt = self._view_box()
        txt += path_txt
        
        if self.show_center_line:
            txt += self.centerline.svg_txt()   
        
        for split in self.splits: # for displaying all cracks
            txt+= split.svg_txt()+'\n'
        txt += self._support_txt()
        txt +="</svg>"
        return txt    
    
    def support_points(self):
        if self.standard_support:
            S0 = self.bottom[-1].P0
            S3 = self.bottom[0].P3
        else:
            cL = self.centerline
            t = self.thickness
            P0 = self.bottom[-1].P0
            P3 = self.bottom[0].P3
            sag = cL.P0[1] + self.sag + t - P0[1]
            bot = Spline(P0,P3)
            bot.sag = sag
            x0 = cL.P0[0]
            x3 = cL.P3[0]
            y = bot.Y_(cL.P0[0])
            S0 = [x0, y]
            S3 = [x3, y]
        Supports = namedtuple("Supports","S0 S3")
        supports = Supports(S0, S3)
        return supports
        
    def _support_txt(self):
        S = self.support_points()
        txt = roller(S.S3)
        txt += pinned(S.S0)
        return txt
    
    def get_corners(self, suppress=False):
        if len(self.top)==1:
            Top = self.top[0].bounds()
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        if len(self.bottom)==1:
            Bot = self.bottom[0].bounds()
        else:
            er="not implemented!!!!!!"
            raise(RuntimeError(er))
        left = min(Top.left, Bot.left)-30
        top = min(Top.top, Bot.top)
        right = max(Top.left+Top.width, Bot.left+Bot.width)+30
        bottom = max(Top.top+Top.height, Bot.top+Bot.height)+60 - self.top[0].sag
        width = abs(right-left)
        height = abs(bottom-top)
        self.corners = Corners(left,top,width,height)
        if not suppress:
            return self.corners
        
    def _view_box(self):
        if not self.manual_viewbox:
            self.get_corners(True)
        left,top,width,height = self.corners
        txt = '<svg  viewBox = "%f %f %f %f">\n'%(left,top,width,height)
        txt += ('<rect x="%f" y="%f" width="%f" height="%f" '
                'style="fill:#F2F2F2;fill-opacity:1;"/>\n'%(left,top,width,height))
        return txt
    
    
    def _repr_svg_(self):
        txt = self._view_box()
        for index,top_ in enumerate(self.top):
            txt+= top_.svg_txt()+'\n'
        for index, bottom_ in enumerate(self.bottom):
            txt+= bottom_.svg_txt()+'\n'
        if self.show_center_line:
            txt += self.centerline.svg_txt()   
        
        for split in self.splits: # for displaying all cracks
            txt+= split.svg_txt()+'\n'
        
        for line in self.L_end:
            txt+= line.svg_line_txt()+'\n'
        for line in self.R_end:
            txt+= line.svg_line_txt()+'\n' 
        txt += self._support_txt()
        txt +="</svg>"
        return txt
    
    def save(self, filename=""):
        if not self._warned:
            max_file_num = input(("What is the maximum number of versions "
                                  "of this beam do you want to save? "
                                  "[1000]")) or '1000'
            self.max_file_number = int(max_file_num)
            self._warned = True
            self._suffix_count = int(ceil(log10(self.max_file_number)))
        if filename == "" and self._filename!=None:
            filename = self._filename # reuse last filename
            path = self._path
            extension = self._extension
            count = self._count
        elif filename != "":
            path = '/'.join(filename.split('/')[:-1])
            path = '.' if path =='' else path
            self._path = path
            filename = filename.split('/')[-1]
            extension = filename.split('.')[-1].lower()
            filename = '.'.join(filename.split('.')[:-1]) if extension == 'svg' else filename
            self._filename = filename
            extension = '.svg'
            self._extension = extension
            count = 0
            self._count = count
        else:
            er="A file name must be provided at least once."
            raise(ValueError(er))
            
        def Filename(num):
            sufix = '-{:{fill}>{width}}'.format(num,fill='0',width=self._suffix_count)
            File = path+'/'+filename+sufix+extension
            return File
            
        if os.path.exists(Filename(count)):
            files_ = os.listdir(self._path)
            numbers = []
            for name in files_:
                if filename in name:
                    numbers.append(int(name.strip(extension).strip(filename).strip('-')))
            count = max(numbers)+1
            self._count=count
        if count == 0:
            print("Saving first svg drawing to: {}".format(Filename(count)))
        with open(Filename(count),'w') as f:
            f.write(self.svg_alt())

In [18]:
bb = beam()
bb.display_points=False
#bb.show_center_line= False
bb.sag = 60
corners = bb.get_corners()
S = bb.support_points()
bb


Out[18]:

In [20]:
bb.show_center_line = False
bb.manual_viewbox=True
for i in range(30):
    bb.sag = i*2
    adjust_viewbox(corners, S, bb)
    bb.save('./Deflection_failure/beam')
bb


Saving first svg drawing to: ./Deflection_failure/beam-000.svg
Out[20]:

In [32]:
print(bb._view_box())


<svg  viewBox = "-17.552297 -27.267023 349.143272 88.572724">


In [4]:
bb.sag = 30
bb


Out[4]:

In [35]:
# print(bb.svg_alt())

bb.save('straight_beam.svg')


In [ ]:
# for i in range(29):
#     bb.sag = i+2
#     bb.save()

In [ ]:
# bb.centerline

In [ ]:
# bb.sag = 30
# bb

In [ ]: