|
| 1 | +# ========================================================================================== |
| 2 | +# 2D Complex Shape Sampling and Winding Number Visualization |
| 3 | +# |
| 4 | +# This example demonstrates how to: |
| 5 | +# 1. Load a 2D geometry from an ASCII file (e.g., a curve). |
| 6 | +# 2. Sample particles within this complex geometry using the `ComplexShape` functionality. |
| 7 | +# 3. Utilize the Winding Number algorithm to determine if points are inside or outside. |
| 8 | +# 4. Visualize the sampled particles and the winding number field. |
| 9 | +# |
| 10 | +# The example uses an "inverted_open_curve" geometry, where standard inside/outside |
| 11 | +# definitions might be ambiguous without a robust point-in-polygon test like winding numbers. |
| 12 | +# ========================================================================================== |
| 13 | + |
1 | 14 | using TrixiParticles |
2 | | -using Plots |
| 15 | +using Plots # For visualizing the winding number field |
| 16 | +using Plots.PlotMeasures # For plot margins, if needed |
| 17 | + |
| 18 | +# ------------------------------------------------------------------------------ |
| 19 | +# Parameters |
| 20 | +# ------------------------------------------------------------------------------ |
| 21 | +# Particle spacing for sampling the complex shape |
| 22 | +particle_spacing = 0.05 # meters |
| 23 | + |
| 24 | +# Geometry file details |
| 25 | +geometry_filename_stem = "inverted_open_curve" |
| 26 | +# Assuming the data file is in "examples/preprocessing/data/" relative to TrixiParticles.jl root |
| 27 | +geometry_file_path = joinpath(pkgdir(TrixiParticles), "examples", "preprocessing", "data", |
| 28 | + geometry_filename_stem * ".asc") |
| 29 | + |
| 30 | +# Parameters for the Winding Number algorithm |
| 31 | +# `winding_number_factor` helps classify points near the boundary. |
| 32 | +# `hierarchical_winding` uses a multi-level approach for robustness and efficiency. |
| 33 | +winding_number_threshold_factor = 0.4 # Points with winding number > factor are considered inside |
| 34 | +use_hierarchical_winding = true |
| 35 | + |
| 36 | +# ------------------------------------------------------------------------------ |
| 37 | +# Load Geometry and Sample Particles |
| 38 | +# ------------------------------------------------------------------------------ |
| 39 | +# Load the 2D geometry from the specified file. |
| 40 | +# `geometry` will be a `BoundaryPath` object or similar. |
| 41 | +println("Loading 2D geometry from: $geometry_file_path") |
| 42 | +geometry = load_geometry(geometry_file_path) |
| 43 | + |
| 44 | +# Optional: Export the loaded geometry to a VTK file for inspection. |
| 45 | +# This helps verify that the geometry was loaded correctly. |
| 46 | +output_vtk_geometry_filename = "out/$(geometry_filename_stem)_boundary.vtk" |
| 47 | +trixi2vtk(geometry, filename=output_vtk_geometry_filename) |
| 48 | +println("Exported loaded geometry to $output_vtk_geometry_filename") |
| 49 | + |
| 50 | +# Define the point-in-geometry algorithm using Winding Numbers. |
| 51 | +# This algorithm determines which grid points (for sampling) fall inside the geometry. |
| 52 | +point_in_geometry_test = WindingNumberJacobson(geometry=geometry, |
| 53 | + winding_number_factor=winding_number_threshold_factor, |
| 54 | + hierarchical_winding=use_hierarchical_winding) |
3 | 55 |
|
4 | | -particle_spacing = 0.05 |
| 56 | +# Sample particles within the complex shape. |
| 57 | +# `ComplexShape` generates an `InitialCondition` object containing particle positions and properties. |
| 58 | +# `store_winding_number=true` stores the calculated winding number for each sampled point. |
| 59 | +println("Sampling particles within the complex shape...") |
| 60 | +sampled_shape_data = ComplexShape(geometry; |
| 61 | + particle_spacing=particle_spacing, |
| 62 | + density=1.0, # Dummy density for visualization |
| 63 | + store_winding_number=true, |
| 64 | + point_in_geometry_algorithm=point_in_geometry_test) |
5 | 65 |
|
6 | | -filename = "inverted_open_curve" |
7 | | -file = joinpath("examples", "preprocessing", "data", filename * ".asc") |
| 66 | +# The actual particle data is in `sampled_shape_data.initial_condition`. |
| 67 | +initial_condition_particles = sampled_shape_data.initial_condition |
8 | 68 |
|
9 | | -geometry = load_geometry(file) |
| 69 | +# Export the sampled particles (initial condition) to a VTK file. |
| 70 | +output_vtk_particles_filename = "out/$(geometry_filename_stem)_sampled_particles.vtp" |
| 71 | +trixi2vtk(initial_condition_particles, filename=output_vtk_particles_filename) |
| 72 | +println("Exported sampled particles to $output_vtk_particles_filename") |
10 | 73 |
|
11 | | -trixi2vtk(geometry) |
| 74 | +# ------------------------------------------------------------------------------ |
| 75 | +# Visualize Winding Numbers (Optional) |
| 76 | +# ------------------------------------------------------------------------------ |
| 77 | +# `sampled_shape_data.grid` contains all grid points considered during sampling. |
| 78 | +# `sampled_shape_data.winding_numbers` contains the winding number for each of these grid points. |
12 | 79 |
|
13 | | -point_in_geometry_algorithm = WindingNumberJacobson(; geometry, |
14 | | - winding_number_factor=0.4, |
15 | | - hierarchical_winding=true) |
| 80 | +# Extract coordinates of all grid points and their winding numbers. |
| 81 | +grid_point_coordinates = stack(sampled_shape_data.grid) # Converts vector of SVectors to matrix |
| 82 | +grid_winding_numbers = sampled_shape_data.winding_numbers |
16 | 83 |
|
17 | | -# Returns `InitialCondition` |
18 | | -shape_sampled = ComplexShape(geometry; particle_spacing, density=1.0, |
19 | | - store_winding_number=true, |
20 | | - point_in_geometry_algorithm) |
| 84 | +# Create an `InitialCondition` object for plotting all grid points colored by winding number. |
| 85 | +# This helps visualize the winding number field. |
| 86 | +grid_visualization_ic = InitialCondition(coordinates=grid_point_coordinates, |
| 87 | + density=1.0, # Dummy density |
| 88 | + particle_spacing=particle_spacing) # For marker size in plot |
21 | 89 |
|
22 | | -trixi2vtk(shape_sampled.initial_condition) |
| 90 | +println("Plotting the winding number field for all considered grid points...") |
| 91 | +winding_number_plot = plot(grid_visualization_ic, |
| 92 | + zcolor=grid_winding_numbers, # Color by winding number |
| 93 | + markersize=2, markerstrokewidth=0, |
| 94 | + colorbar_title="Winding Number", |
| 95 | + title="Winding Number Field of '$geometry_filename_stem'", |
| 96 | + aspect_ratio=:equal) |
| 97 | +# Overlay the original geometry boundary for context |
| 98 | +plot!(winding_number_plot, geometry, linecolor=:black, linewidth=1.5, label="Geometry Boundary") |
23 | 99 |
|
24 | | -coordinates = stack(shape_sampled.grid) |
25 | | -# trixi2vtk(shape_sampled.signed_distance_field) |
26 | | -# trixi2vtk(coordinates, w=shape_sampled.winding_numbers) |
| 100 | +display(winding_number_plot) |
| 101 | +println("Complex shape 2D example finished. Check 'out/' directory for VTK files and plot window.") |
27 | 102 |
|
28 | | -# Plot the winding number field |
29 | | -plot(InitialCondition(; coordinates, density=1.0, particle_spacing), |
30 | | - zcolor=shape_sampled.winding_numbers) |
| 103 | +# Additional advanced visualization (commented out, requires specific understanding): |
| 104 | +# - `sampled_shape_data.signed_distance_field`: If `create_signed_distance_field=true` was used. |
| 105 | +# trixi2vtk(sampled_shape_data.signed_distance_field) |
| 106 | +# - Winding numbers for only the *sampled* (inside) particles: |
| 107 | +# To get this, one would filter `sampled_shape_data.grid` based on where particles were placed |
| 108 | +# in `initial_condition_particles` and then map the winding numbers. |
| 109 | +# The current `sampled_shape_data.winding_numbers` is for all grid points. |
0 commit comments