(Using Blender 3.6.5)
Documentation:
Preliminary remarks:
As very well explained by Mr A in his proposal, Principal Component Analysis is a well adapted framework for this class of problems. One part of the burden of the general theory he recapped can be alleviate in the present case thanks to the coplanar nature of the points in a same group. Furthermore because only one vector, the normal, is sought, the problem can even be reduced to solving a homogeneous linear equation.
However in the following, classical formulations from this mathematical field are not used because Blender toolbox lack of linear algebra components, mostly matrices. Blender vectors are only geometrical, restricted to 3D space. The assumed choice is to match the algebra available in Geometry Nodes, even if the formulation might seem out of phase for experts. This way graph implementation is following closely mathematical developments.
Mathematical formulation:
Only the points in a same group, sharing the same Group Index and the same normal, are considered. Managing more than one group is detailed in the implementation.
Let $n$ be the number of points, $M_{i, 1\leq i \leq n}$ the points, $C$ the face centre the points were connected to, $\vec{n}$ the sought normal. If $C$ were not available, it would have be replaced by the points barycentre as in lemon proposal.
$C$ and $\vec{n}$ are defining the plane the points lay on, because $\vec{CM_i}$ is perpendicular to $\vec{n}$. Using the dot product "$\cdot$", it yields for every point $i$ :
$$\vec{CM_i} \cdot \vec{n} = 0 \label{CMdorN} \tag{1}$$
All these scalar equations are collected in a matrix form, with matrix $Q$ assembled from each vector $\vec{CM_i}$ written as a line (i.e. the transpose of a column vector) and vector $N$ assembled from the coordinates of $\vec{n}$.
$Q$ is defined as:
$$ Q = \left[ {\begin{array}{c}
\vec{CM_1} \\
\vec{CM_2} \\
\vdots \\
\vec{CM_n}
\end{array}} \right]
= \left[ {\begin{array}{ccc}
x_1 & y_1 & z_1 \\
x_2 & y_2 & z_2 \\
\vdots & \vdots & \vdots \\
x_n & y_n & z_n
\end{array}} \right] \tag{2}$$
where $x_i, y_i, z_i$ are the coordinates of $\vec{CM_i}$.
$N$ is defined as:
$$N = \left[ {\begin{array}{c}
n_x \\
n_y \\
n_z
\end{array}} \right] \tag{3}$$
Equation ($\ref{CMdorN}$) is then rewritten as:
$$ Q N = 0_n \label{QNeq0} \tag{4}$$
where $0_n$ is the column vector of size $n$ full of zeros.
As $Q$ is rectangular, and not square, the "best" solution $N$ that could be achieved is the one minimizing the norm of $Q N$. It is to notice that if some points $M_i$ are out of the plane, the right-hand side of Equation ($\ref{QNeq0}$) is not equal to zero. But if it is small, the minimization of the norm of $Q N$ returns a satisfactory "mean" normal.
Let $Q^T$ be the transpose of $Q$, such that:
$$ Q^T = \left[ {\begin{array}{cccc}
x_1 & x_2 & \ldots & x_n \\
y_1 & y_2 & \ldots & y_n \\
z_1 & z_2 & \ldots & z_n
\end{array}} \right] \tag{5}$$
$N$ is then sought as solution of the homogeneous linear equation:
$$ Q^{T}Q N = 0_3 \label{QtQNeq0} \tag{6}$$
with:
$$ Q^{T}Q = \left[ {\begin{array}{ccc}
\sum_{i=1}^n x_i^2 &
\sum_{i=1}^n x_i y_i &
\sum_{i=1}^n x_i z_i \\
\sum_{i=1}^n y_i x_i &
\sum_{i=1}^n y_i^2 &
\sum_{i=1}^n y_i z_i \\
\sum_{i=1}^n z_i x_i &
\sum_{i=1}^n z_i y_i &
\sum_{i=1}^n z_i^2
\end{array}} \right] \tag{7}$$
$Q^{T}Q$ is called the covariance matrix.
Let $\vec{V_x}, \vec{V_y}, \vec{V_z}$ be vectors defined as:
$$ \vec{V_x} = \left( {\begin{array}{c}
\sum_{i=1}^n x_i^2 \\
\sum_{i=1}^n x_i y_i \\
\sum_{i=1}^n x_i z_i
\end{array}} \right),
\vec{V_y} = \left( {\begin{array}{c}
\sum_{i=1}^n y_i x_i \\
\sum_{i=1}^n y_i^2 \\
\sum_{i=1}^n y_i z_i
\end{array}} \right),
\vec{V_z} = \left( {\begin{array}{c}
\sum_{i=1}^n z_i x_i \\
\sum_{i=1}^n z_i y_i \\
\sum_{i=1}^n z_i^2
\end{array}} \right) \tag{8}$$
Equation ($\ref{QtQNeq0}$) is then rewritten as:
$$ \left[ {\begin{array}{c}
\vec{V_x} \\
\vec{V_y} \\
\vec{V_z}
\end{array}} \right] N = 0 \label{VNeq0} \tag{9}$$
In vectorial formalism, Equation ($\ref{VNeq0}$) reads:
$$ \left\{ {\begin{array}{c}
\vec{V_x} \cdot \vec{n} = 0 \\
\vec{V_y} \cdot \vec{n} = 0 \\
\vec{V_z} \cdot \vec{n} = 0
\end{array}} \right. \label{VxyzdotNeq0} \tag{10}$$
It states that $\vec{n}$ should be perpendicular to the space spanned by the three vectors $(\vec{V_x}, \vec{V_y}, \vec{V_z})$.
As all the points $M_i$ and $C$ are coplanar, it is known that this space is degenerated of dimension 2, even of dimension 1 if the points are aligned. It means that without loosing generality, one might assume that $\vec{V_z}$ is a linear combination of the two others:
$$ \vec{V_z} = \alpha \vec{V_x} + \beta \vec{V_y}\tag{11}$$
So:
$$ \vec{V_z} \cdot \vec{n} = \alpha \vec{V_x} \cdot \vec{n} +
\beta \vec{V_y} \cdot \vec{n}
\tag{12}$$
The last line of Equation ($\ref{VxyzdotNeq0}$) is thus just a consequence of the first two lines. Remains only:
$$ \left\{ {\begin{array}{c}
\vec{V_x} \cdot \vec{n} = 0 \\
\vec{V_y} \cdot \vec{n} = 0
\end{array}} \right. \label{VxydotNeq0} \tag{13}$$
It states that $\vec{n}$ should be perpendicular to $\vec{V_x}$ and $\vec{V_y}$. As a consequence $\vec{n}$ should be collinear to the cross product "$\times$" of both:
$$ \vec{n} \ \| \ \vec{V_x} \times \vec{V_y} \label{VxCrossVy} \tag{14}$$
If the points are aligned, only one line of Equation ($\ref{VxyzdotNeq0}$) remains, and $\vec{n}$ is undetermined.
To not rely on an arbitrary choice of the two vectors in Equation ($\ref{VxCrossVy}$), Gram-Schmidt process is use to orthonormalize the set of vectors $(\vec{V_x}, \vec{V_y}, \vec{V_z})$:
- The first vector labelled $\vec{e_1}$ is chosen among $(\vec{V_x}, \vec{V_y}, \vec{V_z})$ as the one maximizing its norm ; it is normalized.
- Then all three vectors are orthogonalized to $\vec{e_1}$ by:
$$ \vec{U} = \vec{V} - (\vec{V} \cdot \vec{e_1}) \ \vec{e_1} \tag{15}$$
- The second vector labelled $\vec{e_2}$ is chosen among $(\vec{U_x}, \vec{U_y}, \vec{U_z})$ as the one maximizing its norm ; it is normalized.
Finally, the normal $\vec{n}$ is calculated from:
$$\vec{n} = \vec{e_1} \times \vec{e_2} \tag{16}$$
Implementation:
Step 1: Assembly of the covariance matrix
Objective: Compute the set of vectors $(\vec{V_x}, \vec{V_y}, \vec{V_z})$.
1.1. Computation of
$$\vec{CM_i} = \left( {\begin{array}{c}
x_i \\
y_i \\
z_i
\end{array}} \right)$$
1.2. Computation of the vector
$$ x_i \left( {\begin{array}{c}
x_i \\
y_i \\
z_i
\end{array}} \right)
= \left( {\begin{array}{c}
x_i^2 \\
x_i y_i \\
x_i z_i
\end{array}} \right) $$
1.3. Summation of the output of step 1.2 using the Group ID to accumulate the values in different bins, one per Group ID. So each sum is restricted to the points sharing the same Group ID, yielding:
$$\vec{V_x} = \left( {\begin{array}{c}
\sum_{i=1}^n x_i^2 \\
\sum_{i=1}^n x_i y_i \\
\sum_{i=1}^n x_i z_i
\end{array}} \right)$$
1.4. Computation of the vector
$$ y_i \left( {\begin{array}{c}
x_i \\
y_i \\
z_i
\end{array}} \right)
= \left( {\begin{array}{c}
y_i x_i \\
y_i^2 \\
y_i z_i
\end{array}} \right) $$
1.5. Summation of the output of step 1.4 yielding:
$$\vec{V_y} = \left( {\begin{array}{c}
\sum_{i=1}^n y_i x_i \\
\sum_{i=1}^n y_i^2 \\
\sum_{i=1}^n y_i z_i
\end{array}} \right)$$
1.6. Computation of the vector
$$ z_i \left( {\begin{array}{c}
x_i \\
y_i \\
z_i
\end{array}} \right)
= \left( {\begin{array}{c}
z_i x_i \\
z_i y_i \\
z_i^2
\end{array}} \right) $$
1.7. Summation of the output of step 1.6 yielding:
$$\vec{V_z} = \left( {\begin{array}{c}
\sum_{i=1}^n z_i x_i \\
\sum_{i=1}^n z_i y_i \\
\sum_{i=1}^n z_i^2
\end{array}} \right)$$
Step 2: Gram-Schmidt normalization
Objective: Find among $(\vec{V_x}, \vec{V_y}, \vec{V_z})$ the vector with maximal norm, and normalize it.
2.1. Computation of the three norms.
2.2. Sorting of cases following the pseudo-code:
if |Vx| > |Vy|:
if |Vx| > |Vz|:
return Vx
else
return Vz
else
if |Vy| > |Vz|:
return Vy
else
return Vz
2.3. Normalization:
$$ \vec{e} = {\vec{V}} \ / \ {\|\vec{V}\|} $$
Step 3: Gram-Schmidt orthogonalization
Objective: Keep only the part of vector $\vec{V}$ perpendicular to vector $\vec{e}$.
3.1. Computation of the projection of $\vec{V}$ on $\vec{e}$ as $\vec{V} \cdot \vec{e}$.
3.2. Computation of the part of $\vec{V}$ aligned with $\vec{e}$ as $(\vec{V} \cdot \vec{e}) \ \vec{e}$.
3.3. Computation of the part of $\vec{V}$ perpendicular to $\vec{e}$ by removing its part aligned with $\vec{e}$.
Step 4: Computation of the normal
Objective: Find vectors $\vec{e_1}$ and $\vec{e_2}$ by an incomplete orthonormalization of $(\vec{V_x}, \vec{V_y}, \vec{V_z})$, then compute the normal.
4.1. Computation of $\vec{e_1}$.
4.2. Subtraction of direction $\vec{e_1}$ from $(\vec{V_x}, \vec{V_y}, \vec{V_z})$.
4.3. Computation of $\vec{e_2}$.
4.4. Computation of the normal as $\vec{n} = \vec{e_1} \times \vec{e_2}$.
Result:
Resources: