Final pre PR updates

This commit is contained in:
John Wellbelove 2026-05-28 12:39:15 +01:00
parent a4e140f932
commit 28b3a0f38c
26 changed files with 284 additions and 7 deletions

View File

@ -3,4 +3,13 @@ title: "Blog"
weight: 3001
---
Implementing a moving average.
## Graphics
[Determining line-line intersections]({{% relref "./graphics/determining-line-line-intersections.md" %}})
[Applications of dot and cross products]({{% relref "./graphics/applications-of-dot-and-cross-products.md" %}})
[Scanning an arbitrarily rotated rectangular region]({{% relref "./graphics/scanning-an-arbitrarily-rotated-rectangular-region.md" %}})
## Algorithms
[Implementing a low cost moving average]({{% relref "implementing-a-low-cost-moving-average.md" %}})

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,5 @@
---
title: "Graphics"
weight: 1
---

View File

@ -0,0 +1,68 @@
---
title: "Applications of dot and cross products"
weight: 5
---
## The problem
Sometimes in graphical applications there is a need to know the relative position of a point with respect to a line from a reference point.
Where is the target in relation to the reference and its direction?
Dot and cross products can make this an easy task.
Let's start with a reminder of what dot and cross products are.
Take two vectors `x1,y1` and `x2,y2`
The dot product is `(x1 * x2) + (y1 * y2)` and the cross product is `(x1 * y2) - (x2 * y1)`.
Remember that the dot product is a scalar based on `cos(θ)` and the cross product is a scalar based on `sin(θ)`.
**Example**
![DotCross1](images/DotCross1.png)
We have a reference object at `Xr,Yr` facing in the direction of the arrow.
The target is at point `Xt,Yt`
![DotCross3](images/DotCross3.png)
A arbitrary point on the line from the reference in the specified direction is `Xw,Yw`
First, find the coordinates of the target and waypoint relative to the reference.
`dXw = Xw - Xr`
`dYw = Yw - Yr`
`dXt = Xt - Xr`
`dYt = Yt - Yr`
Now find the dot and cross products of these vectors.
`Dot = (dXw * dXt) + (dYw * dYt)`
`Cross = (dXw * dYt) - (dXt * dYw)`
The absolute values of the dot and cross products is unimportant, we just need the *sign*.
`Dot > 0`, `Cross < 0` : The target is forward of the reference and to the right.
`Dot > 0`, `Cross > 0` : The target is forward of the reference and to the left.
`Dot < 0`, `Cross < 0` : The target is behind the reference and to the right.
`Dot < 0`, `Cross > 0` : The target is behind the reference and to the left.
*forward*, *behind*, *left*, and *right* are relative to the reference's direction.
This calculation will be valid for any direction.
## Other uses
This technique can also be used to determine if a point is within a closed convex hull.
![DotCross4](images/DotCross4.png)
A closed convex hull defined by the lines `L1`, `L2`, `L3`, `L4`, and `L5`.
Point `P1` is inside the hull, point `P2` is outside.
For `P1`, the cross product for all of the lines will indicate that it is to the *right* and therefore, *must* be inside.
For `P2`, the cross product for `L1` will put it on the *left* hand side and therefore it *cannot* be inside the hull.
**Why does this work?**
The dot product of vectors `A` and `B` is `|A||B|cos(Ø)`
The cross product of vectors `A` and `B` is `|A||B|sin(Ø)`
Where `Ø` is the angle between the vectors `A` and `B`. Range `+-180°`
Therefore the sign of the result indicates the quadrant that target occupies in the circle around the reference, relative to the reference's direction.

View File

@ -0,0 +1,112 @@
---
title: "Determining line-line intersections"
weight: 2
---
It's quite common in graphics and image processing to want to know the intersection coordinates of two lines.
The common formula for a line is the familiar `y = Mx + C`.
But there is another that can be a lot easier to use when determining line to line intersections in a graphical environment.
## The issues
When using `y = Mx + C` you must be aware of the situations of when the line approaches 'vertical'.
In this case `M` tends towards infinity, which is not good in a programming environment.
The usual trick is to flip the coordinates when the slope is more than 1, and then flip back after the calculations have been made.
This can be confusing to follow and result in errors.
Also, to keep any accuracy, the calculations must normally be kept in the floating point domain, which is not ideal for performance, as the image will be in integral pixel coordinates.
## The solution
Change the definition of your lines to use the formula `Ax + By = C`.
Ideally, your lines would already be in the form `Ax + By = C`, but this is not normally the case, but we *can* easily generate the parameters from two points.
Assume we have a line defined by `(x1, y1)` and `(x2, y2)`.
We can deduce `A`, `B`, and `C` thus:
`A = y2 - y1`
`B = x1 - x2`
`C = Ax1`+ `By1`
---
Another useful calculation is the *determinant*.
Given two lines `A1x + B1y = C1` and `A2x + B2y = C2`:
`determinant = A1 * B2 - A2 * B1`
If `determinant` is zero, then the lines are parallel.
### Notes
- If `A` and `B` are both non-zero.
The equation represents a diagonal line.
- If `A == 0`, `B != 0`.
The line is the parallel to the x-axis.
- If `A != 0`, `B == 0`.
The line is the parallel to the y-axis.
- If `C == 0`
The line passes through the origin.
## Calculating the intersection
The intersection point is calculated like this:
Given two lines described by the points `(x1, y1)`, `(x2, y2)`, and `(x3, y3)`, `(x3, y3)`.
![LineCross1](images/LineCross1.png)
- Calculate the parameters `A`, `B`, and `C` for each.
`A1x + B1y = C1` and `A2x + B2y = C2`
- Calculate the determinant.
`determinant = A1 * B2 - A2 * B1`
- If `determinant == 0` then the lines are parallel, and there is no intersection point.
- Otherwise
`x = (B2 * C1 - B1 * C2) / determinant`
`y = (A1 * C2 - A2 * C1) / determinant`
## They don't need to physically intersect
The intersection point can be found even if the line segments aren't actually long enough to intersect.
The calculation will effectively extend them to where they *would* intersect, if long enough.
This means you can find intersection points relative to a fixed reference line.
![LineCross2](images/LineCross2.png)
## Refection
This describes reflecting a point across a reference line.
It uses the intersection method described above.
In the example below, we want to reflect `P` in the line `Reference`, to give us `P'`.
![Reflection1](images/Reflection1.png)
First, we need the reference line in the form `Ax + By = C`.
Next, we need to find the perpendicular from `Reference` through `P`.
Any line perpendicular to `Ax + By = C` takes the form `Bx + Ay = D`.
To find `D`, just substitute the `x,y` coordinates from `P`.
Now we have the two lines in the form we require to find the intersection.
Find `I`, which is the intersection of the reference line and the perpendicular to `P`.
Compute `P'` by using the formula `P' = I + (I - P)`.
This calculates the vector from `P` to `I` and then adds it to `I` to move the same amount again.
## Line to point distance
The above technique can be used to find the distance of a point to a reference line.
This distance is merely the absolute length of the vector `I - P`.

View File

@ -0,0 +1,22 @@
---
title: "Dot and cross products"
weight: 4
---
## Dot Product
The dot product of two vectors is the sum of the products of the corresponding elements.
The dot product of vectors `(x1, y1)` and `(x2, y2)` is `x1 * x2 + y1 * y2`.
If `A` & `B` are vectors, the dot product is `|A||B|cos(θ)`, where `θ` is the angle between the `A` and `B`.
`|A|` is the length of the vector `A`.
`|B|` is the length of the vector `B`.
Therefore, we can calculate `Cos(θ) = (A ⋅ B)/(|A||B|)`.
A dot product of `0` indicates two perpendicular lines, and that the dot product is greatest when the lines are parallel.
## Cross Product
The cross product of vectors `(x1, y1)` and `(x2, y2)` is `x1 * y2 - y1 * x2`
If `A` & `B` are vectors, the cross product is `|A||B|sin(θ)`.
`|θ|` is the angle between the two vectors, but `θ` can be positive or negative.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -0,0 +1,38 @@
---
title: "Scanning an arbitrarily rotated rectangular region"
weight: 3
---
Quite often in image processing we need to scan a rectangular region in an image. The region is usually aligned to capture a specific feature in the image.
You may be presented with a skewed image, necessitating that the region should also be skewed.
![ScanRectangle1](images/ScanRectangle1.png)
If it is not absolutely necessary to scan the region at the angle of skew, then the fastest way to interrogate the pixels is to scan the image horizontally.
As was shown in the previous post, the crossing points between two lines can be easily and efficiently determined.
By moving a reference line down across the region, the start and end x coordinates of each scan line can be found.
Only the y coordinate of the reference line is relevant, though it must have a length of at least 1 unit.
Of course, we must check every line in the rectangle, and every line in the rectangle may produce a crossing point.
The reference line shown below will identify points `P1`, `P2`, `P3` and `P4` as valid crossing points.
![ScanRectangle4](images/ScanRectangle4.png)
The solution is to compare the crossing coordinates against the bounds of the enclosing rectangle.
![ScanRectangle5](images/ScanRectangle5.png)
This would eliminate points `P1` and `P4`.
![ScanRectangle2](images/ScanRectangle2.png)
This can be done for all scan lines in the bounded rectangle.
![ScanRectangle6](images/ScanRectangle6.png)
Vertical scans may be easily achieved by simply having a vertical reference line.
This technique can be applied to any convex hull.

View File

@ -1,8 +1,9 @@
---
title: "Implementing a moving average"
weight: 10
---
## Example:
## Example
Imagine we have accumulated 10 values and our window size is 10.
`[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]`
@ -18,7 +19,7 @@ In the case of 10,000 samples of 'double' we are looking at 10,000 additions +
So we have an algorithm that has processing and storage complexity of O(N).
---
## What other values give the same average?
**So let's look at the problem again.**
@ -34,7 +35,7 @@ There are many ways of getting the same average:
An average of `5.5` can be got from 10 samples of `5.5`.
So why store 10 samples?
## So why store 10 samples?
We can simply simulate the original sum just by multiplying the current average by the number of samples.
The calculation of the average for adding a new sample becomes this:
@ -45,7 +46,10 @@ Now we have 1 multiplication, 2 additions and 1 division.
Our algorithm now has a complexity of O(1).
We can average over as large a sample size as we like and it will always take the same amount of time and use the same amount of storage. We can also change the sample size on-the-fly if we wish.
There is one downside in that the average will not exactly match the original version, as the actual oldest value is not being erased from the sum. But for the performance and storage advantages it may be a perfectly suitable solution. You may find that you will need a slightly smaller sample size with the rolling mean to get similar results.
There is one downside in that the average will not exactly match the original version, as the actual oldest value is not being erased from the sum, but for the performance and storage advantages it may be a perfectly suitable solution.
You may find that you will need a slightly smaller sample size with the rolling mean to get similar results.
---

View File

@ -1,7 +1,12 @@
---
title: "reference_flat_set"
title: "reference_flat_multiset"
---
{{< callout type="info">}}
Header: `reference_flat_multiset.h`
Similar to: `std::multiset`
{{< /callout >}}
A fixed capacity set based on a sorted vector.
The container stores references to objects, rather than the objects themselves.
The container is an associative lookup table with O(N) insertion and erase, and O(log N) search.

View File

@ -2,6 +2,11 @@
title: "reference_flat_set"
---
{{< callout type="info">}}
Header: `reference_flat_set.h`
Similar to: `std::set`
{{< /callout >}}
A fixed capacity set based on a sorted vector.
The container stores references to objects, rather than the objects themselves.
The container is an associative lookup table with O(N) insertion and erase, and O(log N) search.

View File

@ -3,6 +3,10 @@ title: "multi_vector"
weight: 2
---
{{< callout type="info">}}
Header: `multi_vector.h`
{{< /callout >}}
A fixed capacity multi-dimensional vector.
For C++11 or greater only.

View File

@ -239,5 +239,5 @@ When opening a new pull request, ensure that you include the following informati
- Do not initiate a pull request until *all* of the units tests pass. See above for information on project files and test scripts.
- Branches should be based on the branch `master`.
`development` can change quite frequently, so I will rebase the your PR against it before merging.
- For formatting help, you can use `clang-format`, or the convenience wrapper `treefmt`. See also [Source formatting]({{% ref "source-formatting.md" %}}).
- For formatting help, you can use `clang-format`, or the convenience wrapper `treefmt`. See also [Source formatting]({{% relref "source-formatting.md" %}}).
- Update the relevent documentation markdown file, found in `etl/docs`.

View File

@ -67,6 +67,11 @@ title = 'Embedded Template Library'
url = "https://github.com/ETLCPP/etl-arduino/archive/master.zip"
weight = 4
[[menus.main]]
name = "Blog"
pageRef = "/docs/blog"
weight = 5
[[menus.main]]
name = "About"
pageRef = "/docs/about"