├── .circleci └── config.yml ├── .gitignore ├── Init.py ├── InitGui.py ├── LICENSE ├── OSAFEGui.py ├── README.md ├── check_legal.py ├── civilwelcome.py ├── compile.py ├── dxfColorMap.py ├── dxfImportObjects.py ├── dxfLibrary.py ├── dxfReader.py ├── help ├── cover.tex ├── export.tex ├── faq.tex ├── figures │ ├── assign.png │ ├── autoload.png │ ├── civil.png │ ├── columnedit.png │ ├── comboview.png │ ├── dataview.png │ ├── excel.png │ ├── excel2.png │ ├── export.png │ ├── geometry.png │ ├── geometry2.png │ ├── import_model_from_etabs.png │ ├── logo.svg │ ├── make_auto_strip.png │ └── punch.png ├── freecad.tex ├── help.pdf ├── help.tex ├── import_model.html ├── import_model.md ├── introduction.tex ├── make_auto_strip.html ├── make_auto_strip.md ├── modify.tex ├── punch_report.cls ├── safe.tex └── update.tex ├── old_punch ├── Help.pdf ├── axis.py ├── foundraw │ ├── geom.py │ ├── pyconcreteWelcome.py │ └── safe.py ├── geom.py ├── safe.py └── test_safe.py ├── osafe_draw ├── draw_base_foundation.py ├── draw_beam.py ├── draw_rectangular_slab.py ├── draw_slab.py └── draw_strip.py ├── osafe_funcs └── osafe_funcs.py ├── osafe_gui └── gui_automatic_rebars.py ├── osafe_images ├── auto_strip.svg ├── automatic_base_foundation.svg ├── banner.jpg ├── base_foundation.svg ├── base_plate.svg ├── beam.svg ├── cancel.png ├── change_branch.svg ├── civil-engineering.png ├── civil_Welcome.svg ├── civiltools.png ├── copy.svg ├── draw_strip.svg ├── dxf.svg ├── etabs.png ├── explode.svg ├── explode_foundation.svg ├── export.svg ├── export_strips.svg ├── f2k.svg ├── force.svg ├── foundation.svg ├── help.png ├── import.svg ├── import_dxf.svg ├── opening.svg ├── osafe_rebar.svg ├── pdf.svg ├── png.png ├── preferences-OSAFE.svg ├── preferences-punch.svg ├── punch.svg ├── rectangle.svg ├── rectangular_slab.svg ├── refresh.svg ├── run.svg ├── safe.png ├── segment.png ├── slab.svg ├── strip.svg ├── tick.svg ├── trapozeidal.svg ├── update.png ├── update.svg ├── view_arch_wall.svg ├── view_base_foundation.svg ├── view_beams.svg ├── view_columns.svg ├── view_design_layer.svg ├── view_design_layer_a.svg ├── view_design_layer_b.svg ├── view_foundations.svg ├── view_osafe_rebar.svg ├── view_punch.svg ├── view_slabs.svg ├── wall.svg ├── wireframe.svg ├── word.png └── xlsx.png ├── osafe_import_export ├── export.py ├── report.py ├── safe_read_write_f2k.py └── templates │ └── punch_default.docx ├── osafe_objects ├── base_foundation.py ├── base_plate.py ├── beam.py ├── colorbar.py ├── etabs_foundation.py ├── f2k_object.py ├── foundation.py ├── opening.py ├── osafe_rebar.py ├── punch.py ├── rectangular_slab.py ├── slab.py ├── strip.py └── trapezoidal_slab.py ├── osafe_py_widgets ├── base_foundation_panel.py ├── change_branch.py ├── create_f2k_command.py ├── draw_automatic_rebars.py ├── draw_automatic_strip.py ├── etabs_panel.py ├── etabs_punch.py ├── explode_foundation.py ├── explode_seismic_load_patterns.py ├── export │ ├── export_strips_panel.py │ ├── export_to_dxf_dialog.py │ └── import_from_dxf_dialog.py ├── force_panel.py ├── foundation_panel.py ├── gui_automatic_strip.py ├── gui_dxf.py ├── gui_export_strips.py ├── gui_punch.py ├── osafe_views.py ├── resource.qrc ├── resource_rc.py ├── safe_panel.py └── wall_panel.py ├── osafe_statusbar.py ├── osafe_translate_utils.py ├── osafe_widgets ├── base_foundation.ui ├── base_foundation_panel.ui ├── base_plate.ui ├── change_branch.ui ├── civil_welcome.ui ├── column.ui ├── create_f2k.ui ├── draw_automatic_rebars.ui ├── draw_automatic_strip.ui ├── draw_strip.ui ├── edit_objects │ ├── edit_base_foundation.ui │ └── edit_osafe_rebars.ui ├── etabs_panel.ui ├── explode_seismic_load_patterns.ui ├── export │ └── export_to_dxf.ui ├── export_strips_panel.ui ├── force_panel.ui ├── foundation_panel.ui ├── import_from_dxf.ui ├── preferences-OSAFE_visual.ui ├── safe_panel.ui ├── serial.ui └── wall_panel.ui ├── package.xml ├── requirements.txt └── test ├── osafe_import_export ├── test_report.py └── test_safe_read_write_f2k.py ├── osafe_objects ├── test_base_plate.py ├── test_osafe_rebar.py └── test_strip.py ├── test_beam.py ├── test_etabs_foundation.py ├── test_etabs_punch.py ├── test_f2k_object.py ├── test_files ├── .~lock.davoodabadi_dynamic.xlsx# ├── davoodabadi_dynamic.xlsx ├── freecad │ ├── adampira.FCStd │ ├── base_foundation.FCStd │ ├── base_plate.FCStd │ ├── circle_column.FCStd │ ├── kazemi.FCStd │ ├── khalaji.F2k │ ├── khalaji.FCStd │ ├── mat.FCStd │ ├── rashidzadeh.FCStd │ ├── strip.FCStd │ ├── strip_foundation.FCStd │ ├── test.FCStd │ └── test_p.FCStd ├── khojasteh_97-07-18.xlsx ├── safdari.xlsx └── sattari_safe.xlsx ├── test_geom.py ├── test_opening.py ├── test_osafe_funcs.py ├── test_punch.py ├── test_rectangular_slab.py └── test_trapezoidal_slab.py /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Python CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-python/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` 11 | - image: circleci/python:3.6.1 12 | 13 | # Specify service dependencies here if necessary 14 | # CircleCI maintains a library of pre-built images 15 | # documented at https://circleci.com/docs/2.0/circleci-images/ 16 | # - image: circleci/postgres:9.4 17 | 18 | working_directory: ~/repo 19 | 20 | steps: 21 | - checkout 22 | 23 | # Download and cache dependencies 24 | - restore_cache: 25 | keys: 26 | - v1-dependencies-{{ checksum "requirements.txt" }} 27 | # fallback to using the latest cache if no exact match is found 28 | - v1-dependencies- 29 | 30 | - run: 31 | name: install dependencies 32 | command: | 33 | python3 -m venv venv 34 | . venv/bin/activate 35 | pip install -r requirements.txt 36 | 37 | - save_cache: 38 | paths: 39 | - ./venv 40 | key: v1-dependencies-{{ checksum "requirements.txt" }} 41 | 42 | # run tests! 43 | # this example uses Django's built-in test-runner 44 | # other common Python testing frameworks include pytest and nose 45 | # https://pytest.org 46 | # https://nose.readthedocs.io 47 | - run: 48 | name: run tests 49 | command: | 50 | . venv/bin/activate 51 | python3 -m unittest 52 | 53 | # - store_artifacts: 54 | # path: test-reports 55 | # destination: test-reports 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # pip requirements 39 | requirements.in 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | 109 | # Dolphin file manager 110 | .directory 111 | 112 | # tex 113 | *.aux 114 | *.fls 115 | *.log 116 | *.out 117 | *.toc 118 | *.xdv 119 | *.fdb_latexmk 120 | 121 | # ETABS 122 | *.$et 123 | *.ico 124 | *.ebk 125 | 126 | # test 127 | test/etabs_api/test.* 128 | 129 | # FreeCAD backup files 130 | *.FCStd1 131 | 132 | # protect from users 133 | # OSAFEGui.py 134 | # !OSAFEGui.pyc 135 | # check_legal.py 136 | # !check_legal.pyc 137 | 138 | -------------------------------------------------------------------------------- /Init.py: -------------------------------------------------------------------------------- 1 | import FreeCAD 2 | 3 | FreeCAD.addImportType("CSI SAFE (*.xls *.xlsx)", "old_punch.geom") 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeCAD Civil Foundation Model Workbench 2 | A Workbench in [FreeCAD](https://freecadweb.org) for creating foundation model in [CSI SAFE](https://www.csiamerica.com/products/safe) from [CSI ETABS](https://www.csiamerica.com/products/etabs) Model. It also can Import [CSI SAFE](https://www.csiamerica.com/products/safe) model into FreeCAD and calculate Shear punching of columns according to ACI 318-19. 3 | 4 | ![3](https://user-images.githubusercontent.com/8196112/155970780-e83b9fe9-5e46-4b75-82b6-860aa44f9ee7.jpg) 5 | 6 | ## Dependencies 7 | - This Addon/workbench runs on: 8 | - [x] Most up to date stable release 9 | - [x] Most up to date development release 10 | - [x] Python 3 compatible 11 | - [x] Qt5 compatible 12 | - [ ] Qt5 and Qt6 compatible (uses "import PySide" rather than "import PySide2") 13 | - [x] Backward compatible with 0.19 version of FC. 14 | - [x] 3rd party dependencies: 15 | - [x] numpy 16 | - [x] scipy 17 | - [x] pandas 18 | - [x] git 19 | - [x] comtypes 20 | 21 | 22 | 23 | ## Installation 24 | 25 | ### Addon Manager 26 | 1. Start the Addons Manager from menu Tools -> Addons manager 27 | 2. Locate and install the OSAFE addon 28 | 3. Restart FreeCAD, and switch to the OSAFE workbench 29 | 30 | ### Manually 31 | #### Windows 32 | You can download FreeCAD from below links and install it in windows. After installation, you must clear Civil folder in FreeCAD installation folder (ec. C:\Program Files\FreeCAD 0.19\Mod\Civil) and then clone this two repositories . 33 | 34 | https://github.com/ebrahimraeyat/OSAFE.git 35 | https://github.com/ebrahimraeyat/etabs_api.git 36 | 37 | [link1](https://github.com/ebrahimraeyat/OSAFE/releases/tag/v0.9) 38 | [link2](https://mega.nz/file/sUlAQaoA#SvTKQu_HswPNQxW9wT8PlCxLGXBZbBH_F-xp6A_bsps) 39 | 40 | #### Debian 10 (Buster) 41 | 42 | ```bash 43 | $ sudo apt install freecad-python3 44 | $ sudo update-alternatives --set freecad /usr/lib/freecad/bin/freecad-python3 45 | $ sudo apt install git python3-pandas 46 | $ mkdir -p $HOME/.FreeCAD/Mod 47 | $ cd $HOME/.FreeCAD/Mod 48 | $ git clone https://github.com/ebrahimraeyat/OSAFE.git 49 | $ git clone https://github.com/ebrahimraeyat/etabs_api.git 50 | ``` 51 | 52 | ## Discussion 53 | Forum thread to discuss this workbench can be found in the [FreeCAD Subforums](https://forum.freecadweb.org/viewtopic.php?f=24&t=31813#p264539) 54 | 55 | ## Contribute 56 | Pull Requests are welcome. Please feel free to discuss them on the forum thread. 57 | -------------------------------------------------------------------------------- /check_legal.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import base64 4 | import subprocess 5 | from pathlib import Path 6 | 7 | 8 | class CheckLegalUse: 9 | 10 | def __init__(self, 11 | filename, 12 | gist_url, 13 | dir_name='punch', 14 | n=1, 15 | ): 16 | import FreeCAD 17 | freecad_dir = Path(FreeCAD.ConfigGet("UserAppData")) 18 | if not freecad_dir.exists(): 19 | freecad_dir.mkdir() 20 | application_dir = freecad_dir / dir_name 21 | if not application_dir.exists(): 22 | application_dir.mkdir() 23 | self.filename = application_dir / filename 24 | self.gist_url = gist_url 25 | self.n = n 26 | 27 | def allowed_to_continue(self): 28 | if sys.platform == "win32": 29 | if not self.is_registered: 30 | self.serial = str(subprocess.check_output("wmic csproduct get uuid")).split("\\r\\r\\n")[1].split()[0] 31 | if not internet(): 32 | return False, 'INTERNET' 33 | 34 | if not self.serial_number(self.serial): 35 | return False, 'SERIAL' 36 | else: 37 | self.register() 38 | return True, 'REGISTERED' 39 | return True, '' 40 | return True, '' 41 | 42 | def initiate(self): 43 | if not self.filename.exists(): 44 | with open(self.filename, 'wb') as f: 45 | b = base64.b64encode('0-0'.encode('utf-8')) 46 | f.write(b) 47 | 48 | @property 49 | def is_registered(self): 50 | if not Path(self.filename).exists(): 51 | self.initiate() 52 | return True 53 | else: 54 | text = self.get_registered_numbers() 55 | if text[0] == 1 or text[1] <= self.n: 56 | return True 57 | else: 58 | return False 59 | 60 | def get_registered_numbers(self): 61 | if not Path(self.filename).exists(): 62 | self.initiate() 63 | with open(self.filename, 'rb') as f: 64 | b = f.read() 65 | text = base64.b64decode(b).decode('utf-8').split('-') 66 | return int(text[0]), int(text[1]) 67 | 68 | def add_using_feature(self): 69 | if not Path(self.filename).exists(): 70 | self.initiate() 71 | with open(self.filename, 'rb') as f: 72 | b = f.read() 73 | text = base64.b64decode(b).decode('utf-8').split('-') 74 | text[1] = str(int(text[1]) + 1) 75 | text = '-'.join(*[text]) 76 | with open(self.filename, 'wb') as f: 77 | b = base64.b64encode(text.encode('utf-8')) 78 | f.write(b) 79 | return 80 | 81 | def register(self): 82 | if not Path(self.filename).exists(): 83 | self.initiate() 84 | with open(self.filename, 'rb') as f: 85 | b = f.read() 86 | text = base64.b64decode(b).decode('utf-8').split('-') 87 | text[0] = '1' 88 | text = '-'.join([*text]) 89 | with open(self.filename, 'wb') as f: 90 | b = base64.b64encode(text.encode('utf-8')) 91 | f.write(b) 92 | 93 | def serial_number(self, serial): 94 | import urllib.request 95 | response = urllib.request.urlopen(self.gist_url) 96 | data = response.read() # a `bytes` object 97 | text = data.decode('utf-8') 98 | return serial in text 99 | 100 | def internet(host="8.8.8.8", port=53, timeout=3): 101 | import socket 102 | try: 103 | socket.setdefaulttimeout(timeout) 104 | socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) 105 | return True 106 | except Exception as ex: 107 | # print(ex.message) 108 | return False 109 | -------------------------------------------------------------------------------- /civilwelcome.py: -------------------------------------------------------------------------------- 1 | import os 2 | import FreeCAD 3 | import FreeCADGui 4 | 5 | from PySide2 import QtCore, QtGui 6 | 7 | 8 | def QT_TRANSLATE_NOOP(ctx, txt): return txt 9 | 10 | 11 | class CivilWelcome: 12 | 13 | def GetResources(self): 14 | return {'Pixmap': os.path.join(os.path.dirname(__file__), "Images", "civil_welcom.svg"), 15 | 'MenuText': QT_TRANSLATE_NOOP("Civil_welcome", "Civil welcome screen"), 16 | 'ToolTip': QT_TRANSLATE_NOOP("Civil_welcome", "Show the Civil workbench welcome screen")} 17 | 18 | def Activated(self): 19 | self.form = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), "ui", "civil_welcome.ui")) 20 | self.form.image.setPixmap(QtGui.QPixmap(os.path.join(os.path.dirname(__file__), "images", "banner.jpg"))) 21 | 22 | result = self.form.exec_() 23 | # if result: 24 | # FreeCADGui.runCommand("Civil_Setup") 25 | 26 | FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OSAFE").SetBool("FirstTime", False) 27 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import compileall 3 | import shutil 4 | 5 | from pathlib import Path 6 | 7 | shutil.rmtree(Path("__pycache__")) 8 | compileall.compile_file('OSAFEGui.py') 9 | shutil.copy(Path('__pycache__') / 'OSAFEGui.cpython-38.pyc', Path('OSAFEGui.pyc')) -------------------------------------------------------------------------------- /help/cover.tex: -------------------------------------------------------------------------------- 1 | \pagenumbering{Roman} % a, b, c, ... 2 | \thispagestyle{empty} 3 | % نحوه درج کردن لوگوی دانشگاه 4 | \begin{figure*}[!h] 5 | \centerline{\includegraphics[width=4cm]{logo}} 6 | %\caption{} 7 | \end{figure*} 8 | \begin{center} 9 | %دستوری برای کم کردن فاصله بین لوگو و خط پایین آن 10 | \vspace{-.8cm} 11 | 12 | % {\large پژوهشگاه بین المللی زلزله شناسی و مهندسی زلزله} 13 | %دستوری برای تعیین فاصله بین دو خط 14 | 15 | 16 | \\[1cm] 17 | بنام خدا 18 | \\[1cm] 19 | راهنمای کاربری 20 | \\[.8cm] 21 | % {\nastaliq 22 | \begin{Huge} 23 | نرم افزار برش پانچ 24 | \end{Huge} 25 | 26 | \\[.5cm] 27 | \begin{latin} 28 | \textbf{Ver. 0.9} 29 | \end{latin} 30 | 31 | 32 | 33 | \begin{figure}[ht] 34 | \centering 35 | \includegraphics[width=\linewidth]{figures/punch} 36 | % \caption{DL, units:[m,kN]} 37 | % \label{dl-unitsmkn} 38 | \end{figure} 39 | 40 | \\[1cm]{ توسعه دهنده:} 41 | \\[.3cm] 42 | \textbf{{\large \developer}} 43 | 44 | % \\[2cm]{کارفرما} 45 | % \\[.3cm] 46 | % \textbf{{\large \karfarma}} 47 | % \\ \textit{\address} 48 | 49 | \\[1.cm] 50 | \today 51 | \end{center} 52 | %\maketitle 53 | %دستوری برای رفتن به صفحه جدید 54 | \newpage 55 | \thispagestyle{empty} 56 | % \pagenumbering{Roman} % i, ii, iii, iv, ... 57 | \setcounter{page}{1} 58 | \tableofcontents 59 | % \listoffigures 60 | % \listoftables 61 | \newpage 62 | -------------------------------------------------------------------------------- /help/export.tex: -------------------------------------------------------------------------------- 1 | \section{ذخیره نتایج خروجی} 2 | برای گرفتن خروجی از نرم افزار، میتوانید از آیکون های مرتبط برای فرمت های مختلف استفاده کنید. در حال حاضر نرم افزار قادر به خروجی نتایج گرافیکی پانچ ها به فرمت عکس، 3 | پی دی اف و اتوکد می باشد. همچنین محاسبات پانچ برای تمامی ترکیب بارها را میتوان به صورت فایل اکسل ذخیره نمود. اگر آیکن ها وجود ندارند و یا اینکه منوی 4 | \lr{Civil} 5 | در منوهای نرم افزار موجود نیست باید ابتدا طبق بخش 6 | \ref{sec:loadingcivil} 7 | ورک بنچ را فعال نمایید. 8 | 9 | \begin{figure}[H] 10 | \centering 11 | \includegraphics{figures/export} 12 | \caption{منو و آیکن های خروجی نرم افزار} 13 | \end{figure} -------------------------------------------------------------------------------- /help/faq.tex: -------------------------------------------------------------------------------- 1 | \newpage 2 | \section{پرسش های متداول\label{faq}} 3 | 4 | در این بخش برخی از سوالات متداول که کاربران میپرسند به مرور زمان اضافه میشود. 5 | 6 | \begin{question}{مشخصات فنداسیون یا پانچ برای من نمایش داده نمیشود.} 7 | \true 8 | اگر با کلیک روی فنداسیون یا هر یک از ستونها در محیط سه بعدی یا قسمت بالایی جدول 9 | \lr{Combo View} 10 | مشخصات فنداسیون یا پانچ ها قابل مشاهده نیستند، در قسمت پایین جدول مطمئن شوید که تب 11 | \lr{Data} 12 | فعال باشد، مطابق شکل 13 | \ref{fig:dataview}. 14 | \end{question} 15 | 16 | \begin{figure}[H] 17 | \centering 18 | \includegraphics{figures/dataview} 19 | \caption{تنظیم نمایش مشخصات با انتخاب تب \lr{Data}} 20 | \label{fig:dataview} 21 | \end{figure} 22 | 23 | 24 | \begin{question}{چطور هر بار که نرم افزار را باز میکنم به طور خودکار 25 | \lr{Civil} 26 | لود شود؟} 27 | \true 28 | برای این کار باید از منوی 29 | $Edit \rightarrow Preferences$ 30 | پنجره تنظیمات نرم افزار را باز کنید. مطابق شکل 31 | \ref{fig:autoload} 32 | ، در تب 33 | \lr{General} 34 | قسمت 35 | \lr{Start up} 36 | ورک بنچ 37 | \lr{Civil} 38 | را انتخاب کنید و سپس کلید تایید را بزنید. از این پس بعد از اجرای نرم افزار به طور خودکار ورک بنچ 39 | \lr{Civil} 40 | لود میشود. 41 | \end{question} 42 | 43 | 44 | \begin{figure}[H] 45 | \centering 46 | \includegraphics[width=.7\linewidth]{figures/autoload} 47 | \caption{تنظیم لود شدن خودکار ورک بنچ \lr{Civil} بعد از هر اجرا} 48 | \label{fig:autoload} 49 | \end{figure} -------------------------------------------------------------------------------- /help/figures/assign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/assign.png -------------------------------------------------------------------------------- /help/figures/autoload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/autoload.png -------------------------------------------------------------------------------- /help/figures/civil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/civil.png -------------------------------------------------------------------------------- /help/figures/columnedit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/columnedit.png -------------------------------------------------------------------------------- /help/figures/comboview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/comboview.png -------------------------------------------------------------------------------- /help/figures/dataview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/dataview.png -------------------------------------------------------------------------------- /help/figures/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/excel.png -------------------------------------------------------------------------------- /help/figures/excel2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/excel2.png -------------------------------------------------------------------------------- /help/figures/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/export.png -------------------------------------------------------------------------------- /help/figures/geometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/geometry.png -------------------------------------------------------------------------------- /help/figures/geometry2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/geometry2.png -------------------------------------------------------------------------------- /help/figures/import_model_from_etabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/import_model_from_etabs.png -------------------------------------------------------------------------------- /help/figures/make_auto_strip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/make_auto_strip.png -------------------------------------------------------------------------------- /help/figures/punch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/figures/punch.png -------------------------------------------------------------------------------- /help/freecad.tex: -------------------------------------------------------------------------------- 1 | \section{محاسبه برش پانچ در \lr{FreeCAD}} 2 | 3 | \subsection{بارگذاری ورک بنچ \lr{Civil}\label{sec:loadingcivil}} 4 | بعد از باز کردن نرم افزار 5 | \lr{FreeCAD} 6 | ، مطابق شکل 7 | \ref{fig:civil-workbench} 8 | با کلیک روی منوی کرکره ای ورک بنچ ها، از لیست موجود 9 | \lr{Civil} 10 | را انتخاب کنید. با این کار منو و آیکون های نرم افزار برش پانچ ظاهر میشوند. برای اینکه هر دفعه پس از بازکردن نرم افزار نیاز به این کار نداشته باشید به قسمت 11 | \ref{faq} 12 | مراجعه کنید. 13 | 14 | \begin{figure}[H] 15 | \centering 16 | \includegraphics[width=\linewidth]{figures/civil} 17 | \caption{بارگذاری ورک بنچ \lr{Civil}} 18 | \label{fig:civil-workbench} 19 | \end{figure} 20 | \subsection{بازکردن فایل اکسل} 21 | برای فراخوانی فایل اکسلی که در بخش 22 | \ref{sec:prepare-safe} 23 | ساختیم، کافیست که آنرا مثل یک فایل اکسل در نرم افزار باز کنید. یعنی از منوی 24 | $File \rightarrow Open$ 25 | این کار را انجام دهید. در این مرحله حتی نیاز به فراخوانی ورک بنچ 26 | \lr{Civil} 27 | نمی باشد. بعد از چند لحظه فنداسیون داخل نرم افزار بارگذاری شده و محاسبات پانچ برای تمامی ستونها صورت میگیرد. 28 | 29 | \subsection{ذخیره پروژه} 30 | در ورژن جدید میتوانید به سادگی مثل سایر نرم افزارها پروژه را ذخیره و باز کنید. بعد از بازکردن پروژه های ذخیره شده میتوانید ویرایش های لازم را انجام دهید. این قابلیت در 31 | ورژن های قبلی نرم افزار موجود نبود. -------------------------------------------------------------------------------- /help/help.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/help/help.pdf -------------------------------------------------------------------------------- /help/help.tex: -------------------------------------------------------------------------------- 1 | \documentclass[hidelinks, a4paper,twoside]{punch_report} 2 | \usepackage{array, boldline, makecell, booktabs} 3 | \newcommand\btrule[1]{\specialrule{#1}{0pt}{0pt}} 4 | \usepackage{colortbl} 5 | \usepackage{multicol,caption} % three-column layout 6 | \usepackage[font={footnotesize}]{caption} 7 | \usepackage{xcolor} 8 | \usepackage{amsmath} 9 | % \usepackage{ae} 10 | \usepackage{geometry} 11 | \usepackage{multirow} 12 | \usepackage{siunitx} 13 | \usepackage[title]{appendix} 14 | \usepackage{longtable} 15 | \usepackage{hyperref} 16 | \usepackage{flafter} 17 | \usepackage{afterpage} 18 | \usepackage{varioref} 19 | % \usepackage[utf8]{inputenc} 20 | \usepackage{graphicx} 21 | \usepackage{setspace} 22 | \setstretch{2} 23 | \usepackage[many]{tcolorbox} 24 | \usepackage{float} 25 | \usepackage{xcolor} 26 | \hypersetup{ 27 | colorlinks, 28 | linkcolor={blue!70!black}, 29 | citecolor={blue!50!black}, 30 | urlcolor={blue!80!black} 31 | } 32 | \usepackage{xepersian-multiplechoice} 33 | \usepackage{xepersian} 34 | \settextfont[Scale=1.4]{XB Niloofar} 35 | % \setlatintextfont[Scale=1.3]{Times New Roman} 36 | %\usepackage{draftwatermark} 37 | %\SetWatermarkText{Draft} 38 | %\SetWatermarkScale{1} 39 | 40 | % \newenvironment{Figure} 41 | % {\par\medskip\noindent\minipage{\linewidth}} 42 | % {\endminipage\par\medskip} 43 | 44 | 45 | % \title{دفترچه محاسبات سازه} 46 | % \newcommand{\punch}{نرم افزار برش پانچ} 47 | % \newcommand{\address}{شهرک شکوهیه فاز 2 خ بابایی خ تهرانی مقدم نبش فرعی 5} 48 | % \author{ابراهیم رعیت رکن آبادی} 49 | % % \date{} 50 | % \today{} 51 | 52 | %\vspace{3cm}\textbf{NOTE: This preliminary report is simply deemed suitable for the purpose of pre-dimensioning the structures addressed and obtaining the loads transmitted by them upon the foundation. The equipment modelling doesn't reflect its final geometric characteristics.}} 53 | % \renewcommand{\revision}{1.0} 54 | 55 | \begin{document} 56 | % \begin{center} 57 | % بنام خدا \newline 58 | % \maketitle 59 | % \vspace{10cm} 60 | % مالک: شرکت سلامت گستران پردیس پارت 61 | % \newline 62 | % پلاک ثبتی: 63 | % \end{center} 64 | 65 | 66 | % \newpage 67 | % \renewcommand{\abstractname}{Executive Summary}. 68 | % \maxdeadcycles=200 69 | 70 | % %\maketitle 71 | % \tableofcontents 72 | % \listoftables 73 | % \listoffigures 74 | % \newpage 75 | \input{cover} 76 | \pagenumbering{arabic} 77 | \input{introduction} 78 | % \begin{twocolumn} 79 | \input{safe} 80 | \input{freecad} 81 | \input{modify} 82 | \input{export} 83 | \input{update} 84 | \input{faq} 85 | 86 | % \end{twocolumn} 87 | % \section{Combination of loads} 88 | % \input{combinations.tex} 89 | % \input{xc_design_technique.tex} 90 | 91 | % \clearpage 92 | % \input{appendix} 93 | 94 | % \clearpage 95 | % ----- 96 | %------------------------------------------------ 97 | 98 | %---------------------------------------------------------------------------------------- 99 | % REFERENCE LIST 100 | %---------------------------------------------------------------------------------------- 101 | %\phantomsection 102 | %\nocite{OpenSeesManual,FeynmanVolI,Thomson} % writes also non-cited references 103 | % \nocite{*} 104 | % \bibliography{jubail} %file .bib 105 | % \bibliographystyle{plain} %normal style - listed in ABC order and labeled numerically 106 | %\bibliographystyle{unsrt} %same as plain except entries appear in order of citation 107 | 108 | % to compile the document run: 109 | % latex structural_design_report to create the .aux file 110 | % bibtex structural_design_report to get some of the citations and create a .bbl file 111 | % latex structural_design_report again so that the cross references between latex file and bibliography 112 | % are correct 113 | 114 | %---------------------------------------------------------------------------------------- 115 | 116 | \end{document} 117 | -------------------------------------------------------------------------------- /help/import_model.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | undefined 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |

ورود مدل به داخل نرم افزار

30 |

با زدن آیکن ایتبز پنجره زیر برای کاربر نمایش داده میشود. قبل از این کار سازه حتما باید آنالیز شده باشد، نیاز نیست که کار طراحی انجام شده باشد.

31 |

ورود مدل از ایتبز

32 |

ورود تیرها و ستونها

33 |

شما میتوانید تیرهای یک طبقه را به داخل مدل فری کد وارد کنید. برای این کار میتوانید طبقه مورد نظر خود را از لیست‌انتخاب کنید.

34 |

سه گزینه برای تیرها داریم:

35 | 41 |

ساخت فایل ورودی سیف

42 |

نرم افزار اسیف قادر است فایل ورودی سیف را به طور خودکار ایجاد نماید. یعنی دیگر کاربر نیاز به گرفتن خروجی فایل سیف از نرم افزار ایتبز نمی باشد. 43 | نرم افزار به طور خودکار تمام گره هایی که مقید شده اند را به همراه عکس العمل های آنها به داخل نرم افزار منتقل میکند. در این قسمت اگر فنداسیون در ۲ یا چند سطح باشد، تمام گره ها در ترازی که کاربر در کادر مربوطه وارد میکند ایجاد میشود.

44 |

بعد از تایید این پنجره عملیات انتقال مدل از ایتبز به داخل نرم افزار شروع میشود.

45 |
46 |
47 | 48 |
49 | 50 | -------------------------------------------------------------------------------- /help/import_model.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # ورود مدل به داخل نرم افزار 4 | 5 | با زدن آیکن ایتبز پنجره زیر برای کاربر نمایش داده میشود. قبل از این کار سازه حتما باید آنالیز شده باشد، نیاز نیست که کار طراحی انجام شده باشد. 6 | 7 | ![ورود مدل از ایتبز](figures/import_model_from_etabs.png) 8 | 9 | ## ورود تیرها و ستونها 10 | شما میتوانید تیرهای یک طبقه را به داخل مدل فری کد وارد کنید. برای این کار میتوانید طبقه مورد نظر خود را از لیست‌انتخاب کنید. 11 | 12 | سه گزینه برای تیرها داریم: 13 | 14 | - همه تیرها: همه تیرهای طبقه را وارد میکند 15 | - تیرهای انتخاب شده: فقط تیرهای انتخاب شده در آن طبقه را وارد میکند 16 | - همه تیرها بجز تیرهای انتخاب شده: به غیر از تیرهای انتخاب شده، تمام تیرهای یک طبقه را وارد میکند. 17 | در این قسمت اگر نیم طبقه دارید، بهتر است در مدل ایتبز، تیرهای نیم طبقه را انتخاب کنید و از گزینه سوم استفاده کنید. چون در غیر اینصورت ممکن است چند تیر رویهم قرار بگیرند و در مراحل بعدی نرم افزار دچار ایراد شود. اگر هم چند تیر رویهم وارد مدل شد، میتوانید بعد از ورود مدل نیز براحتی آنها را پاک کنید. 18 | 19 | ## ساخت فایل ورودی سیف 20 | نرم افزار اسیف قادر است فایل ورودی سیف را به طور خودکار ایجاد نماید. یعنی دیگر کاربر نیاز به گرفتن خروجی فایل سیف از نرم افزار ایتبز نمی باشد. 21 | نرم افزار به طور خودکار تمام گره هایی که مقید شده اند را به همراه عکس العمل های آنها به داخل نرم افزار منتقل میکند. در این قسمت اگر فنداسیون در ۲ یا چند سطح باشد، تمام گره ها در ترازی که کاربر در کادر مربوطه وارد میکند ایجاد میشود. 22 | 23 | بعد از تایید این پنجره عملیات انتقال مدل از ایتبز به داخل نرم افزار شروع میشود. 24 | 25 |
26 | -------------------------------------------------------------------------------- /help/introduction.tex: -------------------------------------------------------------------------------- 1 | \section*{مقدمه} 2 | 3 | محاسبه برش پانچ در نرم افزار سیف به صورت صحیح همیشه یکی از دغدغه های مهندسین عمران بوده است. خود من همیشه برای محاسبه پانچ با مشکل روبرو می شدم. بعد از آشنایی با نرم افزار 4 | \lr{FreeCAD} 5 | و بررسی قابلیت های آن تصمیم گرفتم نرم افزار برش پانچ را با استفاده از ابزارهای قدرتمند این نرم افزار ایجاد کنم. مهمترین مشکل این کار، تشخیص درست صفحات پانچ و موقعیت ستون بود که به لطف خدا الگوریتم 6 | بسیار دقیقی برای این کار نوشتم که در تمامی موارد صفحات و موقعیت ستون ها را به درستی تشخیص میدهد. 7 | 8 | 9 | نرم افزار حاضر، نرم افزاری کدباز برای محاسبه برش پانچ فنداسیون با استفاده از خروجی نرم افزار سیف می باشد. این نرم افزار با تلاش های شبانه روزی تهیه شده است و در توسعه آن سعی شده است که ضمن داشتن رابط کاربری آسان، 10 | نتایج نرم افزار از دقت بسیار بالایی برخوردار باشد. امیدوارم که برای مهندسین عزیز مفید باشد. 11 | 12 | 13 | 14 | کانال تلگرام: \lr{@civiltools} \newline 15 | آی دی تلگرام: \lr{@roknabadi} 16 | 17 | \newpage -------------------------------------------------------------------------------- /help/make_auto_strip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | undefined 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |

ایجاد استریپ ها

30 |

برای ساخت استریپ ها، فنداسیون باید ایجاد شده باشد. در این مرحله بعد از انتخاب آیکن ساخت 31 | استریپ، پنجره زیر نمایش داده میشود:

32 |

ساخت استریپ

33 |

ساخت استریپ در فنداسیون های گسترده

34 |

در فنداسیون های گسترده شما میتوانید موارد زیر را انتخاب کنید:

35 | 40 |

ایجاد استریپ در فنداسیون های نواری

41 |

در فنداسیون های نواری، نرم افزار به طور خودکار از روی بیس فنداسیون ها که در ابتدا ترسیم شده اند، استفاده میکند و نیاز به وارد کردن عرض و یا نام نوارها نمی باشد.

42 | 45 | 46 |
47 | 48 | -------------------------------------------------------------------------------- /help/make_auto_strip.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # ایجاد استریپ ها 4 | برای ساخت استریپ ها، فنداسیون باید ایجاد شده باشد. در این مرحله بعد از انتخاب آیکن ساخت 5 | استریپ، پنجره زیر نمایش داده میشود: 6 | 7 | ![ساخت استریپ](figures/make_auto_strip.png) 8 | 9 | ## ساخت استریپ در فنداسیون های گسترده 10 | 11 | در فنداسیون های گسترده شما میتوانید موارد زیر را انتخاب کنید: 12 | 13 | - نام نوارها در راستای x, y 14 | - عرض نوارها در راستای x, y 15 | - با انتخاب Equal Width عرض نوارها به طور مساوی تقسیم بندی میشود، در غیر اینصورت تمام نوارها با عرض انتخابی کاربر ایجاد میشود، به غیر از نوار آخری، که عرض باقیمانده به آن نوار اختصاص داده میشود. 16 | 17 | ## ایجاد استریپ در فنداسیون های نواری 18 | 19 | در فنداسیون های نواری، نرم افزار به طور خودکار از مشخصات بیس فنداسیون ها که در ابتدا ترسیم شده اند، برای رسم استریپ ها استفاده میکند و نیاز به وارد کردن عرض و یا نام نوارها نمی باشد. 20 | 21 | - گزینه Draw Lateral برای ترسیم نوارهای عرضی استفاده میشود که در حال حاضر غیرفعال می باشد. -------------------------------------------------------------------------------- /help/modify.tex: -------------------------------------------------------------------------------- 1 | \section{تغییر مشخصات پانچ ها} 2 | 3 | نرم افزار برش پانچ به گونه ای نوشته شده است که تقریبا در تمامی موارد پانچ ستونها را بدرستی تشخیص داده و نسبت آنها را محاسبه میکند. با این حال برای اینکه کاربر بتواند کنترل بیشتری روی 4 | پارامترهای مختلف محاسبه پانچ داشته باشد، ارتفاع فنداسیون، مقاومت بتن فنداسیون، ابعاد ستونها و موقعیت ستونها در نرم افزار قابل ویرایش هستند. 5 | با تغییر هر یک از پارامترها پانچ تمامی ستونها مجددا محاسبه میشود. ممکن است ستونی که به صورت کنار شناخته شده است با افزایش ضخامت فنداسیون به صورت گوشه شناخته شود و در نتیجه 6 | نسبت تنش پانچ آن افزایش یابد! 7 | 8 | \subsection{تغییر مشخصات فنداسیون} 9 | ضخامت، کاور و مقاومت بتن فنداسیون را میتوان در نرم افزار تغییر داد. برای این کار کافیست که در محیط سه بعدی نرم افزار روی فنداسیون کلیک کنید. با این کار در جدول سمت چپ با نام 10 | \lr{Combo View} 11 | مشخصات فنداسیون به نمایش در می آید (شکل 12 | \ref{fig:comboview} 13 | ). اگر مشخصات قابل رویت نیست به بخش سوالات متداول مراجعه کنید. 14 | % \ref{faq:dataview} 15 | 16 | \begin{figure}[H] 17 | \centering 18 | \includegraphics{figures/comboview} 19 | \caption{تغییر مشخصات فنداسیون} 20 | \label{fig:comboview} 21 | \end{figure} 22 | 23 | \subsection{تغییر مشخصات ستونها} 24 | ابعاد و موقعیت ستونها را میتوان تغییر داد. برای این کار در محیط سه بعدی نرم افزار روی هر کدام از ستونها که قصد تغییر مشخصات آنرا دارید کلیک کنید. 25 | با این کار در جدول سمت چپ مشخصات ستون ظاهر میشود که میتوان ابعاد ستون، 26 | $b_x, b_y$ 27 | و موقعیت ستون، 28 | \lr{Location} 29 | را تغییر داد. برای تغییر ابعاد ستون کافیست که عدد مورد نظر خود را برای ابعاد ستون وارد کنید. 30 | برای تغییر موقعیت ستون ابتدا باید پارامتر 31 | \lr{user modified} 32 | را از حالت 33 | \lr{false} 34 | به 35 | \lr{true} 36 | تغییر دهید و سپس موقعیت جدید ستون را با پارامتر 37 | \lr{Location} 38 | تغییر دهید. دقت کنید که فقط میتوان از پانچ وسط به کنار و گوشه 39 | و از پانچ کنار به گوشه تغییر موقعیت داد. چون باید صفحات مناسب پانچ برای تشخیص وجود داشته باشد. 40 | یعنی نرم افزار با تغییر موقعیت ستون، یک یا دو صفحه پانچ را حذف میکند، ولی نمیتوان صفحه ای که وجود ندارد را به آن اضافه نمود! 41 | 42 | \begin{itemize} 43 | \item نکته: بجای اینکه ویرایش را جدول کنار نرم افزار انجام دهید میتوانید با دابل کلیک روی اسم هر کدام از پانچ ها در قسمت بالایی جدول 44 | \lr{Combo View} 45 | این کار را انجام دهید. پس از دابل کلیک روی نام پانچ مورد نظر، یک پنجره مطابق شکل 46 | \ref{fig:columnedit} 47 | باز میشود که میتوانید موقعیت و ابعاد ستون را در آن تغییر دهید. در این پنجره نیز برای تغییر موقعیت ستون تیک مورد نظر را بزنید. 48 | \end{itemize} 49 | 50 | \begin{figure}[H] 51 | \centering 52 | \includegraphics{figures/columnedit} 53 | \caption{پنجره تغییر مشخصات ستون} 54 | \label{fig:columnedit} 55 | \end{figure} 56 | -------------------------------------------------------------------------------- /help/punch_report.cls: -------------------------------------------------------------------------------- 1 | % This document class provides a simple memo for LaTeX users. 2 | % It is based on article.cls and inherits most of the functionality 3 | % that class. 4 | % 5 | % Author: Rob Oakes, Copyright 2010. Released under the LGPL, version 3. 6 | % A copy of the LGPL can be found at http://www.gnu.org/licenses/lgpl.html 7 | 8 | \NeedsTeXFormat{LaTeX2e} 9 | \ProvidesClass{punch_report}[2010/07/31 - Simple Report Class, Including Logo] 10 | \RequirePackage{palatino} 11 | \RequirePackage{fancyhdr} 12 | \RequirePackage{geometry} 13 | \RequirePackage{lastpage} 14 | \RequirePackage{svg} 15 | 16 | \newcommand{\revision}{0.0} 17 | \newcommand{\reportLogo}{\includesvg[height=10mm]{figures/punch}} 18 | %%\newcommand{\reportLogo}{\includesvg{xc_report_logo}} 19 | 20 | % Load the Base Class 21 | \LoadClassWithOptions{article} 22 | \usepackage{geometry} 23 | \geometry{hmargin={2.5cm,1.5cm},vmargin={3cm,3cm}} 24 | 25 | \pagestyle{fancy} 26 | \fancypagestyle{plain}{ 27 | \fancyhf{} %anula los valores de fancy por defecto 28 | 29 | \fancyfoot[LE]{صفحه \thepage\ از \pageref{LastPage}} 30 | \fancyfoot[CE]{\reportLogo} 31 | % \fancyfoot[RE]{\emph{rev. \revision}} 32 | 33 | \fancyfoot[LO]{\emph{\date}} 34 | \fancyfoot[CO]{\reportLogo} 35 | \fancyfoot[RO]{صفحه \thepage\ از \pageref{LastPage}} 36 | \renewcommand{\headrulewidth}{0pt} %Dibuja una raya debajo de la cabecera 37 | \renewcommand{\footrulewidth}{0pt} 38 | } 39 | 40 | \renewcommand{\headrulewidth}{0pt} %Dibuja una raya debajo de la cabecera 41 | \renewcommand{\footrulewidth}{0pt} 42 | % \textheight= 22cm %%Espacio vertical para el texto. 43 | %\textwidth=16cm 44 | \marginparwidth=2mm 45 | 46 | \fancyhf{} %anula los valores de fancy por defecto 47 | 48 | \fancyhead[LE]{\textsc{\karfarma}} 49 | \fancyhead[RO]{\leftmark} 50 | \fancyhead[RO]{\textsc{\rightmark}} 51 | 52 | \fancyfoot[LE]{صفحه \thepage\ از \pageref{LastPage}} 53 | \fancyfoot[CE]{\reportLogo} 54 | % \fancyfoot[RE]{\emph{rev. \revision}} 55 | 56 | \fancyfoot[LO]{\emph{\date}} 57 | \fancyfoot[CO]{\reportLogo} 58 | \fancyfoot[RO]{صفحه \thepage\ از \pageref{LastPage}} 59 | 60 | \newcommand{\developer}{ابراهیم رعیت رکن آبادی} 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /help/safe.tex: -------------------------------------------------------------------------------- 1 | \section{ساخت فایل خروجی اکسل در سیف\label{sec:prepare-safe}} 2 | مراحلی که در زیر آمده است برای آماده سازی فایل نرم افزار برش پانچ کفایت میکند. بنابراین نیاز به انجام کار اضافی نیست. مثلا نیاز به تخصیص سختی خاک، المانهای ستون 3 | \lr{(Stiff)} 4 | و ... نمی باشد. 5 | 6 | 7 | \subsection{ترسیم هندسه پی} 8 | در این مرحله فقط هندسه پی مطابق شکل 9 | \ref{geometry} 10 | ترسیم می شود. سعی کنید در این مرحله تمیزکاری زیادی نکنید. منظورم این هست که 11 | \textbf{هم پوشانی} 12 | نوارهای فنداسیون چه در سیف و چه در نرم افزار برش پانچ مشکلی ایجاد نمیکند، 13 | بنابراین سعی نکنید که لبه های نوارها را بهم بچسبانید، بلکه اجازه دهید مقداری هم پوشانی در نوارها ایجاد شود. 14 | برای ترسیم پی های نواری میتوانید از 15 | \textbf{بازشو} 16 | هم استفاده کنید و نرم افزار برش پانچ با ترسیم بازشو مشکلی ندارد و محاسبات پانچ به درستی انجام میگیرد. 17 | 18 | \begin{figure}[H] 19 | \centering 20 | \includegraphics[scale=.6]{figures/geometry2} 21 | \caption{ترسیم هندسه پی در سیف} 22 | \label{geometry} 23 | \end{figure} 24 | 25 | 26 | \subsection{اختصاص مقطع به پی} 27 | در این مرحله از منوی 28 | $Assign \rightarrow Slab Data \rightarrow Properties$ 29 | به هندسه ترسیم شده مقطع مناسب را اختصاص دهید (شکل 30 | \ref{assign}): 31 | 32 | \begin{figure}[H] 33 | \centering 34 | \includegraphics[scale=.6]{figures/assign} 35 | \caption{اختصاص مقطع به پی} 36 | \label{assign} 37 | \end{figure} 38 | 39 | 40 | \subsection{خروجی به اکسل} 41 | مطابق شکل 42 | \ref{excel} 43 | از منوی 44 | $File \rightarrow Export Model \rightarrow Excel $ 45 | خروجی به اکسل را انتخاب کنید. مطابق شکل 46 | \ref{excel2} 47 | تیک بخش 48 | \lr{MODEL DEFINITION} 49 | را بزنید و 50 | در قسمت 51 | \lr{Select Load Patterns} 52 | تمامی الگوی بارها را انتخاب و واحد خروجی را 53 | $KN, mm, C$ 54 | برگزینید. فایل را در محل دلخواه ذخیره کنید. 55 | 56 | \begin{figure}[H] 57 | \centering 58 | \includegraphics[width=.7\linewidth]{figures/excel} 59 | \caption{خروجی به اکسل} 60 | \label{excel} 61 | \end{figure} 62 | 63 | \begin{figure}[H] 64 | \centering 65 | \includegraphics[width=.7\linewidth]{figures/excel2} 66 | \caption{تنظیم پارامترهای خروجی فایل اکسل} 67 | \label{excel2} 68 | \end{figure} -------------------------------------------------------------------------------- /help/update.tex: -------------------------------------------------------------------------------- 1 | \section{آپدیت نرم افزار} 2 | برای آپدیت به آخرین تغییرات نرم افزار کافیست که روی آیکن شکل چرخ دنده کلیک کنید. برای اولین بار احتمالا حدود ۲-۳ دقیقه برای آپدیت نرم افزار زمان نیاز است. 3 | بعد از اتمام نصب، پوشه 4 | \lr{Civil} 5 | در محل نصب نرم افزار را پاک کنید. به طور معمول پوشه 6 | \lr{Civil} 7 | در مسیر زیر قرار دارد: 8 | 9 | \begin{center} 10 | 11 | \lr{C:\textbackslash Program Files\textbackslash FreeCAD\textbackslash mod} 12 | \end{center} 13 | 14 | 15 | نرم افزار را مجددا راه اندازی کنید. برای آپدیت های بعدی نیاز به پاک کردن فایل نیست، چون اصلا فایلی وجود ندارد! 16 | -------------------------------------------------------------------------------- /old_punch/Help.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/old_punch/Help.pdf -------------------------------------------------------------------------------- /old_punch/foundraw/pyconcreteWelcome.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2017 Yorik van Havre * 4 | #* * 5 | #* This program is free software; you can redistribute it and/or modify * 6 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 7 | #* as published by the Free Software Foundation; either version 2 of * 8 | #* the License, or (at your option) any later version. * 9 | #* for detail see the LICENCE text file. * 10 | #* * 11 | #* This program is distributed in the hope that it will be useful, * 12 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | #* GNU Library General Public License for more details. * 15 | #* * 16 | #* You should have received a copy of the GNU Library General Public * 17 | #* License along with this program; if not, write to the Free Software * 18 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | #* USA * 20 | #* * 21 | #*************************************************************************** 22 | 23 | """This module contains FreeCAD commands for the pyconcrete workbench""" 24 | 25 | import os 26 | import FreeCAD 27 | import FreeCADGui 28 | 29 | from PySide import QtCore, QtGui 30 | 31 | 32 | def QT_TRANSLATE_NOOP(ctx, txt): return txt # dummy function for the QT translator 33 | 34 | 35 | class PyconcreteWelcome: 36 | 37 | def GetResources(self): 38 | 39 | return {'Pixmap': os.path.join(os.path.dirname(__file__), "icons", "pyconcrete_Welcome.svg"), 40 | 'MenuText': QT_TRANSLATE_NOOP("pyconcrete_Welcome", "pyconcrete Welcome screen"), 41 | 'ToolTip': QT_TRANSLATE_NOOP("pyconcrete_Welcome", "Show the pyconcrete workbench welcome screen")} 42 | 43 | def Activated(self): 44 | 45 | # load dialog 46 | self.form = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), "ui", "dialogWelcome.ui")) 47 | 48 | # set the title image 49 | self.form.image.setPixmap(QtGui.QPixmap(os.path.join(os.path.dirname(__file__), "icons", "banner.png"))) 50 | 51 | # handle the tutorial link 52 | # QtCore.QObject.connect(self.form.label_4, QtCore.SIGNAL("linkActivated(QString)"), self.launchTutorial) 53 | 54 | # center the dialog over FreeCAD window 55 | mw = FreeCADGui.getMainWindow() 56 | self.form.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center()) 57 | 58 | # show dialog and run setup dialog afterwards if OK was pressed 59 | result = self.form.exec_() 60 | if result: 61 | FreeCADGui.runCommand("Pyconcrete_Setup") 62 | 63 | # remove first time flag 64 | FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Pyconcrete").SetBool("FirstTime", False) 65 | 66 | # def launchTutorial(self,link): 67 | 68 | # if hasattr(self,"form"): 69 | # self.form.hide() 70 | # FreeCADGui.runCommand("Pyconcrete_Tutorial") 71 | -------------------------------------------------------------------------------- /old_punch/test_safe.py: -------------------------------------------------------------------------------- 1 | from safe import Safe 2 | import unittest 3 | 4 | 5 | class TestSafe(unittest.TestCase): 6 | def setUp(self): 7 | self.safe = Safe("test_files/sattari_safe.xlsx") 8 | 9 | def test_program_control(self): 10 | self.assertEqual(self.safe.program_name, "SAFE 2016") 11 | self.assertEqual(self.safe.version, "16.0.1") 12 | self.assertEqual(self.safe.curr_units.force, "N") 13 | self.assertEqual(self.safe.curr_units.length, " mm") 14 | self.assertEqual(self.safe.curr_units.temp, " C") 15 | self.assertEqual(self.safe.concrete_code, "ACI 318-14") 16 | 17 | def test_obj_geom_points(self): 18 | points = self.safe.obj_geom_points 19 | no_of_points = len(list(points.keys())) 20 | coord = points[29] 21 | self.assertEqual(no_of_points, 84) 22 | self.assertEqual(coord.x, 0) 23 | self.assertEqual(coord.y, 5.1) 24 | self.assertEqual(coord.z, 0) 25 | self.assertEqual(coord.special, True) 26 | 27 | coord = points[72] 28 | self.assertAlmostEqual(coord.x, 4.46, places=2) 29 | self.assertAlmostEqual(coord.y, 11.034, places=3) 30 | self.assertEqual(coord.z, 0) 31 | self.assertEqual(coord.special, False) 32 | 33 | def test_obj_geom_areas(self): 34 | areas = self.safe.obj_geom_areas 35 | no_of_areas = len(list(areas.keys())) 36 | self.assertEqual(no_of_areas, 6) 37 | point_numbers = areas[11] 38 | self.assertEqual(point_numbers, [76, 75, 73, 72]) 39 | 40 | stiff = self.safe.obj_geom_stiff 41 | point_numbers = stiff[20] 42 | self.assertEqual(point_numbers, [112, 113, 114, 115]) 43 | 44 | def test_point_loads(self): 45 | point_loads = self.safe.point_loads 46 | self.assertEqual(len(point_loads), 10) 47 | no_of_loads = len(list(point_loads[29]['loads'].keys())) 48 | self.assertEqual(no_of_loads, 27) 49 | # point 29 50 | load = point_loads[29]['loads']['Dead_ABOVE'] 51 | self.assertAlmostEqual(load.fx, -6.667899, places=5) 52 | self.assertAlmostEqual(load.fy, -8.647991, places=5) 53 | self.assertAlmostEqual(load.fz, 284.2031, places=4) 54 | self.assertAlmostEqual(load.mx, 9.956049, places=5) 55 | self.assertAlmostEqual(load.my, -7.921433, places=5) 56 | self.assertAlmostEqual(load.mz, 0.004490824, places=9) 57 | self.assertEqual(point_loads[29]['xdim'], 400) 58 | self.assertEqual(point_loads[29]['ydim'], 400) 59 | # point 49 EYALL-0.3EX(3/3)_ABOVE load 60 | load = point_loads[49]['loads']['EYALL-0.3EX(3/3)_ABOVE'] 61 | self.assertAlmostEqual(load.fx, -9.586596, places=5) 62 | self.assertAlmostEqual(load.fy, 37.83184, places=5) 63 | self.assertAlmostEqual(load.fz, 62.41515, places=5) 64 | self.assertAlmostEqual(load.mx, -113.0372, places=4) 65 | self.assertAlmostEqual(load.my, -31.61491, places=5) 66 | self.assertAlmostEqual(load.mz, 0.3031819, places=7) 67 | self.assertEqual(point_loads[49]['xdim'], 400) 68 | self.assertEqual(point_loads[49]['ydim'], 400) 69 | 70 | class TestSafe_safdari(unittest.TestCase): 71 | def setUp(self): 72 | self.safe = Safe("test_files/safdari.xlsx") 73 | 74 | def test_solid_slabs(self): 75 | solid_slabs = self.safe.solid_slabs 76 | slab_prop = solid_slabs['COL'] 77 | self.assertEqual(slab_prop.type, 'Stiff') 78 | self.assertEqual(slab_prop.matProp, 'C35') 79 | self.assertEqual(slab_prop.thickness, 3000) 80 | 81 | def test_slab_prop_assignment(self): 82 | slab_prop_assignment = self.safe.slab_prop_assignment 83 | self.assertEqual(slab_prop_assignment[9], 'SLAB70') 84 | self.assertEqual(slab_prop_assignment[31], 'COL') 85 | self.assertEqual(slab_prop_assignment[56], 'SLAB55') 86 | 87 | def test_concrete_mat(self): 88 | concrete_mat = self.safe.concrete_mat 89 | self.assertAlmostEqual(concrete_mat['C35'], 3.56900667277847, places=10) 90 | 91 | class TestSafe_safdari(unittest.TestCase): 92 | def setUp(self): 93 | self.safe = Safe("test_files/davoodabadi_dynamic.xlsx") 94 | 95 | def test_obj_geom_all_areas(self): 96 | areas = self.safe.obj_geom_areas 97 | no_of_areas = len(list(areas.keys())) 98 | self.assertEqual(no_of_areas, 8) 99 | point_numbers = areas[12] 100 | self.assertEqual(point_numbers, [257, 256, 255, 254, 253]) 101 | 102 | point_numbers = areas[11] 103 | self.assertEqual(point_numbers, [252, 251, 250, 249]) 104 | 105 | point_numbers = areas[106] 106 | self.assertEqual(point_numbers, [465, 4, 461, 25, 462, 463, 464, 427, 428]) 107 | 108 | 109 | def test_grid_lines(self): 110 | pass 111 | # grid_lines = self.safe.grid_lines() 112 | # no_of_x_grid_line = len(grid_lines['x']) 113 | # no_of_y_grid_line = len(grid_lines['y']) 114 | # self.assertEqual(no_of_x_grid_line, 5) 115 | # self.assertEqual(no_of_y_grid_line, 4) 116 | 117 | def test_load_cases(self): 118 | pass 119 | 120 | def test_load_combinations(self): 121 | pass 122 | 123 | def test_punching_shear(self): 124 | pass 125 | 126 | def test_slab_prop(self): 127 | pass 128 | 129 | def soil_prop(self): 130 | pass 131 | 132 | 133 | -------------------------------------------------------------------------------- /osafe_draw/draw_beam.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | from PySide2 import QtCore 4 | from PySide2.QtCore import QT_TRANSLATE_NOOP 5 | 6 | import FreeCAD 7 | import FreeCADGui as Gui 8 | import Draft 9 | import DraftVecUtils 10 | 11 | from draftutils.translate import translate 12 | from draftguitools.gui_lines import Line 13 | 14 | 15 | class Beam(Line): 16 | """Gui command for the Beam tool.""" 17 | 18 | def __init__(self, wiremode=True): 19 | super().__init__() 20 | self.isWire = wiremode 21 | 22 | def GetResources(self): 23 | menu_text = QtCore.QT_TRANSLATE_NOOP( 24 | "osafe_draw_beam", 25 | "Beam") 26 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 27 | "osafe_draw_beam", 28 | "Draw Beam") 29 | path = str( 30 | Path(__file__).parent.parent / "osafe_images" / "beam.svg" 31 | ) 32 | return {'Pixmap': path, 33 | 'MenuText': menu_text, 34 | 'ToolTip': tool_tip} 35 | 36 | def finish(self, closed=False, cont=False): 37 | """Terminate the operation and close the polyline if asked. 38 | 39 | Parameters 40 | ---------- 41 | closed: bool, optional 42 | Close the line if `True`. 43 | """ 44 | self.removeTemporaryObject() 45 | if self.oldWP: 46 | FreeCAD.DraftWorkingPlane = self.oldWP 47 | if hasattr(Gui, "Snapper"): 48 | Gui.Snapper.setGrid() 49 | Gui.Snapper.restack() 50 | self.oldWP = None 51 | 52 | if len(self.node) > 1: 53 | if closed == True: 54 | self.node.append(self.node[0]) 55 | cmd_list = ['from osafe_objects.beam import make_beam'] 56 | for p1, p2 in zip(self.node[:-1], self.node[1:]): 57 | if DraftVecUtils.equals(p1, p2): 58 | continue 59 | p1 = DraftVecUtils.toString(p1) 60 | p2 = DraftVecUtils.toString(p2) 61 | cmd_list.append(f'make_beam({p1}, {p2})') 62 | self.commit(translate("civil", "Create beam"), 63 | cmd_list) 64 | super(Line, self).finish() 65 | Gui.Selection.clearSelection() 66 | if self.ui and self.ui.continueMode: 67 | self.Activated() -------------------------------------------------------------------------------- /osafe_draw/draw_slab.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | from PySide2 import QtCore 4 | 5 | import FreeCAD 6 | import FreeCADGui as Gui 7 | import DraftVecUtils 8 | 9 | from draftutils.translate import translate 10 | from draftguitools.gui_lines import Line 11 | 12 | 13 | class Slab(Line): 14 | """Gui command for the Slab tool.""" 15 | 16 | def __init__(self, wiremode=True): 17 | super().__init__() 18 | self.isWire = wiremode 19 | 20 | def GetResources(self): 21 | menu_text = QtCore.QT_TRANSLATE_NOOP( 22 | "civil_slab", 23 | "Create slab") 24 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 25 | "civil_slab", 26 | "Create slab") 27 | path = str( 28 | Path(__file__).parent.parent / "osafe_images" / "slab.svg" 29 | ) 30 | return {'Pixmap': path, 31 | 'MenuText': menu_text, 32 | 'ToolTip': tool_tip} 33 | 34 | def finish(self, closed=False, cont=False): 35 | """Terminate the operation and close the polyline if asked. 36 | 37 | Parameters 38 | ---------- 39 | closed: bool, optional 40 | Close the line if `True`. 41 | """ 42 | self.removeTemporaryObject() 43 | if self.oldWP: 44 | FreeCAD.DraftWorkingPlane = self.oldWP 45 | if hasattr(Gui, "Snapper"): 46 | Gui.Snapper.setGrid() 47 | Gui.Snapper.restack() 48 | self.oldWP = None 49 | 50 | if len(self.node) > 2: 51 | Gui.addModule("Draft") 52 | # The command to run is built as a series of text strings 53 | # to be committed through the `draftutils.todo.ToDo` class. 54 | 55 | # Insert a Draft wire 56 | rot, sup, pts, fil = self.getStrings() 57 | 58 | _base = DraftVecUtils.toString(self.node[0]) 59 | _cmd = 'Draft.makeWire' 60 | _cmd += '(' 61 | _cmd += 'points, ' 62 | _cmd += 'placement=pl, ' 63 | _cmd += 'closed=' + str(closed) + ', ' 64 | _cmd += 'face=' + fil + ', ' 65 | _cmd += 'support=' + sup 66 | _cmd += ')' 67 | _cmd_list = [ 68 | 'pl = FreeCAD.Placement()', 69 | 'pl.Rotation.Q = ' + rot, 70 | 'pl.Base = ' + _base, 71 | 'points = ' + pts, 72 | 'line = ' + _cmd, 73 | 'Draft.autogroup(line)', 74 | 'FreeCAD.ActiveDocument.recompute()', 75 | 'from osafe_objects.slab import make_slab', 76 | 'make_slab(line)', 77 | 'line.ViewObject.hide()', 78 | ] 79 | self.commit(translate("civil", "Create Slab"), 80 | _cmd_list) 81 | super(Line, self).finish() 82 | Gui.Selection.clearSelection() 83 | if self.ui and self.ui.continueMode: 84 | self.Activated() 85 | 86 | Gui.addCommand('civil_slab', Slab()) 87 | -------------------------------------------------------------------------------- /osafe_gui/gui_automatic_rebars.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | 4 | from PySide2 import QtCore 5 | from PySide2.QtCore import QT_TRANSLATE_NOOP 6 | 7 | import FreeCAD 8 | import FreeCADGui as Gui 9 | 10 | 11 | class OsafeAutomaticRebar: 12 | """Gui command for the creating stirp automatically in mat foundations.""" 13 | 14 | def GetResources(self): 15 | menu_text = QtCore.QT_TRANSLATE_NOOP( 16 | "osafe", 17 | "Auto Rebar") 18 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 19 | "osafe", 20 | "Create Rebars automatically from strips") 21 | path = str( 22 | Path(__file__).parent.parent / "osafe_images" / "osafe_rebar.svg" 23 | ) 24 | return {'Pixmap': path, 25 | 'MenuText': menu_text, 26 | 'ToolTip': tool_tip} 27 | 28 | def Activated(self): 29 | from osafe_py_widgets import draw_automatic_rebars 30 | win = draw_automatic_rebars.Form() 31 | Gui.Control.showDialog(win) 32 | 33 | def IsActive(self): 34 | return not FreeCAD.ActiveDocument is None 35 | 36 | if FreeCAD.GuiUp: 37 | Gui.addCommand('osafe_automatic_rebars', OsafeAutomaticRebar()) -------------------------------------------------------------------------------- /osafe_images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/banner.jpg -------------------------------------------------------------------------------- /osafe_images/base_plate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 46 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 48 | 49 | 107 | -------------------------------------------------------------------------------- /osafe_images/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/cancel.png -------------------------------------------------------------------------------- /osafe_images/change_branch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /osafe_images/civil-engineering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/civil-engineering.png -------------------------------------------------------------------------------- /osafe_images/civiltools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/civiltools.png -------------------------------------------------------------------------------- /osafe_images/draw_strip.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 20 | 24 | 28 | 29 | 32 | 36 | 40 | 41 | 44 | 48 | 52 | 53 | 56 | 60 | 64 | 65 | 68 | 72 | 76 | 77 | 88 | 99 | 110 | 111 | 131 | 136 | 140 | 141 | -------------------------------------------------------------------------------- /osafe_images/dxf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 6 | 7 | -------------------------------------------------------------------------------- /osafe_images/etabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/etabs.png -------------------------------------------------------------------------------- /osafe_images/explode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 14 | 17 | 20 | 22 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /osafe_images/export.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 19 | 20 | 25 | 35 | -------------------------------------------------------------------------------- /osafe_images/f2k.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 65 | 67 | 69 | 71 | 73 | 75 | 77 | 79 | 81 | 83 | 85 | 87 | 89 | 91 | F2K 101 | -------------------------------------------------------------------------------- /osafe_images/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/help.png -------------------------------------------------------------------------------- /osafe_images/import.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 53 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 55 | 61 | 62 | -------------------------------------------------------------------------------- /osafe_images/opening.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 34 | 39 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /osafe_images/png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/png.png -------------------------------------------------------------------------------- /osafe_images/rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /osafe_images/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 70 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 72 | 79 | 80 | -------------------------------------------------------------------------------- /osafe_images/safe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/safe.png -------------------------------------------------------------------------------- /osafe_images/segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/segment.png -------------------------------------------------------------------------------- /osafe_images/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /osafe_images/trapozeidal.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /osafe_images/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/update.png -------------------------------------------------------------------------------- /osafe_images/update.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 62 | 63 | 65 | 66 | 68 | 69 | 71 | 72 | 74 | 75 | 77 | 78 | 80 | 81 | 83 | 84 | 86 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 | 101 | 102 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /osafe_images/wireframe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 6 | 7 | -------------------------------------------------------------------------------- /osafe_images/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/word.png -------------------------------------------------------------------------------- /osafe_images/xlsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_images/xlsx.png -------------------------------------------------------------------------------- /osafe_import_export/templates/punch_default.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/osafe_import_export/templates/punch_default.docx -------------------------------------------------------------------------------- /osafe_objects/beam.py: -------------------------------------------------------------------------------- 1 | import FreeCAD 2 | import Draft 3 | from osafe_funcs import osafe_funcs 4 | 5 | 6 | def make_beam(p1, p2): 7 | obj = Draft.make_line(p1, p2) 8 | obj.addProperty("App::PropertyString", "type").type = 'Beam' 9 | if FreeCAD.GuiUp: 10 | osafe_funcs.format_view_object( 11 | obj=obj, 12 | shape_color_entity="beam_shape_color", 13 | line_width_entity="beam_line_width", 14 | transparency_entity="beam_transparency", 15 | display_mode_entity="beam_display_mode", 16 | line_color_entity="beam_line_color", 17 | ) 18 | obj.ViewObject.PointSize = 4 19 | FreeCAD.ActiveDocument.recompute() 20 | return obj 21 | -------------------------------------------------------------------------------- /osafe_objects/colorbar.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtCore import * 2 | from PySide2.QtGui import * 3 | from PySide2.QtWidgets import * 4 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 5 | import matplotlib.pyplot as plt 6 | import matplotlib as mpl 7 | import numpy as np 8 | 9 | 10 | class ColorMap(QWidget): 11 | def __init__(self, minv, maxv, parent=None): 12 | self.max = maxv 13 | self.min = minv 14 | super(ColorMap, self).__init__(parent) 15 | self.figure = plt.figure() 16 | self.canvas = FigureCanvas(self.figure) 17 | 18 | self.draw_colormap() 19 | 20 | # set the layout 21 | layout = QVBoxLayout() 22 | layout.addWidget(self.canvas) 23 | self.setLayout(layout) 24 | 25 | def draw_colormap(self): 26 | # axes 27 | ax = self.figure.add_axes([0.05, 0.10, 0.2, 0.8]) 28 | cmap = mpl.cm.jet 29 | norm = mpl.colors.Normalize(vmin=self.min, vmax=self.max) 30 | ticks_cm = np.linspace(self.min, self.max, 10, endpoint=True) 31 | cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap, 32 | norm=norm, 33 | ticks=ticks_cm, 34 | orientation='vertical') 35 | # label_cm = 'punch ratio' 36 | # cb1.set_label(label=label_cm,weight='bold') 37 | cb1.ax.tick_params(labelsize=8) 38 | self.canvas.draw() 39 | -------------------------------------------------------------------------------- /osafe_objects/f2k_object.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from pathlib import Path 3 | 4 | import FreeCAD 5 | 6 | 7 | class SafeF2k: 8 | def __init__(self, obj): 9 | self.set_properties(obj) 10 | 11 | def set_properties(self, obj): 12 | self.Type = "f2k" 13 | obj.Proxy = self 14 | 15 | if not hasattr(obj, "input"): 16 | obj.addProperty( 17 | "App::PropertyFile", 18 | "input", 19 | "Safe", 20 | ) 21 | if not hasattr(obj, "input_str"): 22 | obj.addProperty( 23 | "App::PropertyString", 24 | "input_str", 25 | "Safe", 26 | ).input_str='' 27 | if not hasattr(obj, "output"): 28 | obj.addProperty( 29 | "App::PropertyFile", 30 | "output", 31 | "Safe", 32 | ) 33 | obj.setEditorMode('output', 1) 34 | obj.setEditorMode('input_str', 2) 35 | 36 | def execute(self, obj): 37 | input_ = obj.input 38 | obj.input = input_.replace('/', '\\') 39 | output = obj.output 40 | obj.output = output.replace('/', '\\') 41 | try: 42 | if Path(obj.input).exists(): 43 | with open(obj.input) as f: 44 | obj.input_str = f.read() 45 | except: 46 | pass 47 | 48 | def onDocumentRestored(self, obj): 49 | self.set_properties(obj) 50 | 51 | class ViewProviderF2k: 52 | def __init__(self, vobj): 53 | vobj.Proxy = self 54 | 55 | def getIcon(self): 56 | return str(Path(__file__).parent.parent / "osafe_images" / "f2k.svg") 57 | 58 | def __getstate__(self): 59 | return None 60 | 61 | def __setstate__(self, state): 62 | return None 63 | 64 | def make_safe_f2k( 65 | input : str='', 66 | output : Union[str, None] = None, 67 | ): 68 | obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Safe") 69 | SafeF2k(obj) 70 | obj.input = input 71 | if output is not None: 72 | obj.output = output 73 | if FreeCAD.GuiUp: 74 | ViewProviderF2k(obj.ViewObject) 75 | FreeCAD.ActiveDocument.recompute() 76 | return obj 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /osafe_objects/foundation.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Union 3 | 4 | import FreeCAD 5 | import FreeCADGui 6 | import Part 7 | 8 | 9 | class Foundation: 10 | def __init__(self, obj): 11 | self.set_properties(obj) 12 | 13 | def set_properties(self, obj): 14 | self.Type = "Foundation" 15 | obj.Proxy = self 16 | 17 | if not hasattr(obj, "fc"): 18 | obj.addProperty( 19 | "App::PropertyPressure", 20 | "fc", 21 | "Concrete", 22 | ) 23 | 24 | if not hasattr(obj, "height"): 25 | obj.addProperty( 26 | "App::PropertyLength", 27 | "height", 28 | "Foundation", 29 | ) 30 | 31 | if not hasattr(obj, "cover"): 32 | obj.addProperty( 33 | "App::PropertyLength", 34 | "cover", 35 | "Foundation", 36 | ) 37 | 38 | if not hasattr(obj, "d"): 39 | obj.addProperty( 40 | "App::PropertyLength", 41 | "d", 42 | "Foundation", 43 | ) 44 | 45 | if not hasattr(obj, "plan"): 46 | obj.addProperty( 47 | "Part::PropertyPartShape", 48 | "plan", 49 | "Foundation", 50 | ) 51 | 52 | obj.setEditorMode("d", 2) 53 | 54 | def onDocumentRestored(self, obj): 55 | self.set_properties(obj) 56 | 57 | def execute(self, obj): 58 | obj.d = obj.height - obj.cover 59 | sh = obj.plan.extrude(FreeCAD.Vector(0, 0, -(obj.d.Value))) 60 | obj.Shape = sh 61 | 62 | 63 | class ViewProviderFoundation: 64 | 65 | def __init__(self, vobj): 66 | 67 | vobj.Proxy = self 68 | vobj.Transparency = 40 69 | vobj.DisplayMode = "Shaded" 70 | 71 | 72 | def attach(self, vobj): 73 | self.ViewObject = vobj 74 | self.Object = vobj.Object 75 | 76 | def getIcon(self): 77 | return str(Path(__file__).parent.parent / "osafe_images" / "foundation.png") 78 | 79 | def __getstate__(self): 80 | return None 81 | 82 | def __setstate__(self, state): 83 | return None 84 | 85 | 86 | 87 | def make_foundation( 88 | base: Part.Shape = None, 89 | height: Union[float, str] = 1000, 90 | cover: Union[float, str] = 75, 91 | fc: Union[float, str] = 25 92 | ): 93 | if not base: 94 | base = FreeCADGui.Selection.getSelection()[0].Shape 95 | 96 | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Foundation") 97 | Foundation(obj) 98 | ViewProviderFoundation(obj.ViewObject) 99 | obj.plan = base 100 | obj.height = height 101 | obj.cover = cover 102 | obj.fc = f"{fc} MPa" 103 | 104 | FreeCAD.ActiveDocument.recompute() 105 | 106 | return obj 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /osafe_objects/opening.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import Part 3 | import FreeCAD 4 | import Sketcher 5 | import ArchComponent 6 | 7 | try: 8 | from osafe_funcs import osafe_funcs 9 | except: 10 | import osafe_funcs 11 | 12 | 13 | def make_opening(points, height=4000, doc=None): 14 | z = points[0].z 15 | if doc is None: 16 | doc = FreeCAD.ActiveDocument 17 | obj = doc.addObject("Part::FeaturePython", "Opening") 18 | Opening(obj) 19 | sketch = doc.addObject('Sketcher::SketchObject', 'sketch') 20 | sketch.Placement.Base.z = z 21 | points_xy = osafe_funcs.sort_vertex([[p.x, p.y] for p in points]) 22 | points_vec = [FreeCAD.Vector(p[0], p[1], 0) for p in points_xy] 23 | points = points_vec + [points_vec[0]] 24 | sketch_lines = [] 25 | for p1, p2 in zip(points[:-1], points[1:]): 26 | sketch_lines.append(sketch.addGeometry(Part.LineSegment(p1, p2))) 27 | for l1, l2 in zip(sketch_lines[:-1], sketch_lines[1:]): 28 | sketch.addConstraint(Sketcher.Constraint('Coincident', l1, 2, l2, 1)) 29 | sketch.addConstraint(Sketcher.Constraint('Coincident', sketch_lines[-1], 2, sketch_lines[0], 1)) 30 | obj.Base = sketch 31 | # sketch.ViewObject.Visibility = False 32 | obj.height = height 33 | 34 | if FreeCAD.GuiUp: 35 | _ViewProviderOpening(obj.ViewObject) 36 | obj.ViewObject.Visibility = False 37 | FreeCAD.ActiveDocument.recompute() 38 | return obj 39 | 40 | 41 | class Opening(ArchComponent.Component): 42 | def __init__(self, obj): 43 | super().__init__(obj) 44 | obj.IfcType = "Opening Element" 45 | self.set_properties(obj) 46 | 47 | def set_properties(self, obj): 48 | obj.Proxy = self 49 | if not hasattr(obj, "plan"): 50 | obj.addProperty( 51 | "Part::PropertyPartShape", 52 | "plan", 53 | "opening", 54 | ) 55 | # if not hasattr(obj, "solid"): 56 | # obj.addProperty( 57 | # "Part::PropertyPartShape", 58 | # "solid", 59 | # "opening", 60 | # ) 61 | if not hasattr(obj, "height"): 62 | obj.addProperty( 63 | "App::PropertyLength", 64 | "height", 65 | "Base", 66 | ) 67 | 68 | def onDocumentRestored(self, obj): 69 | super().onDocumentRestored(obj) 70 | self.set_properties(obj) 71 | 72 | def execute(self, obj): 73 | if hasattr(obj, "Base") and obj.Base: 74 | wire = obj.Base.Shape.Wires[0] 75 | obj.plan = Part.Face(wire) 76 | # points = osafe_funcs.get_sort_points( 77 | # wire.Edges, 78 | # sort_edges=True, 79 | # ) 80 | # lines = [] 81 | # for p1, p2 in zip(points[:-2], points[2:]): 82 | # lines.append(Part.makeLine(p1, p2)) 83 | 84 | shape = obj.plan.extrude(FreeCAD.Vector(0, 0, -obj.height.Value)) 85 | # obj.Shape = Part.makeCompound([shape] + lines) 86 | obj.Shape = shape 87 | 88 | 89 | class _ViewProviderOpening: 90 | def __init__(self, vobj): 91 | vobj.Proxy = self 92 | vobj.Transparency = 70 93 | vobj.ShapeColor = (0.00,1.00,1.00) 94 | vobj.DisplayMode = "Shaded" 95 | 96 | def attach(self, vobj): 97 | self.ViewObject = vobj 98 | self.Object = vobj.Object 99 | 100 | def claimChildren(self): 101 | children = [self.Object.Base] 102 | return children 103 | 104 | def onDelete(self, vobj, subelements): 105 | name = None 106 | if vobj.Object.Base: 107 | name = vobj.Object.Base.Name 108 | FreeCAD.ActiveDocument.removeObject(vobj.Object.Name) 109 | if name is not None: 110 | FreeCAD.ActiveDocument.removeObject(name) 111 | 112 | def getIcon(self): 113 | return str(Path(__file__).parent.parent / "osafe_images" / "opening.svg") 114 | 115 | def __getstate__(self): 116 | return None 117 | 118 | def __setstate__(self, state): 119 | return None 120 | 121 | 122 | if __name__ == "__main__": 123 | x1 = 10 124 | x2 = 2500 125 | y1 = 7 126 | y2 = 1700 127 | p1=FreeCAD.Vector(x1, y1, 0) 128 | p2=FreeCAD.Vector(x2, y1, 0) 129 | p3=FreeCAD.Vector(x2, y2, 0) 130 | p4=FreeCAD.Vector(x1, y2, 0) 131 | points = [p1, p2, p3, p4] 132 | make_opening( 133 | points=points, 134 | # height = 3, 135 | ) 136 | -------------------------------------------------------------------------------- /osafe_objects/slab.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import Part 3 | import FreeCAD 4 | import Draft 5 | # import Sketcher 6 | import ArchComponent 7 | 8 | from osafe_funcs import osafe_funcs 9 | 10 | 11 | def make_slab( 12 | base, 13 | height=1000, 14 | ks = None, 15 | fc = None, 16 | ): 17 | doc = FreeCAD.ActiveDocument 18 | obj = doc.addObject("Part::FeaturePython", "Slab") 19 | Slab(obj) 20 | if isinstance(base, Part.Shape): 21 | if len(base.Wires) == 1: 22 | points = osafe_funcs.get_sort_points(base.Edges) 23 | base = Draft.make_wire(points, closed=True) 24 | else: 25 | base_obj = doc.addObject("Part::FeaturePython", "Base") 26 | base_obj.Shape = base 27 | base = base_obj 28 | obj.Base = base 29 | obj.height = height 30 | if ks is not None: 31 | obj.ks = ks 32 | if fc is not None: 33 | obj.fc = fc 34 | if FreeCAD.GuiUp: 35 | ViewProviderSlab(obj.ViewObject) 36 | FreeCAD.ActiveDocument.recompute() 37 | return obj 38 | 39 | 40 | class Slab(ArchComponent.Component): 41 | def __init__(self, obj): 42 | super().__init__(obj) 43 | obj.IfcType = "Footing" 44 | self.set_properties(obj) 45 | 46 | def set_properties(self, obj): 47 | obj.Proxy = self 48 | self.Type = "Slab" 49 | if not hasattr(obj, "height"): 50 | obj.addProperty( 51 | "App::PropertyLength", 52 | "height", 53 | "Base", 54 | ) 55 | if not hasattr(obj, "ks"): 56 | obj.addProperty( 57 | "App::PropertyFloat", 58 | "ks", 59 | "Soil", 60 | ) 61 | if not hasattr(obj, "fc"): 62 | obj.addProperty( 63 | "App::PropertyPressure", 64 | "fc", 65 | "Concrete", 66 | ) 67 | 68 | 69 | def onDocumentRestored(self, obj): 70 | super().onDocumentRestored(obj) 71 | self.set_properties(obj) 72 | 73 | def execute(self, obj): 74 | if hasattr(obj, "Base") and obj.Base: 75 | obj.Shape = obj.Base.Shape.extrude(FreeCAD.Vector(0, 0, -obj.height.Value)) 76 | 77 | 78 | class ViewProviderSlab: 79 | def __init__(self, vobj): 80 | vobj.Proxy = self 81 | vobj.DisplayMode = "Shaded" 82 | 83 | def attach(self, vobj): 84 | self.ViewObject = vobj 85 | self.Object = vobj.Object 86 | 87 | def claimChildren(self): 88 | children = [self.Object.Base] 89 | return children 90 | 91 | def onDelete(self, vobj, subelements): 92 | name = None 93 | if vobj.Object.Base: 94 | name = vobj.Object.Base.Name 95 | FreeCAD.ActiveDocument.removeObject(vobj.Object.Name) 96 | if name is not None: 97 | FreeCAD.ActiveDocument.removeObject(name) 98 | 99 | def getIcon(self): 100 | return str(Path(__file__).parent.parent / "osafe_images" / "slab.svg") 101 | 102 | def __getstate__(self): 103 | return None 104 | 105 | def __setstate__(self, state): 106 | return None 107 | 108 | 109 | if __name__ == "__main__": 110 | import FreeCADGui as Gui 111 | sel = Gui.Selection.getSelection() 112 | if sel: 113 | wire = sel[0] 114 | else: 115 | x1 = 0 116 | x2 = 2500 117 | y1 = 0 118 | y2 = 1700 119 | p1=FreeCAD.Vector(x1, y1, 0) 120 | p2=FreeCAD.Vector(x2, y1, 0) 121 | p3=FreeCAD.Vector(x2, y2, 0) 122 | p4=FreeCAD.Vector(x1, y2, 0) 123 | points = [p1, p2, p3, p4, p1] 124 | import Draft 125 | wire = Draft.make_wire(points) 126 | FreeCAD.ActiveDocument.recompute() 127 | make_slab(base=wire, 128 | ) 129 | make_slab(base=wire.Shape) 130 | -------------------------------------------------------------------------------- /osafe_py_widgets/base_foundation_panel.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import FreeCAD 4 | import FreeCADGui as Gui 5 | from draftutils.translate import translate 6 | 7 | from PySide2.QtWidgets import QMessageBox 8 | 9 | from osafe_funcs import osafe_funcs 10 | from osafe_py_widgets import resource_rc 11 | 12 | punch_path = Path(__file__).parent.parent 13 | 14 | 15 | class Form: 16 | 17 | def __init__(self): 18 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'base_foundation_panel.ui')) 19 | self.create_connections() 20 | 21 | 22 | def getStandardButtons(self): 23 | return 0 24 | 25 | def create_connections(self): 26 | self.form.create_pushbutton.clicked.connect(self.create) 27 | self.form.cancel_pushbutton.clicked.connect(self.accept) 28 | 29 | def create(self): 30 | north_dist = self.form.north_distance.value() * 10 if self.form.north_checkbox.isChecked() else None 31 | south_dist = self.form.south_distance.value() * 10 if self.form.south_checkbox.isChecked() else None 32 | east_dist = self.form.east_distance.value() * 10 if self.form.east_checkbox.isChecked() else None 33 | west_dist = self.form.west_distance.value() * 10 if self.form.west_checkbox.isChecked() else None 34 | x_stirp_name = self.form.x_strip_name.currentText() 35 | y_stirp_name = self.form.y_strip_name.currentText() 36 | width = self.form.width_spinbox.value() * 10 37 | height = self.form.height_spinbox.value() * 10 38 | soil_modulus = self.form.soil_modulus.value() 39 | angle = self.form.angle_spinbox.value() 40 | selection = self.form.selection_checkbox.isChecked() 41 | doc = FreeCAD.ActiveDocument 42 | beams = [] 43 | sel = [] 44 | if selection: 45 | sel = Gui.Selection.getSelection() 46 | for o in sel: 47 | if ( 48 | hasattr(o, "type") and 49 | o.type == 'Beam' 50 | ): 51 | beams.append(o) 52 | else: 53 | for o in doc.Objects: 54 | if ( 55 | hasattr(o, "type") and 56 | o.type == 'Beam' 57 | ): 58 | beams.append(o) 59 | 60 | if len(beams) == 0: 61 | if len(sel) > 0: 62 | message = "There is No Beams in selected objects." 63 | else: 64 | message = "There is No Beams in Model." 65 | QMessageBox.warning(None, "Beams", message) 66 | return 67 | if len(sel) == 0: # Check for beams that now are in base foundations 68 | used_beam = osafe_funcs.get_beams_in_doc_that_belogns_to_base_foundations(doc) 69 | current_beam = [beam.Name for beam in beams] 70 | new_beams = set(current_beam).difference(used_beam) 71 | if len(new_beams) == 0: 72 | message = "There is No Remained Beams in Model." 73 | QMessageBox.warning(None, "Beams", message) 74 | return 75 | beams = [doc.getObjectsByLabel(name)[0] for name in new_beams] 76 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Create Base Foundations")) 77 | osafe_funcs.make_automatic_base_foundation(beams, width, north_dist, south_dist, 78 | east_dist, west_dist, x_stirp_name, y_stirp_name, angle, height, soil_modulus) 79 | FreeCAD.ActiveDocument.commitTransaction() 80 | Gui.Selection.clearSelection() 81 | Gui.Control.closeDialog() 82 | 83 | def accept(self): 84 | Gui.Control.closeDialog() 85 | -------------------------------------------------------------------------------- /osafe_py_widgets/change_branch.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2.QtWidgets import QMessageBox 4 | import FreeCADGui as Gui 5 | import git 6 | 7 | civil_path = Path(__file__).parent 8 | 9 | 10 | class Form: 11 | 12 | def __init__(self, 13 | git_url : str = "https://github.com/ebrahimraeyat/Civil.git"): 14 | self.git_url = git_url 15 | self.form = Gui.PySideUic.loadUi(str(civil_path / 'Resources' / 'ui' / 'change_branch.ui')) 16 | # self.fill_branches() 17 | 18 | # def fill_branches(self): 19 | # repo = git.Repo(civil_path) 20 | # branch_names = [branch.name for branch in repo.branches] 21 | # if branch_names: 22 | # self.form.branch_list.addItems(branch_names) 23 | 24 | 25 | def accept(self): 26 | g = git.cmd.Git(civil_path) 27 | branch = self.form.branch_list.currentItem().text() 28 | if not branch: 29 | return 30 | succeed = self.checkout(g, branch) 31 | if not succeed: 32 | if not internet(): 33 | msg = "You are not connected to the Internet, please check your internet connection." 34 | QMessageBox.warning(None, 'update', str(msg)) 35 | return 36 | QMessageBox.information( 37 | None, 38 | "Download", 39 | "Download takes some minutes, please be patient.", 40 | ) 41 | g.execute(f'git clone --branch {branch} --depth 1 {self.git_url}') 42 | self.checkout(g, branch) 43 | Gui.Control.closeDialog() 44 | 45 | def checkout(self, g, branch): 46 | try: 47 | g.execute(f'git checkout {branch}') 48 | msg = f'You have successfully moved to {branch}' 49 | QMessageBox.information(None, 'Change Branch', str(msg)) 50 | return True 51 | except: 52 | return False 53 | 54 | 55 | def internet(host="8.8.8.8", port=53, timeout=3): 56 | import socket 57 | try: 58 | socket.setdefaulttimeout(timeout) 59 | socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) 60 | return True 61 | except Exception as ex: 62 | # print(ex.message) 63 | return False -------------------------------------------------------------------------------- /osafe_py_widgets/draw_automatic_rebars.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2.QtWidgets import QMessageBox 4 | 5 | import FreeCAD 6 | import FreeCADGui as Gui 7 | from draftutils.translate import translate 8 | 9 | 10 | from osafe_py_widgets import resource_rc 11 | 12 | punch_path = Path(__file__).parent.parent 13 | 14 | 15 | class Form: 16 | 17 | def __init__(self): 18 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'draw_automatic_rebars.ui')) 19 | self.create_connections() 20 | # if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OSAFE").GetBool("osafe_split_strips", False): 21 | # self.form.split.setChecked(True) 22 | # self.split_clicked(True) 23 | # tol = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OSAFE").GetFloat("osafe_split_strips_tolerance", .001) 24 | # self.form.tolerance.setValue(tol) 25 | 26 | def create_connections(self): 27 | self.form.create_pushbutton.clicked.connect(self.create) 28 | self.form.cancel_pushbutton.clicked.connect(self.accept) 29 | self.form.help.clicked.connect(self.show_help) 30 | 31 | def create(self): 32 | doc = FreeCAD.ActiveDocument 33 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Create Automatic Rebars")) 34 | rebars = [] 35 | for obj in doc.Objects: 36 | if ( 37 | isinstance(obj, FreeCAD.DocumentObjectGroup) and 38 | 'rebars' in obj.Label.lower() 39 | ): 40 | rebars.append(obj) 41 | if rebars and QMessageBox.question( 42 | None, 43 | 'Remove Rebars', 44 | 'There is rebars exists in Model, Do you want to remove those?', 45 | ) == QMessageBox.Yes: 46 | for rebar in rebars: 47 | for o in rebar.Group: 48 | FreeCAD.ActiveDocument.removeObject(o.Name) 49 | FreeCAD.ActiveDocument.removeObject(rebar.Name) 50 | 51 | from osafe_objects import osafe_rebar 52 | top_rebar_diameter = int(self.form.top_rebar_diameter_combobox.currentText()) 53 | bot_rebar_diameter = int(self.form.bot_rebar_diameter_combobox.currentText()) 54 | stirrup_diameter = int(self.form.stirrup_rebar_diameter_combobox.currentText()) 55 | min_ratio_of_rebars = .0018 if self.form.impose_minimum_checkbox.isChecked() else 0 56 | osafe_rebar.make_rebars( 57 | top_rebar_diameter=top_rebar_diameter, 58 | bot_rebar_diameter=bot_rebar_diameter, 59 | stirrup_diameter=stirrup_diameter, 60 | min_ratio_of_rebars=min_ratio_of_rebars 61 | ) 62 | FreeCAD.ActiveDocument.recompute() 63 | FreeCAD.ActiveDocument.commitTransaction() 64 | self.accept() 65 | 66 | def show_help(self): 67 | from freecad_funcs import show_help 68 | show_help('make_auto_rebars.html', 'OSAFE') 69 | 70 | def accept(self): 71 | Gui.Control.closeDialog() 72 | 73 | def getStandardButtons(self): 74 | return 0 75 | -------------------------------------------------------------------------------- /osafe_py_widgets/draw_automatic_strip.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2.QtWidgets import QMessageBox 4 | 5 | import FreeCAD 6 | import FreeCADGui as Gui 7 | from draftutils.translate import translate 8 | 9 | 10 | from osafe_py_widgets import resource_rc 11 | 12 | punch_path = Path(__file__).parent.parent 13 | 14 | 15 | class Form: 16 | 17 | def __init__(self): 18 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'draw_automatic_strip.ui')) 19 | self.create_connections() 20 | if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OSAFE").GetBool("osafe_split_strips", False): 21 | self.form.split.setChecked(True) 22 | self.split_clicked(True) 23 | tol = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OSAFE").GetFloat("osafe_split_strips_tolerance", .001) 24 | self.form.tolerance.setValue(tol) 25 | self.set_foundation_type() 26 | 27 | def set_foundation_type(self): 28 | doc = FreeCAD.ActiveDocument 29 | if doc is None: 30 | return 31 | if hasattr(doc, "Foundation"): 32 | if doc.Foundation.foundation_type == "Mat": 33 | self.uncheck_strip() 34 | 35 | def create_connections(self): 36 | self.form.create_pushbutton.clicked.connect(self.create) 37 | self.form.cancel_pushbutton.clicked.connect(self.accept) 38 | self.form.mat_foundation.clicked.connect(self.uncheck_strip) 39 | self.form.strip_foundation.clicked.connect(self.uncheck_mat) 40 | self.form.help.clicked.connect(self.show_help) 41 | self.form.split.clicked.connect(self.split_clicked) 42 | 43 | def split_clicked(self, checked): 44 | self.form.tol_label.setEnabled(checked) 45 | self.form.tolerance.setEnabled(checked) 46 | 47 | def uncheck_strip(self): 48 | self.form.mat_foundation.setChecked(True) 49 | self.form.strip_foundation.setChecked(False) 50 | 51 | def uncheck_mat(self): 52 | self.form.mat_foundation.setChecked(False) 53 | self.form.strip_foundation.setChecked(True) 54 | 55 | def create(self): 56 | doc = FreeCAD.ActiveDocument 57 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Create Automatic Strips")) 58 | strips = [] 59 | for obj in doc.Objects: 60 | if ( 61 | isinstance(obj, FreeCAD.DocumentObjectGroup) and 62 | 'strips' in obj.Label 63 | ): 64 | strips.append(obj) 65 | if strips and QMessageBox.question( 66 | None, 67 | 'Remove Strips', 68 | 'There is ' + ' and '.join([s.Label for s in strips]) + ' exists in Model, Do you want to remove those?', 69 | ) == QMessageBox.Yes: 70 | for strip in strips: 71 | for o in strip.Group: 72 | FreeCAD.ActiveDocument.removeObject(o.Base.Name) 73 | FreeCAD.ActiveDocument.removeObject(o.Name) 74 | FreeCAD.ActiveDocument.removeObject(strip.Name) 75 | 76 | from osafe_funcs import osafe_funcs 77 | if self.form.mat_foundation.isChecked(): 78 | draw_x = self.form.x_strips.isChecked() 79 | draw_y = self.form.y_strips.isChecked() 80 | x_width = self.form.x_width.value() 81 | y_width = self.form.y_width.value() 82 | x_layer_name = self.form.x_layer_name.currentText() 83 | y_layer_name = self.form.y_layer_name.currentText() 84 | equal = self.form.equal.isChecked() 85 | consider_openings = self.form.consider_openings.isChecked() 86 | osafe_funcs.draw_strip_automatically_in_mat_foundation( 87 | # foundation=self.foundation, 88 | x_width=x_width * 10, 89 | y_width=y_width * 10, 90 | x_layer_name=x_layer_name, 91 | y_layer_name=y_layer_name, 92 | draw_x=draw_x, 93 | draw_y=draw_y, 94 | equal=equal, 95 | consider_openings=consider_openings, 96 | ) 97 | else: 98 | split = self.form.split.isChecked() 99 | tol = self.form.tolerance.value() 100 | osafe_funcs.draw_strip_automatically_in_strip_foundation(split=split, tolerance=tol) 101 | FreeCAD.ActiveDocument.commitTransaction() 102 | self.accept() 103 | Gui.Selection.clearSelection() 104 | 105 | def show_help(self): 106 | from freecad_funcs import show_help 107 | show_help('make_auto_strip.html', 'OSAFE') 108 | 109 | def accept(self): 110 | Gui.Control.closeDialog() 111 | 112 | def getStandardButtons(self): 113 | return 0 114 | -------------------------------------------------------------------------------- /osafe_py_widgets/explode_foundation.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from PySide2 import QtCore 3 | from PySide2.QtWidgets import QMessageBox 4 | import FreeCAD 5 | 6 | import FreeCADGui as Gui 7 | 8 | from draftutils.translate import translate 9 | from osafe_funcs import osafe_funcs 10 | 11 | from osafe_objects.rectangular_slab import make_rectangular_slab_from_base_foundation 12 | from osafe_objects.slab import make_slab 13 | 14 | 15 | class ExplodeFoundation(): 16 | """Gui command for the Explode Foundation to rectangular slab objects.""" 17 | 18 | 19 | def GetResources(self): 20 | menu_text = QtCore.QT_TRANSLATE_NOOP( 21 | "civil_explode_foundation", 22 | "Explode Foundation") 23 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 24 | "civil_explode_foundation", 25 | "Explode Foundation") 26 | path = str( 27 | Path(__file__).parent.parent / "osafe_images" / "explode_foundation.svg" 28 | ) 29 | return {'Pixmap': path, 30 | 'MenuText': menu_text, 31 | 'ToolTip': tool_tip} 32 | 33 | def Activated(self): 34 | """ 35 | Execute when the command is called. 36 | """ 37 | 38 | # The command to run is built as a series of text strings 39 | # to be committed through the `draftutils.todo.ToDo` class. 40 | sel = Gui.Selection.getSelection() 41 | if not sel: 42 | show_warning() 43 | return 44 | foun = None 45 | for o in sel: 46 | if hasattr(o, "IfcType") and o.IfcType == 'Footing': 47 | foun = o 48 | break 49 | if foun is None: 50 | show_warning() 51 | return 52 | if not foun.base_foundations: 53 | return 54 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Explode Foundation")) 55 | if foun.continuous_layer != 'AB': 56 | foun.continuous_layer = 'AB' 57 | FreeCAD.ActiveDocument.recompute() 58 | slabs = [] 59 | if foun.foundation_type == 'Strip': 60 | for bf in foun.base_foundations: 61 | slab = make_rectangular_slab_from_base_foundation(bf, plan='Auto') 62 | slabs.append(slab) 63 | elif foun.foundation_type == 'Mat': 64 | height = foun.height 65 | if foun.split: 66 | solids = foun.Shape.Solids 67 | assert len(solids) == 5 68 | p0 = osafe_funcs.get_top_faces(solids[0], fuse=True) 69 | p1 = osafe_funcs.get_top_faces(solids[1], fuse=True) 70 | p2 = osafe_funcs.get_top_faces(solids[2], fuse=True) 71 | p3 = osafe_funcs.get_top_faces(solids[3], fuse=True) 72 | p4 = osafe_funcs.get_top_faces(solids[4], fuse=True) 73 | s0 = make_slab(p0, height=height,fc=foun.fc, ks= 2 * foun.ks) 74 | s1 = make_slab(p1, height=height,fc=foun.fc, ks= 2 * foun.ks) 75 | s2 = make_slab(p2, height=height,fc=foun.fc, ks= 1.5 * foun.ks) 76 | s3 = make_slab(p3, height=height,fc=foun.fc, ks= 1.5 * foun.ks) 77 | s4 = make_slab(p4, height=height,fc=foun.fc, ks= foun.ks) 78 | slabs.extend([s0, s1, s2, s3, s4]) 79 | else: 80 | s = make_slab(foun.plan, height=height, fc=foun.fc, ks=foun.ks) 81 | slabs.append(s) 82 | foun_slabs = [] 83 | for slab in slabs: 84 | if slab not in foun.Slabs: 85 | foun_slabs.append(slab) 86 | foun_slabs.extend(foun.Slabs) 87 | foun.base_foundations = [] 88 | foun.Slabs = foun_slabs 89 | for slab in foun.Slabs: 90 | slab.ViewObject.hide() 91 | FreeCAD.ActiveDocument.recompute() 92 | FreeCAD.ActiveDocument.commitTransaction() 93 | 94 | def IsActive(self): 95 | return not FreeCAD.ActiveDocument is None 96 | 97 | def remove_obj(name: str) -> None: 98 | o = FreeCAD.ActiveDocument.getObject(name) 99 | if hasattr(o, "Base") and o.Base: 100 | remove_obj(o.Base.Name) 101 | FreeCAD.ActiveDocument.removeObject(name) 102 | 103 | def show_warning(): 104 | QMessageBox.warning(None, 'Selection', 'Please select the foundation!') 105 | 106 | Gui.addCommand('osafe_explode_foundation', ExplodeFoundation()) 107 | 108 | ## @} 109 | -------------------------------------------------------------------------------- /osafe_py_widgets/explode_seismic_load_patterns.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import FreeCADGui as Gui 4 | 5 | import etabs_obj 6 | from osafe_py_widgets import resource_rc 7 | from PySide2.QtGui import QPixmap 8 | 9 | punch_path = Path(__file__).parent.parent 10 | 11 | class Form: 12 | def __init__(self, parent=None): 13 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'explode_seismic_load_patterns.ui')) 14 | self.etabs = etabs_obj.EtabsModel(backup=False) 15 | self.form.start_button.clicked.connect(self.accept) 16 | # self.form.close_button.clicked.connect(self.reject) 17 | 18 | def accept(self): 19 | ex = self.form.ex.text() 20 | epx = self.form.epx.text() 21 | enx = self.form.enx.text() 22 | ey = self.form.ey.text() 23 | epy = self.form.epy.text() 24 | eny = self.form.eny.text() 25 | equal_names = { 26 | 'XDir' : ex, 27 | 'XDirPlusE' : epx, 28 | 'XDirMinusE' : enx, 29 | 'YDir' : ey, 30 | 'YDirPlusE' : epy, 31 | 'YDirMinusE' : eny, 32 | } 33 | replace_ex = self.form.replace_ex.isChecked() 34 | replace_ey = self.form.replace_ey.isChecked() 35 | drift_prefix = self.form.drift_prefix.text() 36 | drift_suffix = self.form.drift_suffix.text() 37 | pixmap = QPixmap(str(punch_path / 'Resources' / 'icons' / 'tick.svg')) 38 | for ret in self.etabs.database.expand_loads( 39 | equal_names=equal_names, 40 | replace_ex = replace_ex, 41 | replace_ey = replace_ey, 42 | drift_prefix = drift_prefix, 43 | drift_suffix = drift_suffix, 44 | ): 45 | if type(ret) == tuple and len(ret) == 2: 46 | message, number = ret 47 | if type(message) == str and type(number) == int: 48 | self.form.result_label.setText(message) 49 | self.form.progressbar.setValue(number) 50 | if message.startswith('Get'): 51 | if 'case' in message: 52 | self.form.get_loadpat.setPixmap(pixmap) 53 | elif 'load combinations' in message: 54 | if 'Design' in message: 55 | self.form.get_loadcomb.setPixmap(pixmap) 56 | else: 57 | self.form.get_loadcase.setPixmap(pixmap) 58 | elif message.startswith('Apply'): 59 | if 'pattern' in message: 60 | self.form.get_design_loadcomb.setPixmap(pixmap) 61 | elif 'case' in message: 62 | self.form.set_loadpat.setPixmap(pixmap) 63 | elif 'load combinations' in message: 64 | if 'Design' in message: 65 | self.form.set_loadcomb.setPixmap(pixmap) 66 | else: 67 | self.form.set_loadcase.setPixmap(pixmap) 68 | elif 'Finished' in message: 69 | self.form.set_design_loadcomb.setPixmap(pixmap) 70 | elif type(ret) == bool: 71 | if not ret: 72 | self.form.result_label.setText("Error Occurred, process did not finished.") 73 | self.form.start_button.setEnabled(False) 74 | elif type(ret) == str: 75 | self.form.result_label.setText(ret) 76 | 77 | -------------------------------------------------------------------------------- /osafe_py_widgets/export/export_to_dxf_dialog.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2.QtWidgets import QMessageBox 4 | 5 | import FreeCAD 6 | import FreeCADGui as Gui 7 | 8 | from osafe_py_widgets import resource_rc 9 | 10 | punch_path = Path(__file__).parent.parent.parent 11 | 12 | 13 | class Form: 14 | 15 | def __init__(self): 16 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'export' / 'export_to_dxf.ui')) 17 | self.fill_filename() 18 | self.create_connections() 19 | 20 | def fill_filename(self): 21 | doc = FreeCAD.ActiveDocument 22 | if doc.FileName: 23 | name = Path(doc.FileName).with_suffix('.dxf') 24 | else: 25 | try: 26 | import etabs_obj 27 | etabs = etabs_obj.EtabsModel(backup=False) 28 | name = etabs.get_filename().with_suffix('.dxf') 29 | except: 30 | name = '' 31 | self.form.filename.setText(str(name)) 32 | 33 | def create_connections(self): 34 | self.form.browse.clicked.connect(self.browse) 35 | self.form.export_button.clicked.connect(self.export) 36 | self.form.cancel_pushbutton.clicked.connect(self.accept) 37 | 38 | def browse(self): 39 | ext = '.dxf' 40 | from PySide2.QtWidgets import QFileDialog 41 | filters = f"{ext[1:]} (*{ext})" 42 | filename, _ = QFileDialog.getSaveFileName(None, 'select file', 43 | None, filters) 44 | if not filename: 45 | return 46 | if not filename.lower().endswith(ext): 47 | filename += ext 48 | self.form.filename.setText(filename) 49 | 50 | def export(self): 51 | filename = self.form.filename.text() 52 | if not filename: 53 | return 54 | from osafe_import_export import export 55 | ret = export.to_dxf( 56 | filename, 57 | columns=self.form.columns_checkbox.isChecked(), 58 | punches=self.form.punches_checkbox.isChecked(), 59 | ) 60 | if ret: 61 | QMessageBox.information(None, 'Successful', f'Model has been exported to {filename}') 62 | 63 | def getStandardButtons(self): 64 | return 0 65 | 66 | def accept(self): 67 | Gui.Control.closeDialog() 68 | -------------------------------------------------------------------------------- /osafe_py_widgets/export/import_from_dxf_dialog.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | 4 | import FreeCAD 5 | import FreeCADGui as Gui 6 | 7 | from osafe_py_widgets import resource_rc 8 | 9 | punch_path = Path(__file__).parent.parent.parent 10 | 11 | 12 | class Form: 13 | 14 | def __init__(self): 15 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'import_from_dxf.ui')) 16 | # self.fill_filename() 17 | self.create_connections() 18 | 19 | def fill_filename(self): 20 | doc = FreeCAD.ActiveDocument 21 | name = Path(doc.FileName).with_suffix('.dxf') 22 | self.form.filename.setText(str(name)) 23 | 24 | def create_connections(self): 25 | self.form.browse.clicked.connect(self.browse) 26 | self.form.import_button.clicked.connect(self.import_dxf) 27 | self.form.cancel_pushbutton.clicked.connect(self.accept) 28 | 29 | def browse(self): 30 | ext = '.dxf' 31 | from PySide2.QtWidgets import QFileDialog 32 | filters = f"{ext[1:]} (*{ext})" 33 | filename, _ = QFileDialog.getOpenFileName(None, 'select file', 34 | None, filters) 35 | if not filename: 36 | return 37 | if not filename.lower().endswith(ext): 38 | filename += ext 39 | self.form.filename.setText(filename) 40 | 41 | def import_dxf(self): 42 | filename = self.form.filename.text() 43 | if not filename: 44 | return 45 | scale = self.form.scale_box.value() 46 | p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") 47 | p.SetFloat("dxfScaling", scale) 48 | p.SetBool("dxfUseLegacyImporter", True) 49 | p.GetBool("dxfGetOriginalColors", True) 50 | 51 | import importDXF 52 | importDXF.readPreferences() 53 | doc = FreeCAD.ActiveDocument 54 | importDXF.getDXFlibs() 55 | gui = FreeCAD.GuiUp 56 | if importDXF.dxfReader: 57 | # groupname = str(Path(filename).name) 58 | # importgroup = doc.addObject("App::DocumentObjectGroup", groupname) 59 | # importgroup.Label = groupname 60 | importDXF.processdxf(doc, filename) 61 | # for l in layers: 62 | # importgroup.addObject(l) 63 | else: 64 | importDXF.errorDXFLib(gui) 65 | self.accept() 66 | if gui: 67 | Gui.SendMsgToActiveView("ViewFit") 68 | 69 | def getStandardButtons(self): 70 | return 0 71 | 72 | def accept(self): 73 | Gui.Control.closeDialog() 74 | -------------------------------------------------------------------------------- /osafe_py_widgets/force_panel.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2.QtWidgets import QMessageBox 4 | import FreeCAD 5 | import FreeCADGui as Gui 6 | from draftutils.translate import translate 7 | 8 | from osafe_py_widgets import resource_rc 9 | 10 | punch_path = Path(__file__).parent.parent 11 | 12 | 13 | class ForceTaskPanel: 14 | 15 | def __init__(self): 16 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'force_panel.ui')) 17 | self.foun = FreeCAD.ActiveDocument.Foundation 18 | self.fill_load_cases() 19 | self.create_connections() 20 | self.all_loads = [] 21 | self.hide_loads() 22 | 23 | def create_connections(self): 24 | self.form.assign_button.clicked.connect(self.assign_load) 25 | self.form.loadcase.currentIndexChanged.connect(self.hide_loads) 26 | 27 | def fill_load_cases(self): 28 | deads = [] 29 | try: 30 | import find_etabs 31 | etabs, _ = find_etabs.find_etabs(run=False, backup=False) 32 | deads = etabs.load_patterns.get_special_load_pattern_names(1) 33 | except: 34 | if hasattr(FreeCAD, 'load_cases'): 35 | deads = FreeCAD.load_cases 36 | self.form.loadcase.addItems(deads) 37 | 38 | def hide_loads(self): 39 | try: 40 | loads = FreeCAD.ActiveDocument.findObjects(Type='Fem::ConstraintForce') 41 | for load in loads: 42 | load.ViewObject.hide() 43 | self.all_loads = [load.Name for load in loads] 44 | except TypeError: 45 | pass 46 | 47 | def assign_load(self): 48 | loadcase = self.form.loadcase.currentText() 49 | if not loadcase: 50 | QMessageBox.warning(None, 51 | 'Dead load case', 52 | 'Please Select Dead loadcase first.', 53 | ) 54 | return 55 | load_value = self.form.load_value.value() 56 | version = float('.'.join(FreeCAD.Version()[0:2])) 57 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Apply load on Foundation")) 58 | if version <= 0.19: 59 | Gui.activateWorkbench("FemWorkbench") 60 | Gui.activateWorkbench("OSAFEWorkbench") 61 | loads = FreeCAD.ActiveDocument.findObjects(Type='Fem::ConstraintForce') 62 | all_loads = [load.Name for load in loads] 63 | if loadcase in all_loads: 64 | load = FreeCAD.ActiveDocument.getObject(loadcase) 65 | load.Force = load_value 66 | load.ViewObject.show() 67 | else: 68 | constraint = FreeCAD.ActiveDocument.addObject('Fem::ConstraintForce', loadcase) 69 | constraint.addProperty('App::PropertyString', 'loadcase', 'Base') 70 | constraint.loadcase = loadcase 71 | references_names = [] 72 | top_of_foundation = self.foun.Shape.BoundBox.ZMax 73 | for i, face in enumerate(self.foun.Shape.Faces, start=1): 74 | if face.BoundBox.ZMin == top_of_foundation: 75 | references_names.append(f'Face{i}') 76 | constraint.References = [(self.foun, tuple(references_names))] 77 | constraint.Force = load_value 78 | constraint.Reversed = True 79 | FreeCAD.ActiveDocument.recompute() 80 | FreeCAD.ActiveDocument.commitTransaction() 81 | 82 | if __name__ == '__main__': 83 | panel = ForceTaskPanel() 84 | -------------------------------------------------------------------------------- /osafe_py_widgets/gui_automatic_strip.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | 4 | from PySide2 import QtCore 5 | from PySide2.QtCore import QT_TRANSLATE_NOOP 6 | 7 | import FreeCAD 8 | import FreeCADGui as Gui 9 | 10 | 11 | class OsafeAutomaticStrip: 12 | """Gui command for the creating stirp automatically in mat foundations.""" 13 | 14 | def GetResources(self): 15 | menu_text = QtCore.QT_TRANSLATE_NOOP( 16 | "osafe", 17 | "Auto Strip") 18 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 19 | "osafe", 20 | "Create Strips automatically in mat foundations") 21 | path = str( 22 | Path(__file__).parent.parent / "osafe_images" / "auto_strip.svg" 23 | ) 24 | return {'Pixmap': path, 25 | 'MenuText': menu_text, 26 | 'ToolTip': tool_tip} 27 | 28 | def Activated(self): 29 | # def is_foundation_type(obj): 30 | # if hasattr(obj, 'IfcType') and obj.IfcType == 'Footing': 31 | # return True 32 | # return False 33 | 34 | # doc = FreeCAD.ActiveDocument 35 | # sel = Gui.Selection.getSelection() 36 | # foun = None 37 | # if sel and is_foundation_type(sel[0]): 38 | # foun = sel[0] 39 | # if foun is None: 40 | # for o in doc.Objects: 41 | # if is_foundation_type(o): 42 | # foun = o 43 | # break 44 | # if foun is None: 45 | # return 46 | # print('ali') 47 | from osafe_py_widgets import draw_automatic_strip 48 | win = draw_automatic_strip.Form() 49 | Gui.Control.showDialog(win) 50 | 51 | def IsActive(self): 52 | return not FreeCAD.ActiveDocument is None -------------------------------------------------------------------------------- /osafe_py_widgets/gui_dxf.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | 4 | from PySide2 import QtCore 5 | 6 | import FreeCAD 7 | import FreeCADGui as Gui 8 | 9 | 10 | class OsafeDxf: 11 | """Gui command for the Create DXF.""" 12 | 13 | def GetResources(self): 14 | menu_text = QtCore.QT_TRANSLATE_NOOP( 15 | "OSAFE", 16 | "DXF") 17 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 18 | "OSAFE", 19 | "Export Foundation To DXF") 20 | path = str( 21 | Path(__file__).parent.parent / "osafe_images" / "dxf.svg" 22 | ) 23 | return {'Pixmap': path, 24 | 'MenuText': menu_text, 25 | 'ToolTip': tool_tip} 26 | 27 | def Activated(self): 28 | from osafe_py_widgets.export import export_to_dxf_dialog 29 | win = export_to_dxf_dialog.Form() 30 | Gui.Control.showDialog(win) 31 | 32 | def IsActive(self): 33 | return not FreeCAD.ActiveDocument is None 34 | 35 | 36 | class OsafeImportDxf: 37 | """Gui command for import DXF files.""" 38 | 39 | def GetResources(self): 40 | menu_text = QtCore.QT_TRANSLATE_NOOP( 41 | "OSAFE", 42 | "Import DXF") 43 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 44 | "OSAFE", 45 | "Import DXF file into current model") 46 | path = str( 47 | Path(__file__).parent.parent / "osafe_images" / "import_dxf.svg" 48 | ) 49 | return {'Pixmap': path, 50 | 'MenuText': menu_text, 51 | 'ToolTip': tool_tip} 52 | 53 | def Activated(self): 54 | from osafe_py_widgets.export import import_from_dxf_dialog 55 | win = import_from_dxf_dialog.Form() 56 | Gui.Control.showDialog(win) 57 | 58 | def IsActive(self): 59 | return not FreeCAD.ActiveDocument is None -------------------------------------------------------------------------------- /osafe_py_widgets/gui_export_strips.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | 4 | from PySide2 import QtCore 5 | 6 | import FreeCAD 7 | import FreeCADGui as Gui 8 | 9 | 10 | class OsafeExportStrips: 11 | """Gui command for the Create DXF.""" 12 | 13 | def GetResources(self): 14 | menu_text = QtCore.QT_TRANSLATE_NOOP( 15 | "OSAFE", 16 | "Export Strips") 17 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 18 | "OSAFE", 19 | "Export Strips to ETABS or SAFE") 20 | path = str( 21 | Path(__file__).parent.parent / "osafe_images" / "export_strips.svg" 22 | ) 23 | return {'Pixmap': path, 24 | 'MenuText': menu_text, 25 | 'ToolTip': tool_tip} 26 | 27 | def Activated(self): 28 | from osafe_py_widgets.export import export_strips_panel 29 | win = export_strips_panel.Form() 30 | Gui.Control.showDialog(win) 31 | 32 | def IsActive(self): 33 | return not FreeCAD.ActiveDocument is None 34 | 35 | 36 | if FreeCAD.GuiUp: 37 | Gui.addCommand('osafe_export_strips',OsafeExportStrips()) -------------------------------------------------------------------------------- /osafe_py_widgets/gui_punch.py: -------------------------------------------------------------------------------- 1 | 2 | from pathlib import Path 3 | 4 | from PySide2 import QtCore 5 | from PySide2.QtCore import QT_TRANSLATE_NOOP 6 | 7 | import FreeCAD 8 | import FreeCADGui as Gui 9 | import Draft 10 | from draftutils.translate import translate 11 | 12 | from osafe_objects.punch import make_punch 13 | 14 | 15 | class Punch: 16 | """Gui command for the Punch.""" 17 | 18 | def GetResources(self): 19 | menu_text = QtCore.QT_TRANSLATE_NOOP( 20 | "civil_punch", 21 | "Create punch") 22 | tool_tip = QtCore.QT_TRANSLATE_NOOP( 23 | "civil_punch", 24 | "Create punch") 25 | path = str( 26 | Path(__file__).parent.parent / "osafe_images" / "punch.svg" 27 | ) 28 | return {'Pixmap': path, 29 | 'MenuText': menu_text, 30 | 'ToolTip': tool_tip} 31 | 32 | def Activated(self): 33 | def is_foundation_type(obj): 34 | if hasattr(obj, 'Proxy') and hasattr(obj.Proxy, 'Type') and obj.Proxy.Type == 'Foundation': 35 | return True 36 | return False 37 | 38 | doc = FreeCAD.ActiveDocument 39 | sel = Gui.Selection.getSelection() 40 | foun = None 41 | if sel and is_foundation_type(sel[0]): 42 | foun = sel[0] 43 | if foun is None: 44 | for o in doc.Objects: 45 | if is_foundation_type(o): 46 | foun = o 47 | break 48 | if foun is None: 49 | return 50 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Create Punches")) 51 | if hasattr(doc, 'Punches'): 52 | punches = doc.Punches 53 | columns = [punch.column.Name for punch in punches.Group] 54 | else: 55 | punches = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Punches") 56 | columns = [] 57 | for o in doc.Objects: 58 | if hasattr(o, 'IfcType') and \ 59 | o.IfcType == 'Column' and \ 60 | hasattr(o, 'combos_load') and \ 61 | hasattr(o, 'Base'): 62 | if o.Name in columns: 63 | continue 64 | punch = make_punch( 65 | foun, 66 | o, 67 | ) 68 | l = punch.Location 69 | pl = FreeCAD.Vector(0, 0, o.Shape.BoundBox.ZMax) 70 | t = '0.0' 71 | text = Draft.make_text([t, l], placement=pl) 72 | punch.Ratio = t 73 | if FreeCAD.GuiUp: 74 | text.ViewObject.FontSize = 200 75 | punch.text = text 76 | punch.id = o.Label 77 | punches.addObject(punch) 78 | FreeCAD.ActiveDocument.recompute() 79 | FreeCAD.ActiveDocument.commitTransaction() 80 | 81 | def IsActive(self): 82 | return not FreeCAD.ActiveDocument is None -------------------------------------------------------------------------------- /osafe_py_widgets/resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../osafe_images/tick.svg 4 | ../osafe_images/osafe_rebar.svg 5 | ../osafe_images/auto_strip.svg 6 | ../osafe_images/force.svg 7 | ../osafe_images/wall.svg 8 | ../osafe_images/export_strips.svg 9 | ../osafe_images/foundation.svg 10 | ../osafe_images/base_foundation.svg 11 | ../osafe_images/safe.png 12 | ../osafe_images/refresh.svg 13 | ../osafe_images/import_dxf.svg 14 | ../osafe_images/base_plate.svg 15 | ../osafe_images/import.svg 16 | ../osafe_images/etabs.png 17 | ../osafe_images/help.png 18 | ../osafe_images/strip.svg 19 | ../osafe_images/punch.svg 20 | ../osafe_images/dxf.svg 21 | ../osafe_images/cancel.png 22 | 23 | 24 | -------------------------------------------------------------------------------- /osafe_py_widgets/wall_panel.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from PySide2 import QtWidgets 4 | 5 | import FreeCAD 6 | import FreeCADGui as Gui 7 | import ArchWall 8 | from draftutils.translate import translate 9 | 10 | from osafe_py_widgets import resource_rc 11 | 12 | punch_path = Path(__file__).parent.parent 13 | 14 | 15 | class WallTaskPanel: 16 | 17 | def __init__(self): 18 | self.form = Gui.PySideUic.loadUi(str(punch_path / 'osafe_widgets' / 'wall_panel.ui')) 19 | self.fill_load_cases() 20 | self.create_connections() 21 | Gui.runCommand('Std_DrawStyle',2) 22 | Gui.runCommand('OSAFE_view_beams',1) 23 | Gui.runCommand('OSAFE_view_basefoundation',0) 24 | Gui.runCommand('OSAFE_view_design_layer_a',0) 25 | Gui.runCommand('OSAFE_view_design_layer_b',0) 26 | 27 | def fill_load_cases(self): 28 | deads = [] 29 | try: 30 | import find_etabs 31 | etabs, _ = find_etabs.find_etabs(run=False, backup=False) 32 | deads = etabs.load_patterns.get_special_load_pattern_names(1) 33 | except: 34 | if hasattr(FreeCAD, 'load_cases'): 35 | deads = FreeCAD.load_cases 36 | self.form.loadpat.addItems(deads) 37 | 38 | def create_connections(self): 39 | self.form.create_button.clicked.connect(self.create_wall) 40 | 41 | def create_wall(self): 42 | loadpat = self.form.loadpat.currentText() 43 | if not loadpat: 44 | QtWidgets.QMessageBox.warning(None, "Load Case Name", "Please Enter the name of the Loadcase.") 45 | return 46 | sel = Gui.Selection.getSelection() 47 | if len(sel) == 0: 48 | QtWidgets.QMessageBox.warning(None, "Selection", "Please Select Lines or Wires.") 49 | return 50 | 51 | wires = [] 52 | import draftutils.utils as utils 53 | for s in sel: 54 | if utils.get_type(s) == 'Wire': 55 | wires.append(s) 56 | if len(wires) == 0: 57 | QtWidgets.QMessageBox.warning(None, "Selection", "Please Select Lines or Wires.") 58 | return 59 | weight = self.form.weight.value() 60 | height = self.form.height_box.value() 61 | create_blocks = self.form.create_blocks.isChecked() 62 | mat = FreeCAD.ActiveDocument.findObjects(Type='App::MaterialObjectPython') 63 | FreeCAD.ActiveDocument.openTransaction(translate("OSAFE","Create Walls")) 64 | if not mat: 65 | import Arch 66 | mat = Arch.makeMaterial('Bricks') 67 | mat.Color = (0.89, .89, 0.) 68 | else: 69 | mat = mat[0] 70 | for s in wires: 71 | wall = ArchWall.makeWall(baseobj=s,height=height * 1000) 72 | wall.addProperty('App::PropertyInteger', 'weight', 'Wall') 73 | wall.addProperty('App::PropertyString', 'loadpat', 'Wall') 74 | wall.loadpat = loadpat 75 | wall.weight = weight 76 | wall.Base.ViewObject.Visibility = True 77 | wall.Material = mat 78 | wall.Width = 300 79 | if create_blocks: 80 | wall.MakeBlocks = True 81 | wall.BlockHeight = 400 82 | wall.BlockLength = 800 83 | wall.OffsetSecond = 400 84 | wall.Joint = 40 85 | wall.ViewObject.DisplayMode = 'Flat Lines' 86 | wall.ViewObject.LineWidth = 1 87 | else: 88 | wall.ViewObject.DisplayMode = 'Shaded' 89 | wall.ViewObject.Transparency = 60 90 | FreeCAD.ActiveDocument.recompute() 91 | Gui.runCommand('Std_DrawStyle',0) 92 | FreeCAD.ActiveDocument.commitTransaction() 93 | -------------------------------------------------------------------------------- /osafe_statusbar.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import os 4 | import FreeCAD 5 | from osafe_translate_utils import * 6 | 7 | 8 | # Language path for InitGui.py 9 | 10 | 11 | def getLanguagePath(): 12 | 13 | return os.path.join(os.path.dirname(__file__),"translations") 14 | 15 | 16 | # Status bar buttons 17 | 18 | 19 | def setStatusIcons(show=True): 20 | 21 | "shows or hides the BIM icons in the status bar" 22 | 23 | import FreeCADGui 24 | from PySide import QtCore,QtGui 25 | 26 | 27 | def addonMgr(): 28 | 29 | mw = FreeCADGui.getMainWindow() 30 | if mw: 31 | st = mw.statusBar() 32 | statuswidget = st.findChild(QtGui.QToolBar,"OSAFEStatusWidget") 33 | if statuswidget: 34 | updatebutton = statuswidget.findChild(QtGui.QPushButton,"UpdateButton") 35 | if updatebutton: 36 | statuswidget.actions()[-1].setVisible(False) 37 | FreeCADGui.runCommand("Std_AddonMgr") 38 | 39 | 40 | class CheckWorker(QtCore.QThread): 41 | 42 | updateAvailable = QtCore.Signal(bool) 43 | 44 | def __init__(self): 45 | 46 | QtCore.QThread.__init__(self) 47 | 48 | def run(self): 49 | 50 | try: 51 | import git 52 | except ImportError: 53 | return 54 | FreeCAD.Console.PrintLog("Checking for available updates of the OSAFE workbench\n") 55 | osafe_dir = os.path.join(FreeCAD.getUserAppDataDir(),"Mod","OSAFE") 56 | etabs_api_dir = os.path.join(FreeCAD.getUserAppDataDir(),"Mod","etabs_api") 57 | for directory in (osafe_dir, etabs_api_dir): 58 | if os.path.exists(directory) and os.path.exists(directory + os.sep + '.git'): 59 | gitrepo = git.Git(directory) 60 | try: 61 | gitrepo.fetch() 62 | if "git pull" in gitrepo.status(): 63 | self.updateAvailable.emit(True) 64 | return 65 | except: 66 | # can fail for any number of reasons, ex. not being online 67 | pass 68 | self.updateAvailable.emit(False) 69 | 70 | def checkUpdates(): 71 | FreeCAD.osafe_update_checker = CheckWorker() 72 | FreeCAD.osafe_update_checker.updateAvailable.connect(showUpdateButton) 73 | FreeCAD.osafe_update_checker.start() 74 | 75 | def showUpdateButton(avail): 76 | if avail: 77 | FreeCAD.Console.PrintLog("An OSAFE update is available\n") 78 | mw = FreeCADGui.getMainWindow() 79 | if mw: 80 | st = mw.statusBar() 81 | statuswidget = st.findChild(QtGui.QToolBar,"OSAFEStatusWidget") 82 | if statuswidget: 83 | updatebutton = statuswidget.findChild(QtGui.QPushButton,"UpdateButton") 84 | if updatebutton: 85 | # updatebutton.show() # doesn't work for some reason 86 | statuswidget.actions()[-1].setVisible(True) 87 | else: 88 | FreeCAD.Console.PrintLog("No OSAFE update available\n") 89 | if hasattr(FreeCAD,"osafe_update_checker"): 90 | del FreeCAD.osafe_update_checker 91 | 92 | 93 | # main code 94 | 95 | mw = FreeCADGui.getMainWindow() 96 | if mw: 97 | st = mw.statusBar() 98 | statuswidget = st.findChild(QtGui.QToolBar,"OSAFEStatusWidget") 99 | if show: 100 | if statuswidget: 101 | statuswidget.show() 102 | else: 103 | statuswidget = QtGui.QToolBar() 104 | statuswidget.setObjectName("OSAFEStatusWidget") 105 | 106 | # update notifier button (starts hidden) 107 | updatebutton = QtGui.QPushButton() 108 | bwidth = updatebutton.fontMetrics().boundingRect("AAAA").width() 109 | updatebutton.setObjectName("UpdateButton") 110 | updatebutton.setMaximumWidth(bwidth) 111 | updatebutton.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(__file__),"images","update.svg"))) 112 | updatebutton.setText("") 113 | updatebutton.setToolTip(translate("osafe","An update to the OSAFE workbench is available. Click here to open the addons manager.")) 114 | updatebutton.setFlat(True) 115 | QtCore.QObject.connect(updatebutton,QtCore.SIGNAL("pressed()"),addonMgr) 116 | updatebutton.hide() 117 | statuswidget.addWidget(updatebutton) 118 | st.addPermanentWidget(statuswidget) 119 | QtCore.QTimer.singleShot(2500, checkUpdates) # delay a bit the check for osafe WB update... 120 | else: 121 | if statuswidget: 122 | statuswidget.hide() 123 | else: 124 | # when switching workbenches, the toolbar sometimes "jumps" 125 | # out of the status bar to any other dock area... 126 | statuswidget = mw.findChild(QtGui.QToolBar,"OSAFEStatusWidget") 127 | if statuswidget: 128 | statuswidget.hide() 129 | 130 | -------------------------------------------------------------------------------- /osafe_translate_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | #*************************************************************************** 4 | #* * 5 | #* Copyright (c) 2017 Yorik van Havre * 6 | #* * 7 | #* This program is free software; you can redistribute it and/or modify * 8 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 9 | #* as published by the Free Software Foundation; either version 2 of * 10 | #* the License, or (at your option) any later version. * 11 | #* for detail see the LICENCE text file. * 12 | #* * 13 | #* This program is distributed in the hope that it will be useful, * 14 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | #* GNU Library General Public License for more details. * 17 | #* * 18 | #* You should have received a copy of the GNU Library General Public * 19 | #* License along with this program; if not, write to the Free Software * 20 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | #* USA * 22 | #* * 23 | #*************************************************************************** 24 | 25 | """Locate the translation utils""" 26 | 27 | import FreeCAD 28 | 29 | # dummy function for the QT translator 30 | def QT_TRANSLATE_NOOP(ctx,txt): 31 | return txt 32 | 33 | # use latest available translate function 34 | if hasattr(FreeCAD,"Qt"): 35 | translate = FreeCAD.Qt.translate 36 | else: 37 | from DraftTools import translate 38 | -------------------------------------------------------------------------------- /osafe_widgets/change_branch.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | QDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 330 10 | 300 11 | 12 | 13 | 14 | Change Branch 15 | 16 | 17 | 18 | 19 | 20 | Select Branch 21 | 22 | 23 | 24 | 25 | 26 | 27 | 1 28 | 29 | 30 | 31 | master 32 | 33 | 34 | 35 | 36 | develop 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /osafe_widgets/civil_welcome.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 468 10 | 617 11 | 12 | 13 | 14 | Welcome 15 | 16 | 17 | 18 | 19 | 20 | 21 | 450 22 | 178 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 16 35 | 36 | 37 | 38 | Welcome to the Civil workbench! 39 | 40 | 41 | 42 | 43 | 44 | 45 | <html><head/><body><p>This appears to be the first time that you are using the Civil workbench. If you press OK, the next screen will propose you to set a couple of typical FreeCAD options that are suitable for civil work. You can change these options anytime later under menu <span style=" font-weight:600;">Edit -&gt; Preferences</span></p></body></html> 46 | 47 | 48 | true 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 16 57 | 58 | 59 | 60 | Keep updated! 61 | 62 | 63 | 64 | 65 | 66 | 67 | <html><head/><body><p>The Civil workbench is a work-in-progress and will change quite often. Make sure you <span style=" font-weight:600;">update</span> it regularly.</p></body></html> 68 | 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | Qt::Horizontal 78 | 79 | 80 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | buttonBox 90 | accepted() 91 | Dialog 92 | accept() 93 | 94 | 95 | 248 96 | 254 97 | 98 | 99 | 157 100 | 274 101 | 102 | 103 | 104 | 105 | buttonBox 106 | rejected() 107 | Dialog 108 | reject() 109 | 110 | 111 | 316 112 | 260 113 | 114 | 115 | 286 116 | 274 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /osafe_widgets/draw_strip.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 241 10 | 162 11 | 12 | 13 | 14 | Create Base of Foundation 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | cm 23 | 24 | 25 | 1 26 | 27 | 28 | 1000 29 | 30 | 31 | 5 32 | 33 | 34 | 100 35 | 36 | 37 | 38 | 39 | 40 | 41 | cm 42 | 43 | 44 | 0 45 | 46 | 47 | 1000 48 | 49 | 50 | 5 51 | 52 | 53 | 50 54 | 55 | 56 | 57 | 58 | 59 | 60 | Width 61 | 62 | 63 | 64 | 65 | 66 | 67 | Left Width 68 | 69 | 70 | 71 | 72 | 73 | 74 | cm 75 | 76 | 77 | 0 78 | 79 | 80 | 1000 81 | 82 | 83 | 5 84 | 85 | 86 | 50 87 | 88 | 89 | 90 | 91 | 92 | 93 | Strip Layer 94 | 95 | 96 | 97 | 98 | 99 | 100 | Right Width 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | A 109 | 110 | 111 | 112 | 113 | B 114 | 115 | 116 | 117 | 118 | other 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Center 128 | 129 | 130 | 131 | 132 | Left 133 | 134 | 135 | 136 | 137 | Right 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | Align 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | strip_layer 155 | align 156 | width_spinbox 157 | left_width_spinbox 158 | right_width_spinbox 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /osafe_widgets/export/export_to_dxf.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 285 10 | 256 11 | 12 | 13 | 14 | Export Model to DXF 15 | 16 | 17 | 18 | :/osafe_images/dxf.svg:/osafe_images/dxf.svg 19 | 20 | 21 | 22 | 23 | 24 | Columns 25 | 26 | 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | Punches 35 | 36 | 37 | 38 | :/osafe_images/punch.svg:/osafe_images/punch.svg 39 | 40 | 41 | true 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | *.dxf 57 | 58 | 59 | 60 | 61 | 62 | 63 | Browse 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Export 75 | 76 | 77 | 78 | :/safe/osafe_images/dxf.svg:/safe/osafe_images/dxf.svg 79 | 80 | 81 | 82 | 40 83 | 40 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Cancel 92 | 93 | 94 | 95 | :/safe/osafe_images/cancel.png:/safe/osafe_images/cancel.png 96 | 97 | 98 | 99 | 40 100 | 40 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /osafe_widgets/force_panel.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 201 10 | 149 11 | 12 | 13 | 14 | Assign Loads 15 | 16 | 17 | 18 | :/safe/osafe_images/force.svg:/safe/osafe_images/force.svg 19 | 20 | 21 | 22 | 23 | 24 | LoadCase 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | Value 39 | 40 | 41 | 42 | 43 | 44 | 45 | Kg / m2 46 | 47 | 48 | 100000 49 | 50 | 51 | 5 52 | 53 | 54 | 200 55 | 56 | 57 | 58 | 59 | 60 | 61 | Assign 62 | 63 | 64 | 65 | :/safe/osafe_images/force.svg:/safe/osafe_images/force.svg 66 | 67 | 68 | 69 | 60 70 | 60 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /osafe_widgets/import_from_dxf.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 247 10 | 183 11 | 12 | 13 | 14 | Import DXF Model 15 | 16 | 17 | 18 | :/osafe_images/import_dxf.svg:/osafe_images/import_dxf.svg 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | *.dxf 33 | 34 | 35 | 36 | 37 | 38 | 39 | Browse 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Scale 51 | 52 | 53 | 54 | 55 | 56 | 57 | 3 58 | 59 | 60 | 100000.000000000000000 61 | 62 | 63 | 1000.000000000000000 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Import 75 | 76 | 77 | 78 | :/safe/osafe_images/import_dxf.svg:/safe/osafe_images/import_dxf.svg 79 | 80 | 81 | 82 | 40 83 | 40 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Cancel 92 | 93 | 94 | 95 | :/safe/osafe_images/cancel.png:/safe/osafe_images/cancel.png 96 | 97 | 98 | 99 | 40 100 | 40 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /osafe_widgets/wall_panel.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 207 10 | 205 11 | 12 | 13 | 14 | Create Wall 15 | 16 | 17 | 18 | :/safe/osafe_images/wall.svg:/safe/osafe_images/wall.svg 19 | 20 | 21 | 22 | 23 | 24 | Kg/m2 25 | 26 | 27 | 50 28 | 29 | 30 | 10000 31 | 32 | 33 | 5 34 | 35 | 36 | 300 37 | 38 | 39 | 40 | 41 | 42 | 43 | Unit Weight 44 | 45 | 46 | 47 | 48 | 49 | 50 | Create 51 | 52 | 53 | 54 | :/safe/osafe_images/wall.svg:/safe/osafe_images/wall.svg 55 | 56 | 57 | 58 | 60 59 | 60 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Height 68 | 69 | 70 | 71 | 72 | 73 | 74 | Create Blocks 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | m 85 | 86 | 87 | 0.100000000000000 88 | 89 | 90 | 20.000000000000000 91 | 92 | 93 | 0.100000000000000 94 | 95 | 96 | 3.200000000000000 97 | 98 | 99 | 100 | 101 | 102 | 103 | Load Pattern 104 | 105 | 106 | 107 | 108 | 109 | 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | OSAFE 3 | This is a workbench for FreeCAD that creates foundation model from CSI ETABS model results. 4 | 2022.05.29 5 | Raeyat Roknabadi Ebrahim 6 | LGPL-2.1-or-later 7 | https://github.com/ebrahimraeyat/OSAFE 8 | https://github.com/ebrahimraeyat/OSAFE/wiki 9 | osafe_images/safe.png 10 | 11 | 12 | 13 | OSAFE 14 | ./ 15 | comtypes 16 | etabs-api 17 | pydocx 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file requirements.txt requirements.in 6 | # 7 | et-xmlfile>=1.0.1 # via openpyxl 8 | jdcal>=1.4 # via openpyxl 9 | numpy>=1.15.4 # via pandas 10 | openpyxl>=2.5.11 11 | pandas>=0.23.4 12 | python-dateutil>=2.7.5 # via pandas 13 | pytz>=2018.7 # via pandas 14 | six>=1.11.0 # via python-dateutil 15 | xlrd>=1.1.0 16 | -------------------------------------------------------------------------------- /test/osafe_import_export/test_report.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import matplotlib.pyplot as plt 5 | 6 | # path to FreeCAD 7 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 8 | sys.path.append(FREECADPATH) 9 | import FreeCAD 10 | 11 | punch_path = Path(__file__).parent.parent.parent 12 | etabs_api_path = punch_path.parent / 'etabs_api' 13 | sys.path.insert(0, str(punch_path)) 14 | sys.path.insert(1, str(etabs_api_path)) 15 | from osafe_import_export import report 16 | 17 | from python_functions import get_temp_filepath 18 | from freecad_funcs import open_file 19 | 20 | 21 | import pandas as pd 22 | 23 | filename = punch_path / 'test' / 'test_files' / 'freecad' / 'base_plate.FCStd' 24 | circle_filename = punch_path / 'test' / 'test_files' / 'freecad' / 'circle_column.FCStd' 25 | document= FreeCAD.openDocument(str(filename)) 26 | circle_document= FreeCAD.openDocument(str(circle_filename)) 27 | 28 | def test_get_punch_picture(): 29 | punch = document.Punch 30 | filename = get_temp_filepath(suffix='jpg', random=True) 31 | fname = report.get_punch_picture(punch, filename=filename) 32 | assert fname.exists() 33 | open_file(fname) 34 | 35 | def test_get_punch_picture_circle(): 36 | punch = circle_document.Punch009 37 | filename = get_temp_filepath(suffix='jpg', random=True) 38 | fname = report.get_punch_picture(punch, filename=filename) 39 | assert fname.exists() 40 | open_file(fname) 41 | 42 | def test_add_equivalent_column(): 43 | punch = document.Punch 44 | fig = plt.figure() 45 | ax1 = fig.add_subplot(211, aspect='equal') 46 | plt.axis('off') 47 | report.add_equivalent_column(punch, ax1) 48 | filename = get_temp_filepath(suffix='jpg', random=True) 49 | fig.savefig(filename) 50 | assert filename.exists() 51 | open_file(filename) 52 | 53 | def test_add_punch_edges_to_ax(): 54 | punch = document.Punch 55 | fig = plt.figure() 56 | ax1 = fig.add_subplot(211, aspect='equal') 57 | plt.axis('off') 58 | report.add_punch_edges_to_ax(punch, ax1) 59 | filename = get_temp_filepath(suffix='jpg', random=True) 60 | fig.savefig(filename) 61 | 62 | def test_add_punch_edges_to_ax_circle(): 63 | punch = circle_document.Punch009 64 | fig = plt.figure() 65 | ax1 = fig.add_subplot(211, aspect='equal') 66 | plt.axis('off') 67 | report.add_punch_edges_to_ax(punch, ax1) 68 | filename = get_temp_filepath(suffix='jpg', random=True) 69 | fig.savefig(filename) 70 | assert filename.exists() 71 | open_file(filename) 72 | 73 | def test_export_dataframe_to_docx(): 74 | punch = document.Punch 75 | df = pd.DataFrame(list(punch.combos_ratio.items()), columns=['Combo', 'Ratio']) 76 | doc = report.export_dataframe_to_docx(df) 77 | filename = get_temp_filepath(suffix='docx', random=True) 78 | doc.save(filename) 79 | 80 | def test_create_report(): 81 | punch = document.Punch 82 | filename = get_temp_filepath(suffix='docx', random=True) 83 | report.create_report(punch, filename) 84 | assert filename.exists() 85 | open_file(filename) 86 | 87 | def test_create_report_circle(): 88 | punch = circle_document.Punch009 89 | filename = get_temp_filepath(suffix='docx', random=True) 90 | report.create_report(punch, filename) 91 | assert filename.exists() 92 | open_file(filename) 93 | 94 | # def test_get_edges_direction_in_punch(): 95 | # punch = document.Punch 96 | # edges_direction = report.get_edges_direction_in_punch(punch) 97 | # assert list(edges_direction.values()) == ['BOT', 'RIGHT', 'TOP'] 98 | 99 | def test_export_dict_to_doc(): 100 | d = {'location': 'center', 'id': 227} 101 | doc = report.export_dict_to_doc(d, []) 102 | filename = get_temp_filepath(suffix='docx', random=True) 103 | doc.save(filename) 104 | assert True 105 | 106 | def test_add_punch_properties_to_doc(): 107 | punch = document.Punch 108 | doc = report.add_punch_properties_to_doc(punch) 109 | filename = get_temp_filepath(suffix='docx', random=True) 110 | doc.save(filename) 111 | assert True 112 | 113 | def test_create_punches_report(): 114 | filename = get_temp_filepath(suffix='docx', random=True) 115 | report.create_punches_report(document, filename) 116 | 117 | def test_create_punches_report_circle(): 118 | filename = get_temp_filepath(suffix='docx', random=True) 119 | report.create_punches_report(circle_document, filename) 120 | 121 | 122 | if __name__ == '__main__': 123 | # test_get_punch_picture() 124 | test_create_report() 125 | # test_add_punch_properties_to_doc() 126 | # test_export_dict_to_doc() 127 | # test_create_punches_report() -------------------------------------------------------------------------------- /test/osafe_objects/test_base_plate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 7 | sys.path.append(FREECADPATH) 8 | 9 | import FreeCAD 10 | 11 | 12 | 13 | punch_path = Path(__file__).parent.parent.parent 14 | sys.path.insert(0, str(punch_path)) 15 | from osafe_objects import base_plate 16 | 17 | 18 | def test_make_base_plate(): 19 | doc = FreeCAD.newDocument() 20 | FreeCAD.setActiveDocument(doc.Name) 21 | import Arch 22 | col = Arch.makeStructure() 23 | col.recompute(True) 24 | bx = 500 25 | by = 600 26 | bp = base_plate.make_base_plate(bx=bx, by=by, column=col.Label) 27 | bp.recompute(True) 28 | assert hasattr(col, 'base_plate') 29 | assert col.base_plate.Name == bp.Name 30 | assert bp.Bx == bx 31 | assert bp.By == by 32 | 33 | if __name__ == '__main__': 34 | test_make_base_plate() -------------------------------------------------------------------------------- /test/osafe_objects/test_osafe_rebar.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 7 | sys.path.append(FREECADPATH) 8 | 9 | import FreeCAD 10 | 11 | 12 | 13 | punch_path = Path(__file__).parent.parent.parent 14 | sys.path.insert(0, str(punch_path)) 15 | from osafe_objects import osafe_rebar 16 | 17 | filename_rashidzadeh = Path(__file__).parent.parent / 'test_files' / 'freecad' / 'rashidzadeh.FCStd' 18 | 19 | 20 | 21 | def test_make_rebars(): 22 | doc = FreeCAD.openDocument(str(filename_rashidzadeh)) 23 | rebars = osafe_rebar.make_rebars() 24 | FreeCAD.ActiveDocument.recompute() 25 | assert len(rebars.Group) == len(doc.Foundation.base_foundations) 26 | for rebar in rebars.Group: 27 | assert not rebar.Shape.isNull() 28 | 29 | def test_make_rebar_from_scratch(): 30 | doc = FreeCAD.newDocument() 31 | rebar = osafe_rebar.make_rebar_from_scratch(doc=doc) 32 | FreeCAD.ActiveDocument.recompute() 33 | assert not rebar.Shape.isNull() 34 | 35 | if __name__ == '__main__': 36 | test_make_rebars() -------------------------------------------------------------------------------- /test/osafe_objects/test_strip.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 7 | sys.path.append(FREECADPATH) 8 | 9 | import FreeCAD 10 | import Part 11 | import Draft 12 | 13 | 14 | 15 | punch_path = Path(__file__).parent.parent.parent 16 | sys.path.insert(0, str(punch_path)) 17 | from osafe_objects.strip import make_strip 18 | 19 | filename_rashidzadeh = Path(__file__).parent.parent / 'test_files' / 'freecad' / 'rashidzadeh.FCStd' 20 | 21 | 22 | 23 | def test_make_strip(): 24 | FreeCAD.newDocument() 25 | # Part wire 26 | p11 = FreeCAD.Vector(0, 0, 0) 27 | p22 = FreeCAD.Vector(10000, 0, 0) 28 | wire = Part.Wire(Part.makeLine(p11, p22)) 29 | strip = make_strip(base=wire) 30 | FreeCAD.ActiveDocument.recompute() 31 | assert strip.Shape.Area == 10000 * 1000 32 | assert strip.Shape.BoundBox.YMax == 500 33 | assert strip.Shape.BoundBox.YMin == -500 34 | # Draft wire 35 | wire = Draft.make_wire(wire) 36 | strip = make_strip(base=wire) 37 | FreeCAD.ActiveDocument.recompute() 38 | assert strip.Shape.Area == 10000 * 1000 39 | assert strip.Shape.BoundBox.YMax == 500 40 | assert strip.Shape.BoundBox.YMin == -500 41 | 42 | if __name__ == '__main__': 43 | test_make_strip() -------------------------------------------------------------------------------- /test/test_beam.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | from pathlib import Path 4 | 5 | import pytest 6 | 7 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 8 | sys.path.append(FREECADPATH) 9 | 10 | import FreeCAD 11 | 12 | filename_base_plate = Path(__file__).parent / 'test_files' / 'freecad' / 'base_plate.FCStd' 13 | document_base_plate = FreeCAD.openDocument(str(filename_base_plate)) 14 | 15 | 16 | punch_path = Path(__file__).parent.parent 17 | sys.path.insert(0, str(punch_path)) 18 | 19 | from osafe_objects import beam, punch 20 | 21 | def test_make_beam(): 22 | p1 = FreeCAD.Vector(0, 0, 0) 23 | p2 = FreeCAD.Vector(1, 0, 0) 24 | beam.make_beam(p1, p2) 25 | assert True 26 | 27 | def test_punch_reinforcement(): 28 | foun = document_base_plate.Foundation 29 | col = document_base_plate.getObjectsByLabel('C1_Story1')[0] 30 | p = punch.make_punch(foun, col) 31 | p.Use_Reinforcement = True 32 | p.Proxy.execute(p) 33 | assert True 34 | 35 | 36 | 37 | 38 | if __name__ == '__main__': 39 | test_make_punch() -------------------------------------------------------------------------------- /test/test_etabs_foundation.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | 5 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 6 | sys.path.append(FREECADPATH) 7 | 8 | import FreeCAD 9 | 10 | filename_base_foundation = Path(__file__).parent / 'test_files' / 'freecad' / 'base_foundation.FCStd' 11 | document_base_foundation = FreeCAD.openDocument(str(filename_base_foundation)) 12 | 13 | 14 | punch_path = Path(__file__).parent.parent 15 | sys.path.insert(0, str(punch_path)) 16 | from osafe_objects import etabs_foundation 17 | 18 | 19 | def test_make_foundation(): 20 | bfs = [] 21 | for o in document_base_foundation.Objects: 22 | if hasattr(o, 'Proxy') and o.Proxy and o.Proxy.Type == 'BaseFoundation': 23 | bfs.append(o) 24 | ret = etabs_foundation.make_foundation(base_foundations=bfs) 25 | assert True 26 | 27 | def test_change_foundation_height(): 28 | bfs = [] 29 | for o in document_base_foundation.Objects: 30 | if hasattr(o, 'Proxy') and o.Proxy and o.Proxy.Type == 'BaseFoundation': 31 | bfs.append(o) 32 | ret = etabs_foundation.make_foundation(base_foundations=bfs) 33 | ret.height = 810 34 | document_base_foundation.recompute([ret]) 35 | assert ret.height_punch.Value == 810 36 | 37 | 38 | 39 | 40 | if __name__ == '__main__': 41 | test_make_foundation() -------------------------------------------------------------------------------- /test/test_etabs_punch.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | # path to FreeCAD.so 5 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 6 | sys.path.append(FREECADPATH) 7 | import FreeCAD 8 | 9 | punch_path = Path(__file__).parent.parent 10 | sys.path.insert(0, str(punch_path)) 11 | from osafe_py_widgets import etabs_punch 12 | document= FreeCAD.newDocument() 13 | etabs = etabs_punch.EtabsPunch(beam_names=['114', '115', '116']) 14 | 15 | # def test_create_vectors(): 16 | # etabs.create_vectors() 17 | # assert True 18 | 19 | def test_create_slabs_plan(): 20 | etabs.create_slabs_plan() 21 | 22 | def test_create_columns(): 23 | etabs.create_columns() 24 | 25 | def test_import_load_combos(): 26 | etabs = etabs_punch.EtabsPunch() 27 | etabs.import_data(import_load_combos=True) 28 | 29 | if __name__ == '__main__': 30 | test_import_load_combos() -------------------------------------------------------------------------------- /test/test_f2k_object.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | 5 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 6 | sys.path.append(FREECADPATH) 7 | 8 | import FreeCAD 9 | 10 | # filename_base_plate = Path(__file__).absolute().parent.parent / 'test_files' / 'freecad' / 'base_plate.FCStd' 11 | # document_base_plate = FreeCAD.openDocument(str(filename_base_plate)) 12 | 13 | 14 | punch_path = Path(__file__).absolute().parent.parent 15 | sys.path.insert(0, str(punch_path)) 16 | 17 | 18 | def test_make_safe_f2k(): 19 | FreeCAD.newDocument() 20 | from osafe_objects import f2k_object 21 | f2k_object.make_safe_f2k() 22 | assert True 23 | 24 | 25 | if __name__ == '__main__': 26 | test_make_safe_f2k() -------------------------------------------------------------------------------- /test/test_files/.~lock.davoodabadi_dynamic.xlsx#: -------------------------------------------------------------------------------- 1 | ,ebi,manjaro,14.10.2018 20:02,file:///home/ebi/.config/libreoffice/4; -------------------------------------------------------------------------------- /test/test_files/davoodabadi_dynamic.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/davoodabadi_dynamic.xlsx -------------------------------------------------------------------------------- /test/test_files/freecad/adampira.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/adampira.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/base_foundation.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/base_foundation.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/base_plate.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/base_plate.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/circle_column.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/circle_column.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/kazemi.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/kazemi.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/khalaji.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/khalaji.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/mat.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/mat.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/rashidzadeh.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/rashidzadeh.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/strip.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/strip.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/strip_foundation.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/strip_foundation.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/test.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/test.FCStd -------------------------------------------------------------------------------- /test/test_files/freecad/test_p.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/freecad/test_p.FCStd -------------------------------------------------------------------------------- /test/test_files/khojasteh_97-07-18.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/khojasteh_97-07-18.xlsx -------------------------------------------------------------------------------- /test/test_files/safdari.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/safdari.xlsx -------------------------------------------------------------------------------- /test/test_files/sattari_safe.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebrahimraeyat/OSAFE/57c76763eb14942eaea848f773886f8590bcae54/test/test_files/sattari_safe.xlsx -------------------------------------------------------------------------------- /test/test_geom.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 5 | sys.path.append(FREECADPATH) 6 | 7 | import FreeCAD 8 | 9 | 10 | punch_path = Path(__file__).absolute().parent.parent 11 | sys.path.insert(0, str(punch_path)) 12 | 13 | from old_punch import geom 14 | 15 | def test_make_column(): 16 | bx = 400 17 | by = 600 18 | center = FreeCAD.Vector(1000, 1000, 0) 19 | d = {} 20 | FreeCAD.newDocument() 21 | col = geom.make_column(bx, by, center, d) 22 | assert hasattr(col, 'Base') and hasattr(col.Base, 'Height') 23 | assert col.Base.Height == 600 24 | 25 | 26 | if __name__ == '__main__': 27 | test_make_column() -------------------------------------------------------------------------------- /test/test_opening.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | # path to FreeCAD.so 5 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 6 | sys.path.append(FREECADPATH) 7 | import FreeCAD 8 | 9 | punch_path = Path(__file__).absolute().parent.parent 10 | sys.path.insert(0, str(punch_path)) 11 | from osafe_objects.opening import make_opening 12 | document= FreeCAD.newDocument() 13 | 14 | def test_make_opening(): 15 | x1 = 10 16 | x2 = 25 17 | y1 = 7 18 | y2 = 17 19 | p1=FreeCAD.Vector(x1, y1, 0) 20 | p2=FreeCAD.Vector(x2, y1, 0) 21 | p3=FreeCAD.Vector(x2, y2, 0) 22 | p4=FreeCAD.Vector(x1, y2, 0) 23 | points = [p1, p2, p3, p4] 24 | obj = make_opening( 25 | points=points, 26 | height = 3, 27 | doc=document, 28 | ) 29 | assert obj.plan.Area == 15 * 10 30 | 31 | 32 | if __name__ == '__main__': 33 | test_make_opening() 34 | -------------------------------------------------------------------------------- /test/test_punch.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | FREECADPATH = 'G:\\program files\\FreeCAD 0.21\\bin' 7 | sys.path.append(FREECADPATH) 8 | 9 | import FreeCAD 10 | 11 | filename_base_plate = Path(__file__).parent / 'test_files' / 'freecad' / 'base_plate.FCStd' 12 | document_base_plate = FreeCAD.openDocument(str(filename_base_plate)) 13 | 14 | 15 | punch_path = Path(__file__).absolute().parent.parent 16 | sys.path.insert(0, str(punch_path)) 17 | from osafe_objects import punch 18 | 19 | 20 | def test_make_punch(): 21 | foun = document_base_plate.Foundation 22 | col = document_base_plate.getObjectsByLabel('C1_Story1')[0] 23 | p = punch.make_punch(foun, col) 24 | p.Proxy.execute(p) 25 | assert True 26 | 27 | def test_punch_reinforcement(): 28 | foun = document_base_plate.Foundation 29 | col = document_base_plate.getObjectsByLabel('C1_Story1')[0] 30 | p = punch.make_punch(foun, col) 31 | p.Use_Reinforcement = True 32 | p.Proxy.execute(p) 33 | assert True 34 | 35 | def test_rotated_punch(): 36 | foun = document_base_plate.Foundation 37 | col = document_base_plate.getObjectsByLabel('C1_Story1')[0] 38 | p = punch.make_punch(foun, col) 39 | p.Proxy.execute(p) 40 | # Test Location 41 | for angle in range(0, 400, 10): 42 | col.AttachmentOffset = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),angle)) 43 | document_base_plate.recompute() 44 | assert p.Location == 'Corner 1' 45 | # Test Ratio 46 | sec = col.Base 47 | sec.Height = 500 48 | sec.Width = 500 49 | # col.Proxy 50 | col.AttachmentOffset = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) 51 | document_base_plate.recompute() 52 | assert col.Shape.BoundBox.XLength == 500 53 | assert col.Shape.BoundBox.YLength == 500 54 | # document_base_plate.recompute() 55 | for i in range(0, 90, 10): 56 | col.AttachmentOffset = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),i)) 57 | p.Proxy.execute(p) 58 | r1 = p.Ratio 59 | print(20 * '=' + '\n') 60 | print(i, p.Ratio) 61 | print(20 * '=' + '\n') 62 | for angle in range(i+90, 720, 90): 63 | col.AttachmentOffset = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),angle)) 64 | p.Proxy.execute(p) 65 | # document_base_plate.recompute() 66 | print(p.angle, p.Ratio) 67 | assert pytest.approx(float(r1), abs=.01) == pytest.approx(float(p.Ratio), abs=.01) 68 | 69 | 70 | if __name__ == '__main__': 71 | test_make_punch() -------------------------------------------------------------------------------- /test/test_rectangular_slab.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 5 | sys.path.append(FREECADPATH) 6 | 7 | import FreeCAD 8 | 9 | 10 | punch_path = Path(__file__).absolute().parent.parent 11 | sys.path.insert(0, str(punch_path)) 12 | 13 | import rectangular_slab 14 | 15 | def test_make_rectangular_slab(): 16 | from beam import make_beam 17 | FreeCAD.newDocument() 18 | p1 = FreeCAD.Vector(0, 0, 0) 19 | p2 = FreeCAD.Vector(1000, 0, 0) 20 | p3 = FreeCAD.Vector(3000, 2000, 0) 21 | b1 = make_beam(p1, p2) 22 | b2 = make_beam(p2, p3) 23 | rectangular_slab.make_rectangular_slab( 24 | beams=[b1, b2], 25 | ) 26 | 27 | 28 | if __name__ == '__main__': 29 | test_make_rectangular_slab() -------------------------------------------------------------------------------- /test/test_trapezoidal_slab.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | # path to FreeCAD.so 5 | FREECADPATH = 'G:\\program files\\FreeCAD 0.19\\bin' 6 | sys.path.append(FREECADPATH) 7 | import FreeCAD 8 | 9 | punch_path = Path(__file__).absolute().parent.parent 10 | sys.path.insert(0, str(punch_path)) 11 | from trapezoidal_slab import make_trapezoidal_slab 12 | document= FreeCAD.newDocument() 13 | 14 | # def test_create_vectors(): 15 | # etabs.create_vectors() 16 | # assert True 17 | 18 | def test_make_trapezoidal_slab(): 19 | obj = make_trapezoidal_slab(p1=FreeCAD.Vector(0, 16400, 0), 20 | p2=FreeCAD.Vector(12210, 16400, 0), 21 | layer='A', 22 | design_type='column', 23 | swl='25 cm', 24 | swr=250, 25 | ewl=250, 26 | ewr=250, 27 | ) 28 | assert obj.plan.Area == 12210 * 500 29 | assert obj.layer == 'A' 30 | 31 | 32 | if __name__ == '__main__': 33 | test_make_trapezoidal_slab() 34 | --------------------------------------------------------------------------------