├── .gitignore ├── FloatSlider.py ├── FloatSliderText.py ├── LICENSE ├── app.py ├── app.spec ├── images ├── .gitkeep ├── F-RD.PNG ├── G_prime.PNG ├── Gyroid.PNG ├── I2-Y.PNG ├── L-Type.PNG ├── Neovius.PNG ├── P_W_Hybrid.PNG ├── SchwarzD.PNG ├── SchwarzP.PNG ├── Skeletal_1.png ├── Skeletal_2.png ├── Tubular_G.png ├── Tubular_P.png └── iWP.PNG ├── pyinstaller.txt ├── readme.md └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/c,python 3 | 4 | ### C ### 5 | # Prerequisites 6 | *.d 7 | 8 | # Object files 9 | *.o 10 | *.ko 11 | *.obj 12 | *.elf 13 | 14 | # Linker output 15 | *.ilk 16 | *.map 17 | *.exp 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | *.su 46 | *.idb 47 | *.pdb 48 | 49 | # Kernel Module Compile Results 50 | *.mod* 51 | *.cmd 52 | .tmp_versions/ 53 | modules.order 54 | Module.symvers 55 | Mkfile.old 56 | dkms.conf 57 | 58 | ### Python ### 59 | # Byte-compiled / optimized / DLL files 60 | __pycache__/ 61 | *.py[cod] 62 | *$py.class 63 | 64 | # C extensions 65 | 66 | # Distribution / packaging 67 | .Python 68 | build/ 69 | develop-eggs/ 70 | dist/ 71 | downloads/ 72 | eggs/ 73 | .eggs/ 74 | lib/ 75 | lib64/ 76 | parts/ 77 | sdist/ 78 | var/ 79 | wheels/ 80 | *.egg-info/ 81 | .installed.cfg 82 | *.egg 83 | 84 | # PyInstaller 85 | # Usually these files are written by a python script from a template 86 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 87 | *.manifest 88 | *.spec 89 | 90 | # Installer logs 91 | pip-log.txt 92 | pip-delete-this-directory.txt 93 | 94 | # Unit test / coverage reports 95 | htmlcov/ 96 | .tox/ 97 | .coverage 98 | .coverage.* 99 | .cache 100 | .pytest_cache/ 101 | nosetests.xml 102 | coverage.xml 103 | *.cover 104 | .hypothesis/ 105 | 106 | # Translations 107 | *.mo 108 | *.pot 109 | 110 | # Flask stuff: 111 | instance/ 112 | .webassets-cache 113 | 114 | # Scrapy stuff: 115 | .scrapy 116 | 117 | # Sphinx documentation 118 | docs/_build/ 119 | 120 | # PyBuilder 121 | target/ 122 | 123 | # Jupyter Notebook 124 | .ipynb_checkpoints 125 | 126 | # pyenv 127 | .python-version 128 | 129 | # celery beat schedule file 130 | celerybeat-schedule.* 131 | 132 | # SageMath parsed files 133 | *.sage.py 134 | 135 | # Environments 136 | .env 137 | .venv 138 | env/ 139 | venv/ 140 | ENV/ 141 | env.bak/ 142 | venv.bak/ 143 | 144 | # Spyder project settings 145 | .spyderproject 146 | .spyproject 147 | 148 | # Rope project settings 149 | .ropeproject 150 | 151 | # mkdocs documentation 152 | /site 153 | 154 | # mypy 155 | .mypy_cache/ 156 | 157 | 158 | # End of https://www.gitignore.io/api/c,python 159 | -------------------------------------------------------------------------------- /FloatSlider.py: -------------------------------------------------------------------------------- 1 | import wx 2 | 3 | class FloatSlider(wx.Slider): 4 | 5 | def __init__(self, parent, id, value, minval, maxval, res, 6 | size=wx.DefaultSize, style=wx.SL_HORIZONTAL, 7 | name='floatslider'): 8 | self._value = value 9 | self._min = minval 10 | self._max = maxval 11 | self._res = res 12 | ival, imin, imax = [round(v/res) for v in (value, minval, maxval)] 13 | self._islider = super(FloatSlider, self) 14 | self._islider.__init__( 15 | parent, id, ival, imin, imax, size=size, style=style, name=name 16 | ) 17 | self.Bind(wx.EVT_SCROLL, self._OnScroll) 18 | 19 | def _OnScroll(self, event): 20 | ival = self._islider.GetValue() 21 | imin = self._islider.GetMin() 22 | imax = self._islider.GetMax() 23 | if ival == imin: 24 | self._value = self._min 25 | elif ival == imax: 26 | self._value = self._max 27 | else: 28 | self._value = ival * self._res 29 | event.Skip() 30 | #print 'OnScroll: value=%f, ival=%d' % (self._value, ival) 31 | 32 | def GetValue(self): 33 | return self._value 34 | 35 | def GetMin(self): 36 | return self._min 37 | 38 | def GetMax(self): 39 | return self._max 40 | 41 | def GetRes(self): 42 | return self._res 43 | 44 | def SetValue(self, value): 45 | self._islider.SetValue(round(value/self._res)) 46 | self._value = value 47 | 48 | def SetMin(self, minval): 49 | self._islider.SetMin(round(minval/self._res)) 50 | self._min = minval 51 | 52 | def SetMax(self, maxval): 53 | self._islider.SetMax(round(maxval/self._res)) 54 | self._max = maxval 55 | 56 | def SetRes(self, res): 57 | self._islider.SetRange(round(self._min/res), round(self._max/res)) 58 | self._islider.SetValue(round(self._value/res)) 59 | self._res = res 60 | 61 | def SetRange(self, minval, maxval): 62 | self._islider.SetRange(round(minval/self._res), round(maxval/self._res)) 63 | self._min = minval 64 | self._max = maxval 65 | -------------------------------------------------------------------------------- /FloatSliderText.py: -------------------------------------------------------------------------------- 1 | import wx 2 | import FloatSlider 3 | 4 | class FloatSliderText(wx.Panel): 5 | def __init__(self, parent, id, caption, value, Min, Max, res): 6 | wx.Panel.__init__(self, parent, id) 7 | self.caption = caption 8 | self.min = Min 9 | self.max = Max 10 | self.value = value 11 | self.res = res 12 | self.build_gui() 13 | self.__bind_events_wx() 14 | self.Show() 15 | 16 | def build_gui(self): 17 | self.sliderctrl = FloatSlider.FloatSlider(self, -1, self.value, 18 | self.min, self.max, self.res) 19 | self.textbox = wx.TextCtrl(self, -1, "%.2f" % self.value, style=wx.TE_READONLY) 20 | 21 | sizer = wx.BoxSizer(wx.HORIZONTAL) 22 | sizer.Add(wx.StaticText(self, -1, self.caption) , 0, wx.EXPAND) 23 | sizer.Add(self.sliderctrl, 1, wx.EXPAND) 24 | sizer.Add(self.textbox, 0, wx.EXPAND) 25 | self.SetSizer(sizer) 26 | 27 | self.Layout() 28 | self.Update() 29 | self.SetAutoLayout(1) 30 | 31 | def __bind_events_wx(self): 32 | self.sliderctrl.Bind(wx.EVT_SCROLL, self.do_slider) 33 | self.Bind(wx.EVT_SIZE, self.onsize) 34 | 35 | def onsize(self, evt): 36 | evt.Skip() 37 | 38 | def do_slider(self, evt): 39 | self.value = self.sliderctrl.GetValue() 40 | self.textbox.SetValue("%.2f" % self.value) 41 | evt.Skip() 42 | 43 | def GetValue(self): 44 | return self.value 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Jairson Dinis 2 | # Copyright (C) 2018, 2019 Sven Fritzsche - Bundesanstalt fuer Materialforschung und -pruefung 3 | # 4 | # 5 | # 6 | # -*- coding: utf-8*- 7 | #This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | 17 | # 18 | # This program creates 3D-dimensional low surfaces structures and allows 19 | # the export into the .stl-format. 20 | # 21 | 22 | 23 | import sys 24 | import os 25 | import wx 26 | import h5py 27 | import numpy as np 28 | 29 | 30 | from vtkmodules.vtkCommonCore import vtkObject 31 | from vtkmodules.vtkFiltersSources import vtkConeSource, vtkSphereSource 32 | from vtkmodules.vtkRenderingAnnotation import vtkAxesActor 33 | from vtkmodules.vtkInteractionWidgets import vtkOrientationMarkerWidget 34 | from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera 35 | 36 | from vtkmodules.vtkRenderingCore import ( 37 | vtkActor, 38 | vtkPolyDataMapper, 39 | vtkDataSetMapper, 40 | vtkRenderer, 41 | vtkRenderWindow 42 | ) 43 | from vtkmodules.vtkFiltersCore import ( 44 | vtkMarchingCubes, vtkWindowedSincPolyDataFilter 45 | ) 46 | 47 | from vtkmodules.vtkIOGeometry import vtkSTLWriter 48 | 49 | from vtkmodules.vtkCommonDataModel import vtkImageData 50 | import vtkmodules.vtkRenderingOpenGL2 51 | 52 | 53 | 54 | 55 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor 56 | from vtk.util import numpy_support 57 | from pubsub import pub 58 | from numpy import cos, sin, ogrid, pi 59 | 60 | 61 | #from slider_text import SliderText 62 | from FloatSliderText import FloatSliderText 63 | 64 | 65 | 66 | def to_vtk(n_array, spacing, slice_number=0, orientation='AXIAL'): 67 | 68 | """ 69 | This function defines orientation or the TPMS. 70 | Starts vtk. 71 | """ 72 | 73 | 74 | try: 75 | dz, dy, dx = n_array.shape 76 | except ValueError: 77 | dy, dx = n_array.shape 78 | dz = 1 79 | 80 | v_image = numpy_support.numpy_to_vtk(n_array.flat) 81 | 82 | if orientation == 'AXIAL': 83 | extent = (0, dx -1, 0, dy -1, slice_number, slice_number + dz - 1) 84 | #elif orientation == 'SAGITAL': 85 | # dx, dy, dz = dz, dx, dy 86 | # extent = (slice_number, slice_number + dx - 1, 0, dy - 1, 0, dz - 1) 87 | #elif orientation == 'CORONAL': 88 | # dx, dy, dz = dx, dz, dy 89 | # extent = (0, dx - 1, slice_number, slice_number + dy - 1, 0, dz - 1) 90 | 91 | # Generating the vtkImageData 92 | image = vtkImageData() 93 | image.SetOrigin(0, 0, 0) 94 | image.SetSpacing(spacing) 95 | # image.SetNumberOfScalarComponents(1) 96 | image.SetDimensions(dx, dy, dz) 97 | image.SetExtent(extent) 98 | # image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype)) 99 | image.AllocateScalars(numpy_support.get_vtk_array_type(n_array.dtype), 1) 100 | image.GetPointData().SetScalars(v_image) 101 | # image.Update() 102 | # image.UpdateInformation() 103 | 104 | # image_copy = vtk.vtkImageData() 105 | # image_copy.DeepCopy(image) 106 | # image_copy.Update() 107 | 108 | return image 109 | 110 | 111 | def fun_schwarzP(type_surface, tam, spacing, hole_size): 112 | 113 | """ 114 | Definition of the different TPMS. 115 | """ 116 | 117 | 118 | 119 | tz, ty, tx = tam 120 | sx, sy, sz = spacing 121 | pos, neg = hole_size 122 | 123 | 124 | 125 | z, y, x = ogrid[-tx/2:tx/2:sx, -ty/2:ty/2:sy, -tz/2:tz/2:sz] 126 | 127 | #print type_surface 128 | if type_surface == 'Schwarz_P': 129 | f = cos(x) + cos(y) + cos(z) 130 | elif type_surface == 'Schwarz D': 131 | f = sin(x) * sin(y) * sin(z) + sin(x) * cos(y) * cos(z) \ 132 | + cos(x) * sin(y) * cos(z) + cos(x) * cos(y) * sin(z) 133 | elif type_surface == "Gyroid": 134 | f = cos(x) * sin(y) + cos(y) * sin(z) + cos(z) * sin(x) 135 | elif type_surface == "F-RD": 136 | cx = cos(2*x) 137 | cy = cos(2*y) 138 | cz = cos(2*z) 139 | f = 4 * cos(x) * cos(y) * cos(z) - (cx * cy + cx * cz + cy * cz) 140 | elif type_surface == "Neovius": 141 | f = 3*(cos(x) + cos(y) + cos(z)) + 4* cos(x) * cos(y) * cos(z) 142 | elif type_surface == "iWP": 143 | f = cos(x) * cos(y) + cos(y) * cos(z) + cos(z) * cos(x) \ 144 | - cos(x) * cos(y) * cos(z) 145 | elif type_surface == 'P_W_Hybrid': 146 | f = 4*(cos(x) * cos(y) + cos(y) * cos(z) + cos(z) * cos(x)) \ 147 | -3* cos(x) * cos(y) * cos(z) + 2.4 148 | elif type_surface == "L-Type": 149 | cxx = cos(2*x) 150 | cyy = cos(2*y) 151 | czz = cos(2*z) 152 | cx = cos(x) 153 | cy = cos(y) 154 | cz = cos(z) 155 | sx = sin(x) 156 | sy = sin(y) 157 | sz = sin(z) 158 | f = 0.5*(sin(2*x) * cy * sz + sin(2*y) * cz * sx \ 159 | + sin(2*z) * cx * sy) - \ 160 | 0.5 * (cxx * cyy + cyy * czz + czz * cxx) + 0.15 161 | elif type_surface == 'Skeletal 1': 162 | cx = cos(x) 163 | cy = cos(y) 164 | cz = cos(z) 165 | f = 10.0*(cx*cy + cy*cz + cz*cx) \ 166 | - 5.0*(cos(x*2) + cos(y*2) + cos(z*2)) - 14.0 167 | elif type_surface == 'Skeletal 2': 168 | cx = cos(4*x) 169 | cy = cos(4*y) 170 | cz = cos(4*z) 171 | xo = x - pi/4 172 | yo = y - pi/4 173 | zo = z - pi/4 174 | f = 10.0 * (sin(xo) * sin(yo) * sin(zo) + sin(xo) * cos(yo) * cos(zo) \ 175 | + cos(xo) * sin(yo) * cos(zo)+ cos(xo) * cos(yo) * sin(zo)) \ 176 | - 0.7*(cx + cy + cz) - 11.0 177 | elif type_surface == 'Tubular G': 178 | cx = cos(2*x) 179 | cy = cos(2*y) 180 | cz = cos(2*z) 181 | f = 10.0*(cos(x) * sin(y) + cos(y) * sin(z) + cos(z) * sin(x)) \ 182 | - 0.5*(cx*cy + cy*cz + cz*cx) - 14.0 183 | elif type_surface == 'Tubular P': 184 | cx = cos(x) 185 | cy = cos(y) 186 | cz = cos(z) 187 | f = 10.0*(cx + cy + cz) - 5.1*(cx*cy + cy*cz + cz*cx) - 14.6 188 | elif type_surface == "I2-Y": 189 | cx = cos(2*x) 190 | cy = cos(2*y) 191 | cz = cos(2*z) 192 | f = -2 * (sin(2*x) * cos(y) * sin(z) + sin(x) * sin(2*y) * cos(z) \ 193 | + cos(x) * sin(y) * sin(2*z)) + cx * cy + cy * cz + cx * cz 194 | elif type_surface == "G'": 195 | sx = sin(2*x) 196 | sy = sin(2*y) 197 | sz = sin(2*z) 198 | cxx = cos(2*x) 199 | cyy = cos(2*y) 200 | czz = cos(2*z) 201 | s4x = sin(4*x) 202 | s4y = sin(4*y) 203 | s4z = sin(4*z) 204 | c4x = cos(4*x) 205 | c4y = cos(4*y) 206 | c4z = cos(4*z) 207 | f = 0.8*(s4x * sz * cyy + s4y * sx * czz + s4z * sy * cxx) \ 208 | - 0.2 * (c4x * c4y + c4y * c4z + c4z * c4x) 209 | elif type_surface == "Double Diamond": 210 | sx = sin(2*x) 211 | sy = sin(2*y) 212 | sz = sin(2*y) 213 | cx = cos(2*x) 214 | cy = cos(2*y) 215 | cz = cos(2*z) 216 | f = sx * sy + sy * sz + sx * sz + cx * cy * cz- 0.35 217 | 218 | elif type_surface == "Double Gyroid": 219 | sx = sin(2*x) 220 | sy = sin(2*y) 221 | sz = sin(2*y) 222 | cx = cos(2*x) 223 | cy = cos(2*y) 224 | cz = cos(2*z) 225 | f = 2.75 * ( sx * sin(z) * cos(y) + sy * sin(x) * cos(z) + sz * sin(y) * cos(x)) - (cx * cy + cy * cz + cx * cz) - 0.35 226 | 227 | elif type_surface == "Fischer-Koch S": 228 | cx = cos(2*x) 229 | cy = cos(2*y) 230 | cz = cos(2*z) 231 | f = cx * sin(y) * cos(z) + cx * sin(z) * cos(x) + cz * sin(x) * cos(y) - 0.375 232 | 233 | elif type_surface == "Double Schwarz P": 234 | cx = cos(2*x) 235 | cy = cos(2*y) 236 | cz = cos(2*z) 237 | f = 0.5 * (cos(x) * cos(y) + cos(y) * cos(z) + cos(z) * cos(x)) + 0.2 * (cx + cy + cz) 238 | 239 | 240 | 241 | #M=numpy.array(f) 242 | M = np.array(((f > -neg) & (f < pos)) * 1.0) 243 | 244 | #print M.shape, (i+2 for i in M.shape) 245 | N = np.zeros([i+2 for i in M.shape]) 246 | N[1:-1, 1:-1, 1:-1] = M 247 | return N 248 | 249 | 250 | 251 | class LeftPanel(wx.Panel): 252 | 253 | """ 254 | Builds the left panel - with the sliders and options 255 | """ 256 | 257 | 258 | def __init__(self, parent, id, style): 259 | wx.Panel.__init__(self, parent, id, style=style) 260 | self.build_gui() 261 | self.__bind_events_wx() 262 | self.__bind_events_pb() 263 | 264 | self.Show() 265 | 266 | # Error description 267 | #log_path = os.path.join('.', 'vtkoutput.txt') 268 | #fow = vtkFileOutputWindow() 269 | #fow.SetFileName(log_path) 270 | #ow = vtkOutputWindow() 271 | #ow.SetInstance(fow) 272 | #----------------------- 273 | 274 | 275 | def build_gui(self): 276 | 277 | """ 278 | Defines the GUI and sliders with wx 279 | """ 280 | 281 | self.choose_scaffold = wx.ComboBox(self, -1, "Schwarz_P",\ 282 | choices=( 283 | "Schwarz_P",\ 284 | "Schwarz D",\ 285 | "Gyroid",\ 286 | "F-RD",\ 287 | "Neovius",\ 288 | "iWP",\ 289 | 'P_W_Hybrid',\ 290 | "L-Type",\ 291 | 'Skeletal 1',\ 292 | 'Skeletal 2',\ 293 | 'Tubular G',\ 294 | 'Tubular P',\ 295 | "I2-Y",\ 296 | "G'",\ 297 | "Double Gyroid",\ 298 | "Double Schwarz P",\ 299 | "Double Diamond",\ 300 | "Fischer-Koch S" 301 | ), 302 | style=wx.CB_READONLY) 303 | 304 | 305 | 306 | 307 | self.Reset_scaffold = wx.Button(self, -1, "Rendering") 308 | 309 | self.porosity_value_x = wx.SpinCtrl(self, -1, '') 310 | self.porosity_value_x.SetRange(1, 20) 311 | self.porosity_value_x.SetValue(1) 312 | 313 | self.porosity_value_y = wx.SpinCtrl(self, -1, '') 314 | self.porosity_value_y.SetRange(1, 20) 315 | self.porosity_value_y.SetValue(1) 316 | 317 | self.porosity_value_z = wx.SpinCtrl(self, -1, '') 318 | self.porosity_value_z.SetRange(1, 20) 319 | self.porosity_value_z.SetValue(1) 320 | 321 | self.spacing_value_x = FloatSliderText(self, -1, '', 0.2, 0.02, 0.5, 0.02) 322 | self.hole_dimension_value1 = FloatSliderText(self, -1, 'Positive Direction', 0.3, 0.1, 3.14, 0.1) 323 | self.hole_dimension_value2 = FloatSliderText(self, -1, 'Negative Direction', 0.3, 0.1, 3.14, 0.1) 324 | 325 | self.v_porosity = wx.StaticText(self, -1, "") 326 | self.Lx = wx.StaticText(self, -1, "") 327 | self.Ly = wx.StaticText(self, -1, "") 328 | self.Lz = wx.StaticText(self, -1, "") 329 | 330 | 331 | b_sizer = wx.BoxSizer(wx.VERTICAL) 332 | 333 | 334 | b_sizer.Add(wx.StaticText(self, -1, "Type of Minimal Surface"), 0, wx.CENTRE | wx.ALL, 10) 335 | b_sizer.Add(self.choose_scaffold, 0, wx.CENTRE) 336 | b_sizer.Add(wx.StaticText(self, -1, "Number of primitive cells in X-direction"), 0, wx.CENTRE | wx.ALL, 10) 337 | b_sizer.Add(self.porosity_value_x, 0, wx.CENTRE) 338 | b_sizer.Add(wx.StaticText(self, -1, "Number of primitive cells in Y-direction"), 0, wx.CENTRE | wx.ALL, 10) 339 | b_sizer.Add(self.porosity_value_y, 0, wx.CENTRE) 340 | b_sizer.Add(wx.StaticText(self, -1, "Number of primitive cells in Z-direction"), 0, wx.CENTRE | wx.ALL, 10) 341 | b_sizer.Add(self.porosity_value_z, 0, wx.CENTRE) 342 | b_sizer.Add(wx.StaticText(self, -1, "Quality factor (lower is better)"), 0, wx.CENTRE | wx.ALL, 10) 343 | b_sizer.Add(self.spacing_value_x, 0, wx.EXPAND) 344 | b_sizer.Add(wx.StaticText(self, -1, "Wall Thickness"), 0, wx.CENTRE | wx.ALL, 10) 345 | b_sizer.Add(self.hole_dimension_value1, 0, wx.EXPAND) 346 | b_sizer.Add(self.hole_dimension_value2, 0, wx.EXPAND) 347 | b_sizer.Add(self.Reset_scaffold, 0) 348 | 349 | 350 | 351 | 352 | b_sizer.Add(self.v_porosity, 0, wx.EXPAND) 353 | b_sizer.Add(self.Lx, 0, wx.EXPAND) 354 | b_sizer.Add(self.Ly, 0, wx.EXPAND) 355 | b_sizer.Add(self.Lz, 0, wx.EXPAND) 356 | 357 | 358 | 359 | hbox = wx.BoxSizer(wx.VERTICAL) 360 | hbox.Add(b_sizer, 1, wx.EXPAND) 361 | 362 | 363 | self.SetSizer(hbox) 364 | 365 | def __bind_events_wx(self): 366 | self.Reset_scaffold.Bind(wx.EVT_BUTTON, self.renderer) 367 | 368 | 369 | 370 | def __bind_events_pb(self): 371 | pub.subscribe(self._show_info, 'show info') 372 | 373 | def renderer(self, evt): 374 | tipo = self.choose_scaffold.GetValue() 375 | X = 2 * pi * self.porosity_value_x.GetValue() 376 | Y = 2 * pi * self.porosity_value_y.GetValue() 377 | Z = 2 * pi * self.porosity_value_z.GetValue() 378 | sX = self.spacing_value_x.GetValue() 379 | sY = self.spacing_value_x.GetValue() 380 | sZ = self.spacing_value_x.GetValue() 381 | pos = self.hole_dimension_value1.GetValue() 382 | neg = self.hole_dimension_value2.GetValue() 383 | 384 | 385 | tam = X, Y, Z 386 | spacing = sX, sY, sZ 387 | hole_size = neg, pos 388 | 389 | 390 | pub.sendMessage('Recalculating surface', msg=(tipo, tam, spacing, hole_size)) 391 | pub.sendMessage('Calculating porosity') 392 | 393 | def _show_info(self, msg3): 394 | p, lx, ly, lz = msg3 395 | self.v_porosity.SetLabel('Porosity: %.2f %%' % p) 396 | self.Lx.SetLabel('Length X-direction: %.2f units' % lx) 397 | self.Ly.SetLabel('Length Y-direction: %.2f units' % ly) 398 | self.Lz.SetLabel('Length Z-direction: %.2f units' % lz) 399 | 400 | 401 | 402 | 403 | class PanelRight(wx.Panel): 404 | 405 | """ 406 | Builds the image panel that shows the TPMS 407 | """ 408 | 409 | def __init__(self, parent, id, style): 410 | wx.Panel.__init__(self, parent, id, style=style) 411 | 412 | self.frontview = FrontView(self, id=-1, style=wx.BORDER_SUNKEN) 413 | 414 | 415 | 416 | vbox = wx.BoxSizer(wx.VERTICAL) 417 | vbox.Add(self.frontview, 1, wx.EXPAND) 418 | #vbox.Add(self.visaotop, 1, wx.EXPAND) 419 | 420 | 421 | 422 | hbox = wx.BoxSizer() 423 | hbox.Add(vbox, 1, wx.EXPAND) 424 | 425 | self.SetSizer(hbox) 426 | 427 | 428 | 429 | class FrontView(wx.Panel): 430 | def __init__(self, parent, id, style): 431 | wx.Panel.__init__(self, parent, id, style=style) 432 | 433 | self.renderer = vtkRenderer() 434 | self.Interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) 435 | self.Interactor.GetRenderWindow().AddRenderer(self.renderer) 436 | self.Interactor.Render() 437 | 438 | 439 | 440 | istyle = vtkInteractorStyleTrackballCamera() 441 | 442 | self.Interactor.SetInteractorStyle(istyle) 443 | 444 | 445 | 446 | hbox = wx.BoxSizer(wx.VERTICAL) 447 | hbox.Add(wx.StaticText(self, -1, 'Global Structure of Scaffold')) 448 | 449 | 450 | hbox.Add(self.Interactor, 1, wx.EXPAND) 451 | self.SetSizer(hbox) 452 | 453 | 454 | self.init_actor() 455 | self.add_axes() 456 | self.draw_surface() 457 | self.renderer.ResetCamera() 458 | 459 | pub.subscribe(self._draw_surface, 'Recalculating surface') 460 | pub.subscribe(self._calculate_porosity, 'Calculating porosity') 461 | 462 | def init_actor(self): 463 | self.mapper = vtkPolyDataMapper() 464 | 465 | 466 | self.SurfaceActor = vtkActor() 467 | self.SurfaceActor.SetMapper(self.mapper) 468 | 469 | self.renderer.AddActor(self.SurfaceActor) 470 | 471 | self.renderer.ResetCamera() 472 | 473 | self.Interactor.Render() 474 | 475 | 476 | 477 | def _draw_surface(self, msg): 478 | tipo, tam, spacing, hole_size = msg 479 | 480 | self.draw_surface(tipo, tam, spacing, hole_size) 481 | 482 | 483 | def draw_surface(self, tipo='Schwarz_P', tam=None, 484 | spacing=None, hole_size=None): 485 | if tam is None: 486 | tam = 2*pi, 2*pi, 2*pi 487 | if spacing is None: 488 | spacing = 0.1, 0.1, 0.1 489 | if hole_size is None: 490 | hole_size = 0.3, 0.3 491 | #print hole_size 492 | 493 | M = fun_schwarzP(tipo, tam, spacing, hole_size) 494 | 495 | f = h5py.File("1.hdf5", "w") 496 | f['data'] = M 497 | f['spacing'] = np.array(spacing) 498 | 499 | self.M = M 500 | self.spacing = spacing 501 | 502 | 503 | image = to_vtk(M, spacing) 504 | 505 | surf = vtkMarchingCubes() 506 | surf.SetInputData(image) 507 | #surf.SetValue(0,0.5) 508 | surf.SetValue(0, 0.1) 509 | surf.ComputeNormalsOn() 510 | surf.ComputeGradientsOn() 511 | surf.Update() 512 | 513 | subdiv = vtkWindowedSincPolyDataFilter() 514 | subdiv.SetInputData(surf.GetOutput()) 515 | subdiv.SetNumberOfIterations(100) 516 | subdiv.SetFeatureAngle(120) 517 | subdiv.SetBoundarySmoothing(60) 518 | subdiv.BoundarySmoothingOn() 519 | subdiv.SetEdgeAngle(90) 520 | subdiv.Update() 521 | 522 | ## subdiv= vtk.vtkLoopSubdivisionFilter() 523 | ## subdiv.SetInput(surf.GetOutput()) 524 | ## subdiv.Update() 525 | 526 | #self.mapper.SetInput(surf.GetOutput()) 527 | 528 | self.mapper.SetInputData(subdiv.GetOutput()) 529 | self.Interactor.Render() 530 | 531 | def _calculate_porosity(self): 532 | p = self.calculate_porosity() 533 | lx, ly, lz = self.measure_distance() 534 | 535 | pub.sendMessage('show info', msg3=(p, lx, ly, lz)) 536 | 537 | 538 | 539 | 540 | 541 | def calculate_porosity(self): 542 | M = self.M 543 | sx, sy, sz = self.spacing 544 | z, y, x = M.shape 545 | 546 | v_total = x*sx * y*sy * z*sz 547 | #v_walls = ((M > -0.1) & (M < 0.1)).sum() * sx*sy*sz 548 | v_walls = M.sum() * sx*sy*sz 549 | 550 | vporos = v_total - v_walls 551 | 552 | return 100.0*vporos/v_total 553 | 554 | 555 | 556 | def measure_distance(self): 557 | M = self.M 558 | sx, sy, sz = self.spacing 559 | z, y, x = M.shape 560 | 561 | Lx = x*sx 562 | Ly = y*sy 563 | Lz = z*sz 564 | return Lz, Ly, Lx 565 | 566 | 567 | 568 | 569 | def add_axes(self): 570 | axes = vtkAxesActor() 571 | self.marker = vtkOrientationMarkerWidget() 572 | self.marker.SetInteractor(self.Interactor) 573 | self.marker.SetOrientationMarker(axes) 574 | self.marker.SetViewport(0.75, 0, 1, 0.25) 575 | self.marker.SetEnabled(1) 576 | 577 | 578 | 579 | def write_model_stl(self, path): 580 | write = vtkSTLWriter() 581 | write.SetInputData(self.mapper.GetInput()) 582 | write.SetFileTypeToBinary() 583 | write.SetFileName(path) 584 | write.Write() 585 | write.Update() 586 | 587 | 588 | 589 | 590 | class MainWindow(wx.Frame): 591 | def __init__(self, parent, id, title): 592 | wx.Frame.__init__(self, parent, id, title, size=(700, 650)) 593 | 594 | #---------------------------------- 595 | 596 | panel = wx.Panel(self, -1) 597 | 598 | self.currentDirectory = os.getcwd() 599 | 600 | self.RightPanel = PanelRight(self, id=-1, style=wx.BORDER_SUNKEN) 601 | self.LeftPanel = LeftPanel(self, id=-1, style=wx.BORDER_SUNKEN) 602 | 603 | 604 | hbox = wx.BoxSizer() 605 | hbox.Add(self.RightPanel, 1, wx.EXPAND) 606 | hbox.Add(self.LeftPanel, 1, wx.EXPAND) 607 | 608 | 609 | 610 | self.SetSizer(hbox) 611 | 612 | #criar menu 613 | 614 | MenuBar = wx.MenuBar() 615 | menu = wx.Menu() 616 | 617 | 618 | save = menu.Append(-1, "&Save ") 619 | close = menu.Append(-1, "&Exit") 620 | MenuBar.Append(menu, "File") 621 | 622 | self.SetMenuBar(MenuBar) 623 | 624 | # tratar os eventos 625 | self.Bind(wx.EVT_MENU, self.close_program, close) 626 | self.Bind(wx.EVT_MENU, self.save_model_stl, save) 627 | 628 | 629 | self.Show() 630 | 631 | 632 | def close_program(self, event): 633 | dial = wx.MessageDialog(None, 'Do you really want to close this program?', 'Question', wx.YES_NO |wx.NO_DEFAULT | wx.ICON_QUESTION) 634 | ret = dial.ShowModal() 635 | if ret == wx.ID_YES: 636 | self.Destroy() 637 | 638 | 639 | 640 | def save_model_stl(self, evt): 641 | wildcard = "(*.stl)|*.stl" 642 | dlg = wx.FileDialog( 643 | self, message="Save file as ...", 644 | defaultDir=self.currentDirectory, 645 | defaultFile="", wildcard=wildcard, style=wx.FD_SAVE 646 | ) 647 | if dlg.ShowModal() == wx.ID_OK: 648 | path = dlg.GetPath() 649 | self.RightPanel.frontview.write_model_stl(path) 650 | dlg.Destroy() 651 | 652 | 653 | 654 | 655 | if __name__ == '__main__': 656 | App = wx.App(0) 657 | W = MainWindow(None, -1, 'Interface Scaffold ') 658 | W.Show() 659 | App.MainLoop() 660 | -------------------------------------------------------------------------------- /app.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['C:/Users/sfritzsc/AppData/Local/Programs/Python/Python37/Scripts/Source Code/app.py'], 7 | pathex=['C:\\Users\\sfritzsc'], 8 | binaries=[], 9 | datas=[], 10 | hiddenimports=['vtkmodules', 'vtkmodules.all', 'vtkmodules.util', 'vtk.wx', 'vtkmodules.util.numpy_support'], 11 | hookspath=[], 12 | runtime_hooks=[], 13 | excludes=[], 14 | win_no_prefer_redirects=False, 15 | win_private_assemblies=False, 16 | cipher=block_cipher, 17 | noarchive=False) 18 | pyz = PYZ(a.pure, a.zipped_data, 19 | cipher=block_cipher) 20 | exe = EXE(pyz, 21 | a.scripts, 22 | [], 23 | exclude_binaries=True, 24 | name='app', 25 | debug=False, 26 | bootloader_ignore_signals=False, 27 | strip=False, 28 | upx=True, 29 | console=True ) 30 | coll = COLLECT(exe, 31 | a.binaries, 32 | a.zipfiles, 33 | a.datas, 34 | strip=False, 35 | upx=True, 36 | upx_exclude=[], 37 | name='app') 38 | -------------------------------------------------------------------------------- /images/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/F-RD.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/F-RD.PNG -------------------------------------------------------------------------------- /images/G_prime.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/G_prime.PNG -------------------------------------------------------------------------------- /images/Gyroid.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Gyroid.PNG -------------------------------------------------------------------------------- /images/I2-Y.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/I2-Y.PNG -------------------------------------------------------------------------------- /images/L-Type.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/L-Type.PNG -------------------------------------------------------------------------------- /images/Neovius.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Neovius.PNG -------------------------------------------------------------------------------- /images/P_W_Hybrid.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/P_W_Hybrid.PNG -------------------------------------------------------------------------------- /images/SchwarzD.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/SchwarzD.PNG -------------------------------------------------------------------------------- /images/SchwarzP.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/SchwarzP.PNG -------------------------------------------------------------------------------- /images/Skeletal_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Skeletal_1.png -------------------------------------------------------------------------------- /images/Skeletal_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Skeletal_2.png -------------------------------------------------------------------------------- /images/Tubular_G.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Tubular_G.png -------------------------------------------------------------------------------- /images/Tubular_P.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/Tubular_P.png -------------------------------------------------------------------------------- /images/iWP.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAMresearch/ScaffoldStructures/649880ba57975910163b8cb2e950ecddfdf9ed50/images/iWP.PNG -------------------------------------------------------------------------------- /pyinstaller.txt: -------------------------------------------------------------------------------- 1 | pyinstaller -y --hidden-import vtkmodules --hidden-import vtkmodules.all --hidden-import vtkmodules.util --hidden-import vtk.wx --hidden-import vtkmodules.util.numpy_support ".\app.py" -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![DOI](https://zenodo.org/badge/225376252.svg)](https://zenodo.org/badge/latestdoi/225376252) 2 | 3 | 4 | 5 | ## What it does: 6 | You can generate minimal surface structures in any desired dimension and save them as .stl-file. An overview of the possible scaffolds/structures follows below. 7 | 8 | ## How to run? 9 | 10 | The software was tested with Python 3.7 under Ubuntu 19.04. 11 | 12 | Or open the folder in Terminal and run it with: `python app.py` 13 | Needed packages are: 14 | - numpy 15 | - vtk 16 | - pypubsub 17 | - wx 18 | 19 | ## Explanation of the sliders: 20 | - **Number of primitive cells in X,Y,Z direction** - changes the number of unit cells in each direction 21 | 22 | - **Quality factor** - changes the surface quality of the structure, lower values took longer to render 23 | 24 | - **Wall thickness** - changes the thickness of the wall seen from the surface of the minimal surface structure 25 | 26 | 27 | 28 | 29 | ## Overview of the different possible scaffolds 30 | 31 | Currently the following triply periodic minimal surfaces are available: 32 | 33 | - Schwarz P ![SchwarzP](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/SchwarzP.PNG) 34 | - Schwarz D ![SchwarzD](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/SchwarzD.PNG) 35 | - Gyroid ![F-RD](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Gyroid.PNG) 36 | - F-RD ![F-RD](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/F-RD.PNG) 37 | - Neovius ![Neovius](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Neovius.PNG) 38 | - iWP ![iWP](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/iWP.PNG) 39 | - L-Type ![L-Type](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/L-Type.PNG) 40 | - G' ![G'](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/G_prime.PNG) 41 | - Tubular G ![Tubular G](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Tubular_G.png) 42 | - Tubular P ![Tubular P](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Tubular_P.png) 43 | - P_W_Hybride ![P_W_Hybride](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/P_W_Hybrid.PNG) 44 | - I2-Y ![I2-Y](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/I2-Y.PNG) 45 | - Skeletal 1 ![Skeletal 1](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Skeletal_1.png) 46 | - Skeletal 2 ![Skelatal 2](https://github.com/BAMresearch/ScaffoldStructures/blob/master/images/Skeletal_2.png) 47 | 48 | 49 | ## License 50 | 51 | This program was originally developed by J.C. Dinis at Centre for Rapid and Sustainable Product Development, Polytechnic Institute of Leiria, Marinha Grande, Portugal and the Renato Archer Information Technology Center – CTI, Campinas, São Paulo, Brazil. 52 | 53 | It is open-source and available under GNU GPLv2.0. 54 | 55 | Link to the original paper: 56 | https://doi.org/10.1016/j.protcy.2014.10.176 57 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | This app was originally developed by J.C. Dinis at Centre for Rapid and Sustainable Product Development, Polytechnic Institute of Leiria, Marinha Grande, Portugal. 2 | 3 | 4 | 5 | The software is open-source and available under GPLv2.0. 6 | The software was tested with Python 3.5 under Ubuntu 18.04. 7 | 8 | Executables: 9 | 10 | Windows: https://sjtdelfs.de/nextcloud/index.php/s/ro5Fnwp6CWq6J7e 11 | 12 | Or open the folder in Terminal and run it with: python3 app.py 13 | Needed packages are: 14 | - numpy 15 | - vtk 16 | - pypubsub 17 | - wx_py 18 | 19 | 20 | 21 | Link to the original paper: 22 | https://ac.els-cdn.com/S2212017314004034/1-s2.0-S2212017314004034-main.pdf?_tid=be3427dc-0613-46a7-a352-bddb6eeef126&acdnat=1522688132_85126d4288a6ddb69cc3cc2a5711e428 23 | --------------------------------------------------------------------------------