SVG2mesh 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
svg2mesh-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SVG2mesh developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: SVG2mesh
3
+ Version: 1.0.0
4
+ Summary: FE mesh generator
5
+ Author-email: Vladimír Lukeš <vlukes@fav.zcu.cz>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 SVG2mesh developers
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: homepage, https://github.com/sfepy/svg2mesh
29
+ Keywords: SVG,FE mesh,generator
30
+ Classifier: Development Status :: 4 - Beta
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: Topic :: Scientific/Engineering
33
+ Classifier: Programming Language :: Python
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Requires-Python: >=3.8
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: numpy
39
+ Requires-Dist: gmsh
40
+ Requires-Dist: meshio
41
+ Requires-Dist: svgelements
42
+ Dynamic: license-file
43
+
44
+ # SVG2mesh
45
+
46
+ Generate finite element meshes from SVG-defined geometry.
47
+
48
+ SVG2mesh converts 2D geometries stored in SVG files into finite element meshes
49
+ using Gmsh. The generated mesh can be exported to formats supported by MeshIO
50
+ and used in finite element analysis and scientific computing workflows.
51
+
52
+ ## Features
53
+
54
+ - Create FE meshes directly from SVG geometry
55
+ - Mesh generation powered by Gmsh
56
+ - Export meshes through MeshIO
57
+ - Optional generation of periodic meshes
58
+ - Export geometry preview as PNG
59
+
60
+ ## Installation
61
+
62
+ ### Requirements
63
+
64
+ - Python 3.8+
65
+ - NumPy
66
+ - Gmsh
67
+ - MeshIO
68
+ - svgelements
69
+
70
+ ### Install from Source
71
+
72
+ ```bash
73
+ git clone https://github.com/sfepy/svg2mesh.git
74
+ cd svg2mesh
75
+ pip install .
76
+ ```
77
+
78
+ ### Install form PyPI
79
+
80
+ ```bash
81
+ pip install svg2mesh
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### Basic Usage
87
+
88
+ ```bash
89
+ svg2mesh geometry.svg
90
+ ```
91
+
92
+ ### Specify Output File
93
+
94
+ ```bash
95
+ svg2mesh geometry.svg -o mesh.vtk
96
+ ```
97
+
98
+ ### Generate Unit Cell Mesh
99
+
100
+ ```bash
101
+ svg2mesh geometry.svg -u
102
+ ```
103
+
104
+ ### Generate Periodic Mesh
105
+
106
+ ```bash
107
+ svg2mesh geometry.svg -p
108
+ ```
109
+
110
+ ### Control Mesh Size
111
+
112
+ ```bash
113
+ svg2mesh geometry.svg -s 0.05
114
+ ```
115
+
116
+ ### Export PNG Preview
117
+
118
+ ```bash
119
+ svg2mesh geometry.svg -e
120
+ ```
121
+
122
+ ## Input Geometry
123
+
124
+ The input geometry must be provided as a **plain** SVG file.
125
+
126
+ Each physical (material) group must be defined in a separate SVG group labeled `layer1`, `layer2`, `layer3`, and so on. These layers are used to identify individual material regions in the generated mesh.
127
+
128
+ All SVG objects should be defined as paths.
129
+
130
+
131
+ ## Examples
132
+
133
+ ### Mesh1 -- multimaterial part
134
+
135
+ ```bash
136
+ svg2mesh mesh1.svg -s 10
137
+ ```
138
+ ![mesh1.png](svg2mesh/examples/mesh1.png)
139
+
140
+ ### Mesh2 -- single material part
141
+
142
+ ```bash
143
+ svg2mesh mesh2.svg -s 5
144
+ ```
145
+ ![mesh2.png](svg2mesh/examples/mesh2.png)
146
+
147
+ ### Mesh3 -- periodic unit cell
148
+
149
+ ```bash
150
+ svg2mesh mesh3.svg -u -s "[10, 2]"
151
+ ```
152
+ ![mesh3.png](svg2mesh/examples/mesh3.png)
153
+
154
+ ## License
155
+
156
+ SVG2mesh is distributed under the MIT License.
157
+
158
+ See the `LICENSE` file for details.
@@ -0,0 +1,115 @@
1
+ # SVG2mesh
2
+
3
+ Generate finite element meshes from SVG-defined geometry.
4
+
5
+ SVG2mesh converts 2D geometries stored in SVG files into finite element meshes
6
+ using Gmsh. The generated mesh can be exported to formats supported by MeshIO
7
+ and used in finite element analysis and scientific computing workflows.
8
+
9
+ ## Features
10
+
11
+ - Create FE meshes directly from SVG geometry
12
+ - Mesh generation powered by Gmsh
13
+ - Export meshes through MeshIO
14
+ - Optional generation of periodic meshes
15
+ - Export geometry preview as PNG
16
+
17
+ ## Installation
18
+
19
+ ### Requirements
20
+
21
+ - Python 3.8+
22
+ - NumPy
23
+ - Gmsh
24
+ - MeshIO
25
+ - svgelements
26
+
27
+ ### Install from Source
28
+
29
+ ```bash
30
+ git clone https://github.com/sfepy/svg2mesh.git
31
+ cd svg2mesh
32
+ pip install .
33
+ ```
34
+
35
+ ### Install form PyPI
36
+
37
+ ```bash
38
+ pip install svg2mesh
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### Basic Usage
44
+
45
+ ```bash
46
+ svg2mesh geometry.svg
47
+ ```
48
+
49
+ ### Specify Output File
50
+
51
+ ```bash
52
+ svg2mesh geometry.svg -o mesh.vtk
53
+ ```
54
+
55
+ ### Generate Unit Cell Mesh
56
+
57
+ ```bash
58
+ svg2mesh geometry.svg -u
59
+ ```
60
+
61
+ ### Generate Periodic Mesh
62
+
63
+ ```bash
64
+ svg2mesh geometry.svg -p
65
+ ```
66
+
67
+ ### Control Mesh Size
68
+
69
+ ```bash
70
+ svg2mesh geometry.svg -s 0.05
71
+ ```
72
+
73
+ ### Export PNG Preview
74
+
75
+ ```bash
76
+ svg2mesh geometry.svg -e
77
+ ```
78
+
79
+ ## Input Geometry
80
+
81
+ The input geometry must be provided as a **plain** SVG file.
82
+
83
+ Each physical (material) group must be defined in a separate SVG group labeled `layer1`, `layer2`, `layer3`, and so on. These layers are used to identify individual material regions in the generated mesh.
84
+
85
+ All SVG objects should be defined as paths.
86
+
87
+
88
+ ## Examples
89
+
90
+ ### Mesh1 -- multimaterial part
91
+
92
+ ```bash
93
+ svg2mesh mesh1.svg -s 10
94
+ ```
95
+ ![mesh1.png](svg2mesh/examples/mesh1.png)
96
+
97
+ ### Mesh2 -- single material part
98
+
99
+ ```bash
100
+ svg2mesh mesh2.svg -s 5
101
+ ```
102
+ ![mesh2.png](svg2mesh/examples/mesh2.png)
103
+
104
+ ### Mesh3 -- periodic unit cell
105
+
106
+ ```bash
107
+ svg2mesh mesh3.svg -u -s "[10, 2]"
108
+ ```
109
+ ![mesh3.png](svg2mesh/examples/mesh3.png)
110
+
111
+ ## License
112
+
113
+ SVG2mesh is distributed under the MIT License.
114
+
115
+ See the `LICENSE` file for details.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: SVG2mesh
3
+ Version: 1.0.0
4
+ Summary: FE mesh generator
5
+ Author-email: Vladimír Lukeš <vlukes@fav.zcu.cz>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 SVG2mesh developers
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: homepage, https://github.com/sfepy/svg2mesh
29
+ Keywords: SVG,FE mesh,generator
30
+ Classifier: Development Status :: 4 - Beta
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: Topic :: Scientific/Engineering
33
+ Classifier: Programming Language :: Python
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Requires-Python: >=3.8
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: numpy
39
+ Requires-Dist: gmsh
40
+ Requires-Dist: meshio
41
+ Requires-Dist: svgelements
42
+ Dynamic: license-file
43
+
44
+ # SVG2mesh
45
+
46
+ Generate finite element meshes from SVG-defined geometry.
47
+
48
+ SVG2mesh converts 2D geometries stored in SVG files into finite element meshes
49
+ using Gmsh. The generated mesh can be exported to formats supported by MeshIO
50
+ and used in finite element analysis and scientific computing workflows.
51
+
52
+ ## Features
53
+
54
+ - Create FE meshes directly from SVG geometry
55
+ - Mesh generation powered by Gmsh
56
+ - Export meshes through MeshIO
57
+ - Optional generation of periodic meshes
58
+ - Export geometry preview as PNG
59
+
60
+ ## Installation
61
+
62
+ ### Requirements
63
+
64
+ - Python 3.8+
65
+ - NumPy
66
+ - Gmsh
67
+ - MeshIO
68
+ - svgelements
69
+
70
+ ### Install from Source
71
+
72
+ ```bash
73
+ git clone https://github.com/sfepy/svg2mesh.git
74
+ cd svg2mesh
75
+ pip install .
76
+ ```
77
+
78
+ ### Install form PyPI
79
+
80
+ ```bash
81
+ pip install svg2mesh
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### Basic Usage
87
+
88
+ ```bash
89
+ svg2mesh geometry.svg
90
+ ```
91
+
92
+ ### Specify Output File
93
+
94
+ ```bash
95
+ svg2mesh geometry.svg -o mesh.vtk
96
+ ```
97
+
98
+ ### Generate Unit Cell Mesh
99
+
100
+ ```bash
101
+ svg2mesh geometry.svg -u
102
+ ```
103
+
104
+ ### Generate Periodic Mesh
105
+
106
+ ```bash
107
+ svg2mesh geometry.svg -p
108
+ ```
109
+
110
+ ### Control Mesh Size
111
+
112
+ ```bash
113
+ svg2mesh geometry.svg -s 0.05
114
+ ```
115
+
116
+ ### Export PNG Preview
117
+
118
+ ```bash
119
+ svg2mesh geometry.svg -e
120
+ ```
121
+
122
+ ## Input Geometry
123
+
124
+ The input geometry must be provided as a **plain** SVG file.
125
+
126
+ Each physical (material) group must be defined in a separate SVG group labeled `layer1`, `layer2`, `layer3`, and so on. These layers are used to identify individual material regions in the generated mesh.
127
+
128
+ All SVG objects should be defined as paths.
129
+
130
+
131
+ ## Examples
132
+
133
+ ### Mesh1 -- multimaterial part
134
+
135
+ ```bash
136
+ svg2mesh mesh1.svg -s 10
137
+ ```
138
+ ![mesh1.png](svg2mesh/examples/mesh1.png)
139
+
140
+ ### Mesh2 -- single material part
141
+
142
+ ```bash
143
+ svg2mesh mesh2.svg -s 5
144
+ ```
145
+ ![mesh2.png](svg2mesh/examples/mesh2.png)
146
+
147
+ ### Mesh3 -- periodic unit cell
148
+
149
+ ```bash
150
+ svg2mesh mesh3.svg -u -s "[10, 2]"
151
+ ```
152
+ ![mesh3.png](svg2mesh/examples/mesh3.png)
153
+
154
+ ## License
155
+
156
+ SVG2mesh is distributed under the MIT License.
157
+
158
+ See the `LICENSE` file for details.
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ SVG2mesh.egg-info/PKG-INFO
5
+ SVG2mesh.egg-info/SOURCES.txt
6
+ SVG2mesh.egg-info/dependency_links.txt
7
+ SVG2mesh.egg-info/entry_points.txt
8
+ SVG2mesh.egg-info/requires.txt
9
+ SVG2mesh.egg-info/top_level.txt
10
+ svg2mesh/svg2mesh.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ svg2mesh = svg2mesh.svg2mesh:main
@@ -0,0 +1,4 @@
1
+ numpy
2
+ gmsh
3
+ meshio
4
+ svgelements
@@ -0,0 +1 @@
1
+ svg2mesh
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project.urls]
6
+ homepage = "https://github.com/sfepy/svg2mesh"
7
+
8
+ [project.scripts]
9
+ svg2mesh = "svg2mesh.svg2mesh:main"
10
+
11
+ [project]
12
+ name = "SVG2mesh"
13
+ version = "1.0.0"
14
+ readme = "README.md"
15
+ description="FE mesh generator "
16
+ license = {file = "LICENSE"}
17
+ authors = [{name = "Vladimír Lukeš", email = "vlukes@fav.zcu.cz"}]
18
+ requires-python = ">=3.8"
19
+ dependencies = ["numpy", "gmsh", "meshio", "svgelements"]
20
+ keywords = ["SVG", "FE mesh", "generator"]
21
+ classifiers = [
22
+ "Development Status :: 4 - Beta", # 3 - Alpha, 4 - Beta, 5 - Stable
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Scientific/Engineering",
25
+ "Programming Language :: Python",
26
+ "License :: OSI Approved :: MIT License",
27
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,373 @@
1
+ import sys
2
+ import os
3
+ import argparse
4
+ from ast import literal_eval
5
+ import numpy as nm
6
+ import gmsh
7
+ import meshio
8
+ from svgelements import SVG
9
+
10
+
11
+ def set_periodic(model, dx, dy, dz=0, dim=2):
12
+ """
13
+ Set mesh periodic.
14
+
15
+ Inputs
16
+ ======
17
+ model: gmsh.model
18
+ GMSH model
19
+ dx, dy, dz: floats
20
+ Domain dimensions
21
+ """
22
+ def set_periodic(master, slave, dx, dy, dz):
23
+ for m, s in zip(master, slave):
24
+ gmsh.model.mesh.setPeriodic(
25
+ dim - 1, [s], [m],
26
+ [1, 0, 0, dx,
27
+ 0, 1, 0, dy,
28
+ 0, 0, 1, dz,
29
+ 0, 0, 0, 1]
30
+ )
31
+
32
+ tol = 1e-6
33
+ bnd = nm.array([(tag,) + tuple(gmsh.model.occ.getCenterOfMass(d, tag))
34
+ for d, tag in model.getEntities(dim=dim-1)])
35
+
36
+ for k in range(dim):
37
+ cmin, cmax = nm.min(bnd[:, k + 1]), nm.max(bnd[:, k + 1])
38
+ midxs = nm.where(nm.abs(bnd[:, k + 1] - cmin) < tol)[0]
39
+ sidxs = nm.where(nm.abs(bnd[:, k + 1] - cmax) < tol)[0]
40
+
41
+ dv = nm.array([0] * 3)
42
+ dd = cmax - cmin
43
+ dv[k] = dd
44
+
45
+ pairs = []
46
+ for m in midxs:
47
+ dist = nm.linalg.norm(bnd[sidxs, 1:] - dv - bnd[m, 1:], axis=1)
48
+ idx = nm.where(dist < (dd * tol))[0]
49
+ pairs.append((m, sidxs[idx[0]]))
50
+
51
+ pairs = nm.array(pairs)
52
+ args = tuple(bnd[pairs, 0].T) + tuple(dv)
53
+ set_periodic(*args)
54
+
55
+
56
+ def set_mesh_size(model, pgroups, msize, dim=2):
57
+ """
58
+ Set mesh size individually to each physical group.
59
+
60
+ Inputs
61
+ ======
62
+ model: gmsh.model
63
+ GMSH model
64
+ pgroups: list
65
+ Physical group IDs
66
+ msize: list of floats
67
+ Mesh size for each physical group
68
+ """
69
+ mesh = model.mesh
70
+ fields = []
71
+ for pg, ms in zip(pgroups, msize):
72
+ entities = model.getEntitiesForPhysicalGroup(dim, pg)
73
+ field = mesh.field.add("Constant")
74
+ mesh.field.setNumbers(field, "SurfacesList", entities)
75
+ mesh.field.setNumber(field, "VIn", ms)
76
+ fields.append(field)
77
+
78
+ field = mesh.field.add("Min")
79
+ mesh.field.setNumbers(field, "FieldsList", fields)
80
+ mesh.field.setAsBackgroundMesh(field)
81
+
82
+
83
+ def gmsh_init():
84
+ "Initiate GMSH module."
85
+ gmsh.initialize()
86
+ gmsh.logger.start()
87
+ gmsh.model.add('boolean')
88
+
89
+ return gmsh, gmsh.model, gmsh.model.occ
90
+
91
+
92
+ def get_xy(p, dec=6):
93
+ return (float(nm.round(p.x, dec)), float(nm.round(p.y, dec)))
94
+
95
+
96
+ def quadratic_to_cubic(p0, c1, p1):
97
+ "Convert SVG quadratic bezier into equivalent cubic bezier."
98
+
99
+ c1 = (p0[0] + 2.0/3.0*(c1[0] - p0[0]),
100
+ p0[1] + 2.0/3.0*(c1[1] - p0[1]))
101
+ c2 = (p1[0] + 2.0/3.0*(c1[0] - p1[0]),
102
+ p1[1] + 2.0/3.0*(c1[1] - p1[1]))
103
+
104
+ return c1, c2
105
+
106
+
107
+ def rotate_obj(obj, angle, occ, h, cx=0, cy=0):
108
+ "Rotate OCC object around Z axis."
109
+ if nm.abs(angle) > 1e-3:
110
+ occ.rotate([obj], cx, h - cy, 0, 0, 0, 1, angle)
111
+
112
+
113
+ def get_layer_elements(elements, occ, cargs, merge=False, oflag=''):
114
+ out = []
115
+ for el in elements:
116
+ if isinstance(el, list):
117
+ out += get_layer_elements(el, occ, cargs, True, ' ')
118
+ else:
119
+ shape = el.__class__.__name__
120
+ print(f'{oflag}{shape}: {el}')
121
+ if shape == 'Rect':
122
+ y = cargs[4] - el.y - el.height
123
+ out.append((2, occ.addRectangle(el.x, y, 0,
124
+ el.width, el.height)))
125
+ # rotate_obj(out[-1], -el.rotation, occ, cargs[4])
126
+ # el.transform[4], el.transform[5])
127
+
128
+ elif shape == 'Ellipse':
129
+ out.append((2, occ.addDisk(el.cx, cargs[4] - el.cy, 0,
130
+ el.rx, el.ry)))
131
+ rotate_obj(out[-1], -el.rotation, occ, cargs[4])
132
+
133
+ elif shape == 'Path':
134
+ point_keys = {get_xy(p) for p in el.as_points()}
135
+ points = {p: occ.addPoint(p[0], cargs[4] - p[1], 0)
136
+ for p in point_keys}
137
+
138
+ loops, lines = [], []
139
+ for seg in el:
140
+ seg_name = seg.__class__.__name__
141
+ if seg_name == 'Move':
142
+ if len(lines) > 0:
143
+ if el.id.startswith('text'):
144
+ loops.append(occ.addCurveLoop(lines))
145
+ out.append((2, occ.addPlaneSurface(loops)))
146
+ else:
147
+ loops.append(occ.addCurveLoop(lines))
148
+ lines = []
149
+
150
+ elif seg_name in ['Line', 'Close']:
151
+ p1, p2 = get_xy(seg.start), get_xy(seg.end)
152
+ if p1 != p2:
153
+ lines.append(occ.addLine(points[p1], points[p2]))
154
+
155
+ elif seg_name == 'QuadraticBezier':
156
+ p1, c12, p2 = (get_xy(seg.start), get_xy(seg.control),
157
+ get_xy(seg.end))
158
+ c1, c2 = quadratic_to_cubic(p1, c12, p2)
159
+ points[c1] = occ.addPoint(c1[0], cargs[4] - c1[1], 0)
160
+ points[c2] = occ.addPoint(c2[0], cargs[4] - c2[1], 0)
161
+ lines.append(occ.addBezier([points[p1], points[c1],
162
+ points[c2], points[p2]]))
163
+
164
+ elif seg_name == 'CubicBezier':
165
+ p1, p2 = get_xy(seg.start), get_xy(seg.end)
166
+ c1, c2 = get_xy(seg.control1), get_xy(seg.control2)
167
+ lines.append(occ.addBezier([points[p1], points[c1],
168
+ points[c2], points[p2]]))
169
+
170
+ elif seg_name == 'Arc':
171
+ apoints = [get_xy(seg.point(t))
172
+ for t in nm.linspace(0, 1, 30)]
173
+ spoints = []
174
+ for apt in apoints:
175
+ if apt not in points:
176
+ y = cargs[4] - apt[1]
177
+ points[apt] = occ.addPoint(apt[0], y, 0)
178
+ spoints.append(points[apt])
179
+
180
+ lines.append(occ.addBSpline(spoints))
181
+
182
+ else:
183
+ raise ValueError(f'Unknown Path element "{seg_name}"!')
184
+
185
+ loops.append(occ.addCurveLoop(lines))
186
+ out.append((2, occ.addPlaneSurface(loops)))
187
+
188
+ else:
189
+ raise ValueError(f'Unknown SVG element "{shape}"!')
190
+
191
+
192
+ # merge grouped objects
193
+ if merge and len(out) > 1:
194
+ out, _ = occ.fuse(out[:1], out[1:])
195
+
196
+ # crop to view box
197
+ if cargs and len(out) > 0:
198
+ cbox1 = (2, occ.addRectangle(*cargs))
199
+ cbox2 = (2, occ.addRectangle(-cargs[3], -cargs[4], cargs[2],
200
+ 3*cargs[3], 3*cargs[4]))
201
+ cbox, _ = occ.cut([cbox2], [cbox1])
202
+ out, _ = occ.cut(out, cbox)
203
+
204
+ return out
205
+
206
+
207
+ def save_png(filename_out, filename_png):
208
+ """
209
+ Save mesh screenshot as PNG file.
210
+
211
+ Inputs
212
+ ======
213
+ filename_out: str
214
+ Output file name
215
+ filename_png: str
216
+ PNG output file name
217
+ """
218
+ import pyvista as pv
219
+
220
+ mesh = meshio.read(filename_out)
221
+
222
+ plotter = pv.Plotter(off_screen=True)
223
+ plotter.add_mesh(mesh, show_edges=True, scalars='mat_id', show_scalar_bar=False)
224
+
225
+ plotter.view_xy()
226
+ plotter.camera.zoom(1.4)
227
+ plotter.image_scale = 2
228
+ plotter.show(screenshot=filename_png)
229
+
230
+
231
+ def sum_dict(d):
232
+ return sum(d, [])
233
+
234
+
235
+ def gen_mesh_from_svg(filename_svg, filename_out=None,
236
+ mesh_size=None, unit_cell=False,
237
+ periodic=False, export_png=False):
238
+ """
239
+ Generate FE mesh from geometry defined by a SVG file.
240
+
241
+ Inputs
242
+ ======
243
+ filename_svg: str
244
+ SVG file name
245
+ filename_out: str
246
+ Output file name
247
+ mesh_size: float
248
+ Size of mesh elements
249
+ unit_cell: bool
250
+ If True, generate periodic unit cell
251
+ periodic: bool
252
+ If True, generate periodic mesh
253
+ export_png: bool
254
+ If True, save mesh screenshot (PNG)
255
+ """
256
+ gmsh, model, occ = gmsh_init()
257
+
258
+ # set ppi=25.4 to keep units in mm
259
+ svg = SVG.parse(filename_svg, ppi=25.4)
260
+
261
+ vbox = svg.viewbox
262
+ cargs = (vbox.x, vbox.y, 0, vbox.width, vbox.height)
263
+
264
+ layers = []
265
+ layer_ids = []
266
+ for el in svg:
267
+ lid = int(el.id.replace('layer', ''))
268
+ print(f'layer {lid}:')
269
+ layers.append(get_layer_elements(el, occ, cargs, oflag=' '))
270
+ layer_ids.append(lid)
271
+
272
+ if len(layers) > 1:
273
+ _, ovv = occ.fragment(layers[0], sum_dict(layers[1:]))
274
+ else:
275
+ ovv = layers
276
+
277
+ model.occ.synchronize()
278
+
279
+ mids = sum_dict([[ik] * len(k) for ik, k in enumerate(layers)])
280
+ tags_1 = set()
281
+ pgs = [[] for _ in range(len(layers))]
282
+ for mid in nm.arange(len(ovv))[::-1]:
283
+ ids = [k for _, k in ovv[mid] if k not in tags_1]
284
+ tags_1 = tags_1 | set(ids)
285
+ pgs[mids[mid]].append(ids)
286
+
287
+ pgs1 = []
288
+ for mid, pg in enumerate(pgs):
289
+ pgs1.append(model.addPhysicalGroup(2, sum_dict(pg), layer_ids[mid]))
290
+
291
+ if mesh_size is None:
292
+ mesh_size = vbox.width / 10
293
+
294
+ if isinstance(mesh_size, (int, float)):
295
+ ms = [mesh_size] * len(pgs1)
296
+ else:
297
+ ms = mesh_size
298
+
299
+ if periodic or unit_cell:
300
+ set_periodic(model, vbox.width, vbox.height, 0)
301
+
302
+ set_mesh_size(model, pgs1, ms)
303
+ gmsh.option.setNumber("Mesh.Algorithm", 2)
304
+
305
+ model.mesh.generate(2)
306
+
307
+ filename_base = os.path.splitext(filename_svg)[0]
308
+ filename_msh = f'{filename_base}.msh'
309
+
310
+ gmsh.write(filename_msh)
311
+ gmsh.finalize()
312
+
313
+ mesh = meshio.read(filename_msh)
314
+ mesh.cell_data = {'mat_id': mesh.cell_data['gmsh:physical']}
315
+ mesh.point_data = {}
316
+ mesh.cell_sets = None
317
+
318
+ if unit_cell:
319
+ points = mesh.points
320
+ bbox = [nm.min(points, axis=0), nm.max(points, axis=0)]
321
+ points -= bbox[0]
322
+ dd = bbox[1] - bbox[0]
323
+ for k, d in enumerate(dd[:2]):
324
+ points[:, k] /= d
325
+ points[nm.abs(points[:, k]) < 1e-6, k] = 0.
326
+
327
+ print(mesh)
328
+
329
+ if filename_out is None:
330
+ filename_out = f'{filename_base}.vtk'
331
+
332
+ mesh.write(filename_out, binary=False)
333
+
334
+ if export_png:
335
+ filename_png = f'{filename_base}.png'
336
+ save_png(filename_out, filename_png)
337
+
338
+
339
+ def parse_args():
340
+ parser = argparse.ArgumentParser(
341
+ prog='SVG2MESH',
342
+ description='SVG2MESH - Generate FE mesh using SVG defined geometry.')
343
+
344
+ parser.add_argument('filename_svg')
345
+ parser.add_argument('-o', '--output', action='store',
346
+ dest='filename_out', default=None)
347
+ parser.add_argument('-u', '--unit-cell', action='store_true',
348
+ dest='unit_cell', default=False)
349
+ parser.add_argument('-p', '--periodic', action='store_true',
350
+ dest='periodic', default=False)
351
+ parser.add_argument('-s', '--mesh-size', action='store',
352
+ dest='mesh_size', default=None)
353
+ parser.add_argument('-e', '--export-png', action='store_true',
354
+ dest='export_png', default=False)
355
+
356
+
357
+ return parser.parse_args()
358
+
359
+
360
+ def main():
361
+ args = parse_args()
362
+
363
+ mesh_size = args.mesh_size
364
+ if mesh_size is not None:
365
+ mesh_size = literal_eval(mesh_size)
366
+
367
+ gen_mesh_from_svg(args.filename_svg, filename_out=args.filename_out,
368
+ mesh_size=mesh_size, unit_cell=args.unit_cell,
369
+ periodic=args.periodic, export_png=args.export_png)
370
+
371
+
372
+ if __name__ == '__main__':
373
+ main()