Skip to content

Commit 1188a9a

Browse files
authored
Merge branch 'main' into sgs
2 parents 1d19cef + a91211d commit 1188a9a

File tree

4 files changed

+144
-4
lines changed

4 files changed

+144
-4
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ DelimitedFiles = "1"
4747
DiffEqCallbacks = "4"
4848
FastPow = "0.1"
4949
FileIO = "1"
50-
ForwardDiff = "0.10"
50+
ForwardDiff = "1"
5151
GPUArraysCore = "0.2"
5252
JSON = "0.21"
5353
KernelAbstractions = "0.9"

docs/src/preprocessing/preprocessing.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,51 @@ Pages = [joinpath("preprocessing", "point_in_poly", "winding_number_hormann.jl")
250250
Modules = [TrixiParticles]
251251
Pages = [joinpath("preprocessing", "point_in_poly", "winding_number_jacobson.jl")]
252252
```
253+
# [Read geometries from file](@id read_geometries_from_file)
254+
Geometries can be imported using the [`load_geometry`](@ref) function.
255+
- For 3D geometries, we support the binary (`.stl`) format.
256+
- For 2D geometries, the recommended format is DXF (`.dxf`), with optional support for a simple ASCII (`.asc`) format.
257+
258+
## ASCII Format (.asc)
259+
An .asc file contains a list of 2D coordinates, space-delimited, one point per line,
260+
where the first column are the x values and the second the y values.
261+
For example:
262+
```plaintext
263+
# ASCII
264+
0.0 0.0
265+
1.0 0.0
266+
1.0 1.0
267+
0.0 1.0
268+
```
269+
It is the user’s responsibility to ensure the points are ordered correctly.
270+
This format is easy to generate and inspect manually.
271+
272+
## DXF Format (.dxf) – recommended
273+
The DXF (Drawing Exchange Format) is a widely-used CAD format for 2D and 3D data.
274+
We recommend this format for defining 2D polygonal.
275+
Only DXF entities of type `LINE` or `POLYLINE` are supported.
276+
To create DXF files from scratch, we recommend using the open-source tool [FreeCAD](https://www.freecad.org/).
277+
For a less technical approach, we recommend using [Inkscape](https://inkscape.org/) to create SVG files and then export them as DXF.
278+
279+
### Creating DXF Files with FreeCAD
280+
281+
- Open FreeCAD and create a new document.
282+
- Switch to the Sketcher workbench and create a new sketch.
283+
- Choose the XY-plane orientation and draw your geometry.
284+
- Select the object to be exported.
285+
- Go to "File > Export..." and choose "AutoDesk DXF (*.dxf)" as the format.
286+
- Ensure the following Import-Export options are enabled:
287+
- "Use legacy Python exporter".
288+
- "Project exported objects along current view direction".
289+
290+
### Creating DXF Files with Inkscape
291+
SVG vector graphics can also be used as a basis for geometry.
292+
Use Inkscape to open or create the SVG.
293+
You can simply draw a Bezier curve by pressing "B" on your keyboard.
294+
We reommend the mode "Create spiro paths" for a smooth curve.
295+
Select the relevant path and export it as DXF:
296+
- Go to "File > Save As...".
297+
- Choose "Desktop Cutting Plotter (AutoCAD DXF R12)(*.dxf)" format.
253298

254299
```@autodocs
255300
Modules = [TrixiParticles]

src/preprocessing/geometries/io.jl

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
load_geometry(filename; element_type=Float64)
33
44
Load file and return corresponding type for [`ComplexShape`](@ref).
5-
Supported file formats are `.stl` and `.asc`.
5+
Supported file formats are `.stl`, `.asc` and `dxf`.
6+
For comprehensive information about the supported file formats, refer to the documentation at
7+
[Read geometries from file](@ref read_geometries_from_file).
68
79
# Arguments
810
- `filename`: Name of the file to be loaded.
@@ -17,10 +19,12 @@ function load_geometry(filename; element_type=Float64)
1719

1820
if file_extension == ".asc"
1921
geometry = load_ascii(filename; ELTYPE, skipstart=1)
22+
elseif file_extension == ".dxf"
23+
geometry = load_dxf(filename; ELTYPE)
2024
elseif file_extension == ".stl"
2125
geometry = load(FileIO.query(filename); ELTYPE)
2226
else
23-
throw(ArgumentError("Only `.stl` and `.asc` files are supported (yet)."))
27+
throw(ArgumentError("Only `.stl`, `.asc` and `.dxf` files are supported (yet)."))
2428
end
2529

2630
return geometry
@@ -35,6 +39,97 @@ function load_ascii(filename; ELTYPE=Float64, skipstart=1)
3539
return Polygon(copy(points'))
3640
end
3741

42+
function load_dxf(filename; ELTYPE=Float64)
43+
points = Tuple{ELTYPE, ELTYPE}[]
44+
45+
load_dxf!(points, filename)
46+
47+
return Polygon(stack(points))
48+
end
49+
50+
function load_dxf!(points::Vector{Tuple{T, T}}, filename) where {T}
51+
open(filename) do io
52+
lines = readlines(io)
53+
54+
# Check if the DXF file contains line entities ("LINE") or polyline entities ("POLYLINE")
55+
idx_first_line = findfirst(x -> strip(x) == "LINE", lines)
56+
idx_first_polyline = findfirst(x -> strip(x) == "POLYLINE", lines)
57+
58+
if !isnothing(idx_first_line) && isnothing(idx_first_polyline)
59+
# The file contains only simple line entities ("LINE")
60+
i = idx_first_line
61+
62+
while i <= length(lines)
63+
if strip(lines[i]) == "LINE"
64+
if idx_first_line == i
65+
# For a polygon, we only need to store the start point of the first line entity.
66+
# For subsequent lines, the end point of the previous edge is the start point of the current edge,
67+
# so storing all start points would result in duplicate vertices.
68+
# Therefore, we only push the start point for the very first line.
69+
70+
# Search for the coordinates of the start point:
71+
# Group codes "10", "20", "30" represent x, y, z of the start point
72+
while i <= length(lines) && strip(lines[i]) != "10"
73+
i += 1
74+
end
75+
x1 = parse(T, strip(lines[i + 1]))
76+
@assert strip(lines[i + 2]) == "20"
77+
y1 = parse(T, strip(lines[i + 3]))
78+
79+
push!(points, (x1, y1))
80+
end
81+
82+
# Search for the end point coordinates:
83+
# Group codes "11", "21", "31" represent x, y, z of the end point
84+
while i <= length(lines) && strip(lines[i]) != "11"
85+
i += 1
86+
end
87+
x2 = parse(T, strip(lines[i + 1]))
88+
@assert strip(lines[i + 2]) == "21"
89+
y2 = parse(T, strip(lines[i + 3]))
90+
91+
# Add end point of the line to the point list
92+
push!(points, (x2, y2))
93+
end
94+
i += 1
95+
end
96+
97+
elseif isnothing(idx_first_line) && !isnothing(idx_first_polyline)
98+
# The file contains only polyline entities ("POLYLINE")
99+
i = idx_first_polyline
100+
101+
while i <= length(lines)
102+
line = strip(lines[i])
103+
if line == "VERTEX"
104+
# Search for the coordinates of the current vertex:
105+
# Group codes "10", "20", "30" represent x, y, z of the vertex
106+
while i <= length(lines) && strip(lines[i]) != "10"
107+
i += 1
108+
end
109+
x = parse(T, strip(lines[i + 1]))
110+
@assert strip(lines[i + 2]) == "20"
111+
y = parse(T, strip(lines[i + 3]))
112+
113+
# Add the vertex to the point list
114+
push!(points, (x, y))
115+
116+
elseif line == "SEQEND"
117+
# End of the polyline reached
118+
break
119+
end
120+
i += 1
121+
end
122+
else
123+
throw(ArgumentError("This entity type is not supported. Only 'LINE' OR 'POLYLINE' are allowed."))
124+
end
125+
end
126+
127+
# Remove duplicate points from the list
128+
unique(points)
129+
130+
return points
131+
end
132+
38133
# FileIO.jl docs:
39134
# https://juliaio.github.io/FileIO.jl/stable/implementing/#All-at-once-I/O:-implementing-load-and-save
40135
function load(fn::FileIO.File{FileIO.format"STL_BINARY"}; element_types...)

test/general/smoothing_kernels.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686

8787
# This should work with very tight tolerances
8888
@test isapprox(analytic_deriv, automatic_deriv,
89-
rtol=5e-15, atol=2eps())
89+
rtol=5e-15, atol=4 * eps())
9090
end
9191
end
9292
end

0 commit comments

Comments
 (0)