#
##
## SPDX-FileCopyrightText: © 2007-2023 Benedict Verhegghe <bverheg@gmail.com>
## SPDX-License-Identifier: GPL-3.0-or-later
##
## This file is part of pyFormex 3.4 (Thu Nov 16 18:07:39 CET 2023)
## pyFormex is a tool for generating, manipulating and transforming 3D
## geometrical models by sequences of mathematical operations.
## Home page: https://pyformex.org
## Project page: https://savannah.nongnu.org/projects/pyformex/
## Development: https://gitlab.com/bverheg/pyformex
## Distributed under the GNU General Public License version 3 or later.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see http://www.gnu.org/licenses/.
##
#
"""Some functions operating on 2D structures.
This is a plugin for pyFormex.
(C) 2002 Benedict Verhegghe
See the Section2D example for an example of its use.
"""
import numpy as np
from pyformex.plugins import sectionize
## initialization should be with a closed PolyLine, after checking it is plane
[docs]class PlaneSection():
"""A class describing a general 2D section.
The 2D section is the area inside a closed curve in the (x,y) plane.
The curve is decribed by a finite number of points and by straight
segments connecting them.
"""
def __init__(self, F):
"""Initialize a plane section.
Initialization can be done either by a list of points or a set of line
segments.
By Points:
Each point is connected to the following one, and (unless they are
very close) the last one back to the first. Traversing the resulting
path should rotate positively around the z axis to yield a positive
surface.
By Segments:
It is the responsibilty of the user to ensure that the segments
form a closed curve. If not, the calculated section data will be
rather meaningless.
"""
if F.nplex() == 1:
self.F = sectionize.connectPoints(F, close=True)
elif F.nplex() == 2:
self.F = F
else:
raise ValueError("Expected a plex-1 or plex-2 Formex")
def sectionChar(self):
return sectionChar(self.F)
[docs]def sectionChar(F):
"""Compute characteristics of plane sections.
The plane sections are described by their circumference, consisting of a
sequence of straight segments.
The segment end point data are gathered in a plex-2 Formex.
The segments should form a closed curve.
The z-value of the coordinates does not have to be specified,
and will be ignored if it is.
The resulting path through the points should rotate positively around the
z axis to yield a positive surface.
The return value is a dict with the following characteristics:
- `L` : circumference,
- `A` : enclosed surface,
- `Sx` : first area moment around global x-axis
- `Sy` : first area moment around global y-axis
- `Ixx` : second area moment around global x-axis
- `Iyy` : second area moment around global y-axis
- `Ixy` : product moment of area around global x,y-axes
"""
if F.nplex() != 2:
raise ValueError("Expected a plex-2 Formex!")
#pf.debug("The circumference has %d segments" % F.nelems())
x = F.x
y = F.y
x0 = x[:, 0]
y0 = y[:, 0]
x1 = x[:, 1]
y1 = y[:, 1]
a = (x0*y1 - x1*y0) / 2
return {
'L': np.sqrt((x1-x0)**2 + (y1-y0)**2).sum(),
'A': a.sum(),
'Sx': (a*(y0+y1)).sum()/3,
'Sy': (a*(x0+x1)).sum()/3,
'Ixx': (a*(y0*y0+y0*y1+y1*y1)).sum()/6,
'Iyy': (a*(x0*x0+x0*x1+x1*x1)).sum()/6,
'Ixy': -(a*(x0*y0+x1*y1+(x0*y1+x1*y0)/2)).sum()/6,
}
[docs]def extendedSectionChar(S):
"""Computes extended section characteristics for the given section.
S is a dict with section basic section characteristics as returned by
sectionChar().
This function computes and returns a dict with the following:
- `xG`, `yG` : coordinates of the center of gravity G of the plane section
- `IGxx`, `IGyy`, `IGxy` : second area moments and product around axes
through G and parallel with the global x,y-axes
- `alpha` : angle(in radians) between the global x,y axes and the principal
axes (X,Y) of the section (X and Y always pass through G)
- `IXX`, `IYY` : principal second area moments around X,Y respectively. (The
second area product is always zero.)
"""
xG = S['Sy']/S['A']
yG = S['Sx']/S['A']
IGxx = S['Ixx'] - S['A'] * yG**2
IGyy = S['Iyy'] - S['A'] * xG**2
IGxy = S['Ixy'] + S['A'] * xG*yG
alpha, IXX, IYY = princTensor2D(IGxx, IGyy, IGxy)
return {
'xG': xG,
'yG': yG,
'IGxx': IGxx,
'IGyy': IGyy,
'IGxy': IGxy,
'alpha': alpha,
'IXX': IXX,
'IYY': IYY,
}
[docs]def princTensor2D(Ixx, Iyy, Ixy):
"""Compute the principal values and directions of a 2D tensor.
Returns a tuple with three values:
- `alpha` : angle (in radians) from x-axis to principal X-axis
- `IXX,IYY` : principal values of the tensor
"""
C = (Ixx+Iyy) * 0.5
D = (Ixx-Iyy) * 0.5
R = np.sqrt(D**2 + Ixy**2)
IXX = C+R
IYY = C-R
alpha = np.arctan2(Ixy, D) * 0.5
return alpha, IXX, IYY
# End