Skip to content
Snippets Groups Projects
Commit 41d15729 authored by Kristina Mazur's avatar Kristina Mazur
Browse files

Merge branch 'documentation/webpage-content' into 'develop'

Integrate mkdoxy in CI pipeline

See merge request !17
parents 7431af43 b6e67594
Branches
No related tags found
3 merge requests!76Draft: Updated Python code example,!73Initial open source version,!17Integrate mkdoxy in CI pipeline
Pipeline #1601398 failed
Showing
with 2382 additions and 12 deletions
# Getting Started {#getting_started}
This guide will show you the basic usage of the geometry objects.
→ Only the most important operations are shown here.
You can refer to the *unit tests* within the test folder located in the source code for more examples.
The library is completely *test driven* so you can find most functionality in the tests and see how they are supposed to be used.
→ The following examples assume you execute them in a sequential manner. That means that the objects which appear in the example, which are not directly instantiated, are assumed to already exists and are created in the previous code examples.
---
## Install CGAL
The **CGAL** library is an external dependency we do not provide within the copy of **UNICADO**. So you need to make sure the library is installed and can be found by *CMake*. You can refer to the [→ Getting Started Guide](https://doc.cgal.org/latest/Manual/general_intro.html) of **CGAL** to find instructions for your machine.
If you use the **MSYS2** environment as the **UNICADO** build setup requires, you can install **CGAL** with this command in your terminal:
```sh
pacman -S mingw-w64-ucrt-x86_64-cgal
```
---
## Create Sections
```cpp
/* Includes */
#include <aircraftGeometry2/geometry/section.h>
/* Create the shape */
geom2::Polygon_2 shape;
shape.push_back(geom2::Point_2(0, 0));
shape.push_back(geom2::Point_2(1, 0));
shape.push_back(geom2::Point_2(1, 1));
shape.push_back(geom2::Point_2(0, 1));
/* Create the section */
geom2::PolygonSection section(shape);
```
---
## Create Surfaces
```cpp
/* Includes */
#include <aircraftGeometry2/geometry/surface.h>
#include <aircraftGeometry2/geometry/factory.h>
/* Setup the surface builder */
geom2::SectionBuilder<geom2::PolygonSection> builder;
/* Insert section using different methods */
geom2::Vector_3 offset_z{0.0, 0.0, 0.5};
geom2::Vector_3 offset_y{0.0, 0.5, 0.5};
builder.arrange(shape, offset_z, 2);
builder.insert_back(shape, offset_y);
/* Create the surface */
geom2::MultisectionSurface<geom2::PolygonSection> surface;
surface.sections = builder.get_result();
```
---
## Use Surfaces
### Measure
```cpp
/* Includes */
#include <aircraftGeometry2/processing/measure.h>
/* Measure the surface area of a surface */
double area = geom2::measure::area(surface);
```
### Transform
```cpp
/* Includes */
#include <aircraftGeometry2/processing/transform.h>
/* Create a triangulated surface mesh */
geom2::Mesh mesh = geom2::transform::to_mesh(surface.sections);
```
---
## Import Surfaces
```cpp
/* Includes */
#include <aircraftGeometry2/hull_surface.h>
/* Create the nacelle surface as defined in the Aircraft XML file.
* => Assuming the XML file is loaded and exists as a node object.
* => And assuming you have the path `data_dir` where the dat files are stored.
*/
geom2::HullFactory factory{AcXml, data_dir};
geom2::MultisectionSurface<geom2::PolygonSection> surface = factory.create("Nacelle@ID");
```
---
\ No newline at end of file
# Introduction {#mainpage}
This library is based on the older *aircraftGeometry* library and extends it to be more modular.
The modularity and flexibility is achieved by using the high performance [Computational Geometry Algorithms Library](https://www.cgal.org/) also known as **CGAL**.
The library tries to model the shapes needed for aircraft design as flexibel as possible and tries to limit the assumptions about the shapes as far as possible.
It does not assume a specific orientation of components in the 3D space, but models every component in its own local coordinate space which then can be transformed to any parent coordinate system.
This flexibility is great to very freely define complex shapes which have an arbitrary location/orientation in the 3D space, but is makes it a bit harder to use since you have to remember in which coordinate space you are currently working in.
This documentation explains the different coordinate systems and how they are derived from each other in a later section.
Each geometry object in this library is referred to as an entity and derives from Entity3D.
This class defines the general properties of the location and orientation of the geometry.
It is used to determine the transformation in between the different coordinate spaces.
Before we dive into the coordinate systems however, let's get to know how the actual geometry and shapes are represented using **CGAL**.
@attention &rarr; This library does only support discrete shapes. Meaning the shapes are **always** polygons where the discrete vertices of the polygon are connected with lines which form the edges of the polygon.
!!! note
The Python binding of this library does __not__ include a complete implementation of **CGAL**!
If you want to use the full flexibility of **CGAL** you need to implement your tool in C++.
---
## 2D Geometry
The base of every geometry are 2D sections. The base properties of such a shape are shown here:
![Section 2D](figures/section2d.svg){html: width=30%}
A section **always** defines its local axes as `X` and `Y`. Where `X` is the *horizontal* axis and `Y` the *vertical* axis.
The origin point of the section is the origin point of the local coordinate system.
The local coordinate system in the scope of a section always refers to the shown *X* and *Y* axes.
The shape of the section consists of vertices which form the outline polygon. The polygon can have an arbitrary shape. It only needs to be simple, meaning no edges connecting the vertices should intersect each other.
A concrete implementation of this described polygon concept which is used in the library is the [Polygon_2](https://doc.cgal.org/latest/Polygon/index.html#Chapter_2D_Polygon) class from **CGAL**.
The concept of the polygon shape is formalized with the *C++* concept [Shape](@ref geom2.Shape).
You can use any shape class which satisfies this concept to create the surfaces.
This library currently implements two main types:
- [PolygonSection](@ref geom2.PolygonSection): For creating general "tube" based surfaces, i.e. fuselages or nacelles.
- [AirfoilSection](@ref geom2.AirfoilSection): For creating aerodynamic surfaces which have an airfoil shape.
Those two section types mainly differ via the applied terminology how their geometric parameters are called.
For example: An **AirfoilSection** can only set the *chord length* as opposed to a **PolygonSection** where you can set the *width* and *height* of the section independently.
&rarr; Refer to the documentation of each section type to see what parameters you can set.
---
## 3D Geometry
The connection from the 2D section to create 3D surfaces are [multi-section surfaces](@ref geom2.MultisectionSurface). A multi-section surface consists of multiple sections which form the single segments of the the surface. A projected view of such a surface might look like this:
![View of multi-section surface](figures/surface_top.svg){html: width=30%}
The example consists of three surface sections which result in **two** surface segments.
Note, that the library rather uses sections and not segments and therefore does not allow for disconnected steps in between the sections directly. You can however insert two section at the same location, which have different shapes. This effectively gives a stepped shape when viewed from any side, but the data structure itself is still connected.
The main extrusion direction of the surface is along the local `Z` axis.
The origin points of each section are 3D coordinates and define the location of the section within the surface coordinate space. By moving the origin point, the complete section gets moved as well.
!!! note
The order how you insert the sections in the surface **does** matter as it defines how the sections are connected.
The surfaces themselves have an origin point which defines their location in the 3D space of some parent entity.
The multi-section surface is basically just a `std::vector` which contains multiple section objects.
You can use this vector as a regular iterator and apply **STL** algorithms as you are used to.
---
## Coordinate Systems
You should have a basic idea of how the geometry is build up.
Now, comes the part where we position the geometry within one space and actually build up a complete aircraft in its final shape.
> Keep your right hand ready to always do the "three finger dance" to visualize the right hand coordinate space. Also be aware, **Euler angles** will be involved! :-)
The library currently differentiates three coordinate spaces:
1. `2D` : The local coordinate system of a 2D section profile/shape.
2. `3D` : The local coordinate system of a multi-section surface which can include multiple 2D sections.
3. `3D` : The global body-fixed coordinate system of the aircraft where all the surfaces finally live and are positioned.
Whenever one entity is contained within another parent entity, the **parent coordinate system** refers to this parent entity.
### Transform from 2D to 3D
Let's look at a simple example, which mainly uses 2D coordinates:
![Example coordinate transform](figures/coordinate_example.png){html: width=30%}
The *local point* `p` has 2D coordinates and is defined within the *local coordinate* system of `child`. The `child` represents a 2D section here, so it technically does not have a `Z` axis in its local coordinate space. It is just shown here for completeness to always remember the right-hand convention.
The `child` entity is then contained within the 3D entity `parent`. The `child` has an offset within the `parent` coordinate space as defined by its **origin** point.
Within the scope of `child`, point `p` has the coordinates as they are shown in the figure.
In the scope of `parent`, you have to add the offset of the `child` origin `O` to the coordinates of point `p`.
The resulting coordinates of `p` will then be:
\f{align*}{
p_{parent, 3d} &= \begin{bmatrix} 2.5 \\1.5 \\0.0 \end{bmatrix}
\f}
An additional offset of the `child` in `Z` direction is applied to the coordinate transform in the same manner. It just adds the offset to the resulting `Z` coordinate.
> The **origin** point of a *parent* is always `[0, 0, 0]` when dealing with the **parent scope**! When the *parent* is contained within another parent, its scope becomes the *child scope*. &rarr; This paradigm shift is probably the most confusing and hard to imagine part of this library, but it is important to keep the library flexible!
### Transform from 3D to 3D {#euler_angles}
So far no rotation was involved. The coordinate system had always the same orientation but where just translated within the 3D space.
That is not enough to represent arbitrary geometry. You need a mechanism to orient surfaces which should be extruded in a different direction than the global `Z` axis. For this reason, the idea of the **normal direction** is introduced.
The normal direction basically defines the direction where the local `Z` axis should point to within a parent scope.
The direction is defined using the [Direction_3](@ref geom2.Direction_3) class of **CGAL** and is a vector in 3D space. Although, this class is technically not concerned about the length of the vector, it is a good idea to make sure, that the resulting length of the normal direction is equal to **1.0**.
The definition of this normal direction is not enough to unambiguously define the three Euler angles which are needed for the coordinate transform. The normal direction can only define **two** of the three angles. As a consequence, the third Euler angle $\gamma$ has to be set manually using the `rotation_z` property of the geom2::Entity3D class.
This angle applies a rotation around the local `Z` axis whenever the coordinates of the geometry are transformed to another coordinate system.
!!! note
The handling of the third Euler angle can still be subjected to change.
The usage of the Euler angles can lead to unintuitive results, but that is unfortunately the nature of those angles. Here are some results of Euler angles with different normal directions:
|Normal Direction | Euler Angle $\alpha$ | Euler Angle $\beta$ | Euler Angle $\gamma$ |
| --- | :---: | :---: | :---: |
|`[0, 0, 1]` | 0° | 0° | 0° |
|`[0, 1, 1]` | -45° | 0° | 0° |
|`[1, 0, 1]` | 45° | 0° | 90° |
Understanding the relationship of the normal direction and the Euler angles of this table is key to understand the 3D coordinate transformation with rotation.
**Most importantly,** the last case, where a "simple" rotation around the `Y` axis leads to *two* non-zero angles, especially the third angle $\gamma$ is not zero any more as opposed to the initial assumption! That is again due to the fact how Euler angles work...
In the end, you do not need to understand this in great detail to use the library, just be aware, that certain normal directions can lead to a final orientation which was not the one you expected. In such cases, you can use the `rotation_z` parameter to adjust.
## Class Diagram
Here is an overview how the library is structured:
![](figures/class_diagram.png){html: width=1000}
!!! note
This is still work in progress and can change!
# Convert to Different Format {#tutorial_convert}
The library provides conversion functions which can translate the geometry to different output formats.
It is up to the used converter to define the in- and outputs of this conversion.
The converters do **not** access or write files on the hard drive!
There are just means to translate the geometry to other class concepts.
It is up to those concepts to provide the file access!
The converters are implemented using the *visitor design pattern*.
The converters inherit from a common base class geom2::io::Converter, for future polymorphic use.
You can "visit" a specific converter with a surface object and the object gets converted to the converter specific return type.
You have the specify how the converter "treats" this surface, since certain formats might treat the same surface geometry differently based on their function (for example wings or stabilizers are both airfoil surfaces, but might get treated differently in the export format).
You can select the surface function base on type by using the surface type variant geom2::io::SurfaceType.
Depending on which type you select for the variant the converter will treat the surface accordingly.
!!! note
The python bindings do work differently. They use the same underlying functions, but the interface does not enable the polymorphic use (yet)!
## Convert to aixml::node Object
The following tutorial will show you how you can convert a multi-section surface as a wing surface node to the aircraft XML using the geom2::io::AixmlConverter class.
The *aixml* library will not be discussed in detail in this tutorial.
Refer to its [documentation](https://www.google.de) if you need further information.
!!! note
The Python examples assume that you imported the following modules:
```python
import pyaircraftGeometry2 as geom2
import pyaixml as aixml
```
### Create Node
The *aixml* converter can only insert nodes into an **existing** node!
This is a precaution, so that the user has to take care of memory management and not the library.
The user can provide heap and/or stack allocated node objects.
The library treats both the same and does not leak memory. ;)
For this tutorial, we will show two use cases of inserting a node:
- Create a completely new node.
- Update an existing node which has additional, non geometry related nodes as children.
To create the node objects:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <filesystem>
#include <aixml/node.h>
/* First create an empty node on the stack */
node new_node{};
/* Load an aircraft exchange file to update */
std::shared_ptr<node> AcXML = aixml::openDocument("path/to/your/aircraft.xml");
```
- <span class="tab-title">Python</span>
```python
# First create an empty node on the stack
new_node = aixml.Node()
# Load an aircraft exchange file to update
AcXML = aixml.openDocument("path/to/your/aircraft.xml")
```
</div>
### Convert To Node
@attention The following steps assume that you have created a surface which contains valid geometry!
Refer to @ref tutorial_geometry to learn how to do that.
You can convert an existing surface by visiting the *aixml* converter.
- Convert surface to an *aerodynamic surface node*:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/io/convert.h>
/* Assume this surface has some valid geometry */
geom2::MultisectionSurface<geom2::AirfoilSection> surface = <<some-geometry>>...
/* Treat the surface as an airfoil surface */
geom2::io::SurfaceType wing = geom2::io::AirfoilSurface{surface};
/* Convert using the aixml format */
node& created_node = std::visit(geom2::io::AixmlConverter{new_node, {"aerodynamic_surface", "0", "description"}}, wing);
```
- <span class="tab-title">Python</span>
```python
# Convert the surface specifically to an aerodynamic surface
created_node = geom2.convert.aixml.to_aerodynamic_surface(new_node, ("aerodynamic_surface", "0", "description"), wing)
```
</div>
Now what is happening here:
- You tell the converter to create a node **inside** `new_node`.
- The created node will have the **name** `"aerodynamic_surface"`.
- The created node will have the **id** `"0"`.
- The created node will have the description attribute with convent** `"description"`.
- The created node will contain the **geometry** of `wing`.
- You get direct access of the **created node** by `created_node`.
- *Be aware*: The `new_node` is also modified! In this case `new_node` is equal to `created_node`.
That's it.
Now you can save the node as an XML file or further use the node object.
### Add Another Node
You can add different nodes by specifying different IDs.
- Add another node using the same surface:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
/* Add another node */
node& created_node = std::visit(geom2::io::AixmlConverter{new_node, {"aerodynamic_surface", "1", "main wing"}}, wing);
```
- <span class="tab-title">Python</span>
```python
# Add another node
created_node = geom2.convert.aixml.to_aerodynamic_surface(new_node, ("aerodynamic_surface", "1", "main wing"), wing)
```
</div>
This will add another node inside `new_node`.
However, the `created_node` will just contain the second inserted node!
### Updating A Node
Let's assume we imported the geometry from an existing aircraft XML file and modified the geometry.
Now, we want to update the data inside the imported node tree.
- Update an existing node tree with the surface geometry:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
/* Update the existing node */
node& created_node = std::visit(geom2::io::AixmlConverter{AcXML->at("path/to/wing/geometry/"), {"aerodynamic_surface", "0", "main wing"}}, wing);
```
- <span class="tab-title">Python</span>
```python
# Update the existing node
created_node = geom2.convert.aixml.to_aerodynamic_surface(AcXML.at("path/to/wing/geometry/"), ("aerodynamic_surface", "0", "main wing"), wing)
```
</div>
This will search for `"path/to/wing/geometry/aerodynamic_surface@0"` and update all geometry specific nodes there.
Existing nodes, which are not written by this library, are not touched and their values are unchanged.
Should the node not exist at the specified node path, it will be created just the same as explained in the previous examples.
The `created_node` contains again a reference to the updated or inserted node.
This diff is collapsed.
# Using the Geometry Classes {#tutorial_geometry}
The following tutorial provides you with the basic usage to create your own shapes.
For each instruction the C++ and Python code is available.
The resulting images of each step are not part of the library, though.
Those images are rendered with a 3D animation software after the geometry is exported as a `*.ply` file after each step.
The tutorial will show as a last step, how this is done.
!!! note
The Python examples assume that you imported the module with:
```python
import pyaircraftGeometry2 as geom2
```
---
## Hull Surface
This tutorial shows you how you can define a simple hull surface.
The hull surface is basically a tube where you can define the shape of the cross sections.
### Step 1 - Loading DAT files
The simplest way to start with a shape for the sections is to load the 2D data from a `*.dat` file.
An example file which defines the shape of a circle is provided as part of the unit tests.
The file can be found here: `aircraftGeometry2/test/stubs/circle-tab.dat`.
- Load the geometry:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/io/dat.h>
geom2::Polygon_2 shape = geom2::io::read_dat_file("aircraftGeometry2/test/stubs/dat-files/circle-tab.dat");
```
- <span class="tab-title">Python</span>
```python
shape = geom2.io.read_dat_file("aircraftGeometry2/test/stubs/dat-files/circle-tab.dat")
```
</div>
!!! note
Make sure the path to the file is correct according to your current working directory!
This will give you the following 2D polygon:
![Tutorial Step 1](Tutorial/step001.png){html: width=600}
The 2D polygon is **always** oriented in the `XY`-Plane.
The 2D geometry gets oriented in the 3D space when you use it as the section of a surface.
### Step 2 - Create Section
The imported shape just contains the coordinates of the vertices of the polygon.
You have to create a `geom2::PolygonSection` from this shape to further create geometry.
- Create a section:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/geometry/section.h>
geom2::PolygonSection section(shape);
```
- <span class="tab-title">Python</span>
```python
section = geom2.PolygonSection(shape)
```
</div>
This does not change the shape of the polygon, but it gives the shape an orientation in the 3D space.
The geom2::PolygonSection class has also methods to manipulate the shape of the polygon.
### Step 3 - Create Surface
After converting the imported polygon to a section, you can use this section to build a surface.
This surface is basically a container for different PolygonSections, see geom2::MultisectionSurface.
This library uses the *builder design pattern* to build a surface.
This builder has some convenient methods to quickly create a surface.
The key concept is, that you first create the builder, perform the build steps and then you can retrieve the result from the builder to further use the surface.
- Create a surface with two sections:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/geometry/factory.h>
/* Create builder */
geom2::SectionBuilder<geom2::PolygonSection> builder;
/* Instruct the builder to insert sections */
builder.insert_back(section, geom2::Vector_3(0,0,0)); // Section 0
builder.insert_back(section, geom2::Vector_3(0,0,1)); // Section 1 with offset in Z direction
/* Create the surface */
geom2::MultisectionSurface<geom2::PolygonSection> surface;
surface.sections = builder.get_result();
```
- <span class="tab-title">Python</span>
```python
# Create the builder
builder = geom2.PolygonBuilder()
# Instruct the builder to insert sections
builder.insert_back(section, geom2.Vector_3(0,0,0)) # Section 0
builder.insert_back(section, geom2.Vector_3(0,0,1)) # Section 1 with offset on Z direction
# Create the surface
surface = geom2.PolygonSurface()
surface.sections = builder.get_result()
```
</div>
@remark There is also the geom2::SectionBuilder::arrange function which can be used to insert multiple equidistant sections of the same shape.
A few things to note about the builder:
1. The section gets copy-constructed within the builder. So you can use the same section multiple times and even change some parameters in between without affecting the already inserted sections.
2. The result of the builder gets **moved**! This means, after you retrieve the result, the builder is "empty" and does not contain a valid surface anymore.
3. Instead of providing a geom2::PolygonSection as the shape input, you can directly use a geom2::Polygon_2. The builder converts the polygon to a section automatically in this case.
The resulting surface has two circular sections and looks like a cylinder.
The length of the cylinder is 1 as the defined by the offset of the second section.
![Tutorial Step 3](Tutorial/step003.png){html: width=600}
### Step 4 - Modify Section
The builder is only intended to create the basic structure of the multi-section surface with the necessary section count.
It does not provide all the flexibility which is needed to form any surface.
You can access and modify each section of the surface instead.
- Set the width of the second section:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
surface.sections.back().set_width(0.5);
```
- <span class="tab-title">Python</span>
```python
surface.sections[-1].set_width(0.5)
```
</div>
The top section looks like this now:
![Tutorial Step 4](Tutorial/step004.png){html: width=600}
The **width** refers to the dimension in the local `X` direction of the section.
Whereas the **height** refers to the `Y` direction.
You can set the width and height separately for geom2::PolygonSection.
The **scale** methods applies an **uniform** scaling with the origin point of the section as the scaling center.
### Step 5 - Move Section
You also change the location of the section within the surface afterwards, but **not the order of the sections!**
The location of the section within the local coordinate system of the section is defined by the origin point of the section.
- Change the location of a section:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
surface.sections[0].origin = geom2::Point_3(-0.5,0,0);
```
- <span class="tab-title">Python</span>
```python
surface.sections[0].origin = geom2.Point_3(-0.5,0,0);
```
</div>
!!! note
**CGAL** does not supply a mechanism to change single components of points or vectors. You always have to assign a complete new set of coordinates. However, you can read the value of single coordinate components.
As you can see, the bottom section moved in negative X direction:
![Tutorial Step 5](Tutorial/step005.png){html: width=600}
### Step 6 - Orient Surface
So far, we only operated in the local coordinate system of the multi-section surface.
The surface can be aligned in the global 3D space using its **origin** and, most importantly, its **normal** direction.
The main extrusion axis within the **local coordinate** system of the surface is always the `Z` direction, as you can see in the previous steps.
You can specify the orientation of the *local* `Z` axis within the *global* 3D space by defining the direction of the **normal**.
The **normal** defacto represents the orientation of the `Z` axis.
@attention It is not enough to just specify the `Z` direction to fully define the orientation of geometry within the 3D space. This only defines 2 of the 3 necessary Euler angles, the third is technically undefined! See @ref euler_angles for more details. The library assumes the third angle to be 0°, which leads most of the times to the expected result.
- Orient the surface along global X direction:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
surface.normal = geom2::Direction_3(1,0,0);
```
- <span class="tab-title">Python</span>
```python
surface.normal = geom2.Direction_3(1,0,0);
```
</div>
In this example the result is this:
![Tutorial Step 6](Tutorial/step006.png){html: width=600}
The highlighted coordinate system in the figure is now the **global** coordinate space and not the local any more.
The local coordinate system of the surface remains unchanged.
You can also see that the orientation introduced a rotation around the local `Z` axis in this case, again see @ref euler_angles why this is.
For the expected result you need to set the parameter `rotate_z` of the surface.
!!! note
This behavior is admittedly not the most intuitive result. In further release this might get fixed. In a practical point of view, you are most often interested in properties within the local coordinate system of the surface rather then global properties in between multiple surfaces.
### Step 7 - Move Surface
As for moving the location of a section, the same principle applies when moving the surface within the global 3D space.
The **origin** of the surface defines the global position.
The **origin** is located at the origin of the first section.
- Move surface
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
surface.origin = geom2::Point_3(0,-0.25,0);
```
- <span class="tab-title">Python</span>
```python
surface.normal = geom2.Point_3(0,-0.25,0);
```
</div>
This moves the surface *0.25* in negative `Y` direction:
![Tutorial Step 7](Tutorial/step007.png){html: width=600}
### Step 8 - Measure Surface
After creating the geometry, you can use different measurement tools to extract dimensions.
The measurement tools always reference the surface in its local coordinate system.
So the results do not depend on the global orientation of the surface.
- Measure the width of the surface at local `Z` position 0.5:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/processing/measure.h>
double width = geom2::measure::width(surface, 0.5);
```
- <span class="tab-title">Python</span>
```python
width = geom2.measure.width(surface, 0.5)
```
</div>
This should result in `width = 0.75`.
!!! note
There are measurement functions which use additional properties of the geom2::MultisectionSurface as their input, because their result depends on more information than contained in the *sections* vector. See the documentation of each measurement for more information.
### Step 9 - Export PLY (optional)
As an optional step, you can export your surface as a triangulated surface mesh in the **PLY** format.
- Export mesh:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <CGAL/Surface_mesh/IO/PLY.h>
geom2::Mesh mesh = geom2::transform::to_mesh(surface);
CGAL::IO::write_PLY("./mesh.ply", mesh);
```
- <span class="tab-title">Python</span>
```python
geom2.io.export_ply(surface, "./mesh.ply")
```
</div>
!!! note
This function is rather a debugging tool than a fully tested tool for production use!
---
## Airfoil Surface {#tutorial_airfoil_surface}
An airfoil surface works similar to the hull surface.
Instead of using polygon sections, the airfoil surface expects geom2::AirfoilSection as inputs.
Those sections follow the same principle as the polygon sections, but offer different methods which are more suitable/intuitive for airfoil shapes.
- You can load an airfoil files with the corresponding function:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/io/dat.h>
geom2::Polygon_2 shape = geom2::io::read_airfoil("aircraftGeometry2/test/stubs/n0012-tab.dat");
```
- <span class="tab-title">Python</span>
```python
shape = geom2.io.read_airfoil("aircraftGeometry2/test/stubs/n0012-tab.dat")
```
</div>
This reads the dat file **and** additionally sorts the points that the resulting polygon does not intersect itself, because airfoil coordinate files specify first the upper coordinates and then the lower coordinates which would result in a discontinuity in between the upper and lower part.
- Create an initial airfoil surface:
<div class="tabbed">
- <span class="tab-title">C++</span>
```cpp
#include <aircraftGeometry2/geometry/factory.h>
/* Create builder */
geom2::SectionBuilder<geom2::AirfoilSection> builder;
/* Instruct the builder to arrange 3 sections */
builder.arrange(section, geom2::Vector_3(0,0,-1), 3);
/* Create the surface */
geom2::MultisectionSurface<geom2::AirfoilSection> surface;
surface.sections = builder.get_result();
```
- <span class="tab-title">Python</span>
```python
# Create the builder
builder = geom2.AirfoilBuilder()
# Instruct the builder to arrange 3 sections
builder.arrange(section, geom2.Vector_3(0,0,-1), 3)
# Create the surface
surface = geom2.AirfoilSurface()
surface.sections = builder.get_result()
```
</div>
This gives a planar rectangular wing shape:
![Tutorial Airfoil Surface](Tutorial/airfoil_surface.png){html: width=600}
@attention You should always use the **negative** `Z` direction as the main span direction of an airfoil surface. Only then do the angles like twist or dihedral rotate in the direction you would expect.
# Tutorial {#Tutorial}
There are tutorial available for the following topics:
- @subpage tutorial_geometry
- @subpage tutorial_factory
- @subpage tutorial_convert
\ No newline at end of file
...@@ -25,8 +25,8 @@ glightbox: false ...@@ -25,8 +25,8 @@ glightbox: false
Here you can found different designed aircraft. The designs are made using the **UNICADO** workflow and are generally in a *valid* and *converged* state. Here you can found different designed aircraft. The designs are made using the **UNICADO** workflow and are generally in a *valid* and *converged* state.
!!! note 🔔 This repository will be translated to a actual database in the future!
This repository will be translated to a actual database in the future!
- [:simple-gitlab: Engines :octicons-link-external-16:](https://git.rwth-aachen.de/unicado/engines): - [:simple-gitlab: Engines :octicons-link-external-16:](https://git.rwth-aachen.de/unicado/engines):
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
docs/documentation/sizing/empennage_design/figures/Report_page_empennage_design.png

131 B

docs/documentation/sizing/empennage_design/figures/Report_page_empennage_design_change.png

131 B

This diff is collapsed.
# Introduction {#mainpage}
The empennage is an essential part of the aircraft. The _empennage\_design_ tool is one of the core design tools in UNICADO and enables the workflow to design an empennage according to specified requirements and design specifications.
## A User's Guide to Empennage Design
The _empennage\_design_ tool will help you design various empennages for classical configurations to blended wing body confiugartions. This user documentation will guide you through all necessary steps to understand the tool as well as the necessary inputs and configurations to create a new empennage from scratch.
The following pages will guide you through the process of generating your first empennage within UNICADO:
- [Basic Concepts](basic-concepts.md)
- [Getting Started](getting-started.md)
- [Design your first empennage](dfe.md)
So let's get started!
## You are a Developer?
If you are familiar with these concepts and want to contribute - head over to the developers guide to get your own method running in UNICADO!
The following pages will help you understand the code structure:
- [Prerequisites](prerequisites.md)
- [Build the code](build-the-code.md)
- [Empennage module structure](wing-module-structure.md)
- [Available methods](available-methods.md)
- [Method template](method-template.md)
We appreciate it!
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# Software architecture
This site is currently under development. :construction:
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment