Finished the implementation section
This commit is contained in:
parent
9879a04efe
commit
a0a5f2f624
@ -6,6 +6,7 @@
|
||||
\usepackage[]{float}
|
||||
\usepackage[]{multicol}
|
||||
\usepackage{mathtools}
|
||||
\usepackage[]{listings}
|
||||
|
||||
\usepackage{tikz}
|
||||
\usepackage{tkz-euclide}
|
||||
@ -21,6 +22,25 @@
|
||||
\pgfdeclarelayer{edgelayer}
|
||||
\pgfsetlayers{edgelayer,nodelayer,main}
|
||||
|
||||
|
||||
\lstset{language=C++,
|
||||
basicstyle=\ttfamily,
|
||||
keywordstyle=\color{blue}\ttfamily,
|
||||
stringstyle=\color{red}\ttfamily,
|
||||
commentstyle=\color{green}\ttfamily,
|
||||
morecomment=[l][\color{magenta}]{\#},
|
||||
emph={std, vector, vec2d, polygon, collision},
|
||||
emphstyle=\color{orange}\ttfamily,
|
||||
numbers=left,
|
||||
morekeywords={uint},
|
||||
numberstyle=\footnotesize,
|
||||
numbersep=5pt,
|
||||
frame=lines,
|
||||
% breaklines=true,
|
||||
% breaklines=true,
|
||||
% postbreak=\raisebox{0ex}[0ex][0ex]{\space\ensuremath{\hookrightarrow}},
|
||||
}
|
||||
|
||||
\graphicspath{{../figures/}{./figures/}}
|
||||
|
||||
\newcommand*{\vv}[1]{\overrightarrow{#1}}
|
||||
|
BIN
figures/bounding_box.pdf
Normal file
BIN
figures/bounding_box.pdf
Normal file
Binary file not shown.
@ -1 +1,178 @@
|
||||
\section{Implementation}
|
||||
|
||||
This section will dive into the actual implementing of the project, trying to
|
||||
describe how the theoretical concepts in section \ref{sec:theory}.
|
||||
|
||||
\subsection{Structure}
|
||||
|
||||
The added code to the project can be divided into three parts
|
||||
\begin{itemize}
|
||||
\item polygon generator;
|
||||
\item collision detection;
|
||||
\item collision resolution.
|
||||
\end{itemize}
|
||||
|
||||
Each part will be explained in the following sub-sections.
|
||||
|
||||
\subsubsection{Polygon generator}
|
||||
Before talking about how we generate polygons, let's first talk about how the
|
||||
polygons are defined in this project.
|
||||
|
||||
In the file \texttt{polygons.h} we define a polygon as
|
||||
|
||||
\begin{lstlisting}[caption={Polygon class (simplified)},label={lst:polygon}]
|
||||
class polygon {
|
||||
std::vector<vec2d> points;
|
||||
|
||||
vec2d center;
|
||||
double angle;
|
||||
|
||||
double inertia;
|
||||
double mass;
|
||||
|
||||
std::vector<vec2d> global_points();
|
||||
|
||||
vec2d speed;
|
||||
double angular_speed;
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
Polygons possesses multiple fields. Firstly we have
|
||||
\lstinline{std::vector<vec2d> points}, a collection of
|
||||
\lstinline{vec2d} objects, which represent the set of ordered points that
|
||||
compose the polygon. Those points are expressed in local coordinates, and the
|
||||
center of mass of the polygon is placed the origin.
|
||||
|
||||
Now that we know how the polygon is composed, we want to move it around the
|
||||
space it lives in, that is the purpose of the \lstinline{vec2d center} field.
|
||||
It represents where the center of mass of the polygon is located in simulation.
|
||||
Since the shapes also rotate, we need to keep track of the rotation of the
|
||||
polygon, hence the use of \lstinline{double angle}. All of these fields are used
|
||||
when computing the result of the method \lstinline{std::vector<vec2d> global_points()}.
|
||||
|
||||
Finally, we have \lstinline{double inertia} and \lstinline{double mass} which,
|
||||
as their name suggest, store the value for the polygon's inertia and mass, that
|
||||
are used to calculate the polygons final speed and angular speed, who are
|
||||
represented by \lstinline{vec2d speed} and \lstinline{double angular_speed}.
|
||||
|
||||
\paragraph{Polygon generator} Now that we know how polygons are represented in
|
||||
our simulation, we can generate them. In \texttt{polygon\_generator.h} (and its
|
||||
implementation file \texttt{polygon\_generator.cc}), there are some functions
|
||||
for that. In Listing \ref{lst:polygon_gen} we can see the signature of the
|
||||
functions that generate
|
||||
\begin{itemize}
|
||||
\item a rectangle of a certain width and height;
|
||||
\item a square of a certain side length;
|
||||
\item a triangle given two side lengths and an angle between them;
|
||||
\item a regular polygon;
|
||||
\item an arbitrary polygon.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Polygon Generator header
|
||||
file},label={lst:polygon_gen}]
|
||||
namespace poly_generate {
|
||||
polygon rectangle(double width, double height);
|
||||
|
||||
inline polygon square(double width) {
|
||||
assert(width > 0);
|
||||
return rectangle(width, width, label);
|
||||
};
|
||||
|
||||
polygon triangle(double side1, double side2, double angle);
|
||||
|
||||
polygon regular(double radius, uint n_sides);
|
||||
|
||||
polygon general(std::vector<vec2d> points);
|
||||
};
|
||||
\end{lstlisting}
|
||||
|
||||
The implementation of those functions are fairly straight forward, they generate
|
||||
the appropriate number of points in the correct place. After what they calculate
|
||||
the mass and subsequent inertia of the polygon as shown in section
|
||||
\ref{sub:moment}.
|
||||
|
||||
\subsubsection{Collision}
|
||||
\label{sub:implementation-collision-detection}
|
||||
|
||||
The algorithm described in section~\ref{sub:vertex-collision} is implemented in
|
||||
\texttt{collision.cc} and exposed to other modules with \texttt{collision.h}.
|
||||
|
||||
The module exposes a collision structure and a collides function.
|
||||
|
||||
\begin{lstlisting}[caption={Collision header file},label={lst:collision}]
|
||||
struct collision {
|
||||
bool collides = false;
|
||||
vec2d impact_point;
|
||||
vec2d n;
|
||||
vec2d overlap;
|
||||
};
|
||||
|
||||
extern collision collides(polygon& p, polygon& q);
|
||||
\end{lstlisting}
|
||||
|
||||
The \lstinline{collides} function takes in two polygons and checks with
|
||||
vertex-collision algorithm whether they collide or not. The result is return
|
||||
through an instance of \lstinline{struct collision}. The \lstinline{bool collides}
|
||||
and \lstinline{vec2d impact_point} fields is self-explanatory. The
|
||||
\lstinline{vec2d n} is the normal vector that pushes \lstinline{polygon& p} away
|
||||
from \lstinline{polygon& q}. Finally, \lstinline{vec2d overlap} is a scalar
|
||||
multiplication of \lstinline{vec2d n}. Where the latter is a normalized vector,
|
||||
the former represents how deep \lstinline{p} is in \lstinline{q}. If we push
|
||||
\lstinline{p} the exact amount of \lstinline{overlap}, then \lstinline{p} would
|
||||
just be touching \lstinline{q} with point \lstinline{impact_point}, and no
|
||||
further overlap would occur.
|
||||
|
||||
In reality, we do not simply push \lstinline{p} by \lstinline{overlap}, but we
|
||||
push \lstinline{p} and \lstinline{q} away from each other proportionally to
|
||||
their mass.
|
||||
|
||||
\paragraph{Collision resolution} In \texttt{polygons.cc}, among other functions,
|
||||
we apply the collision resolution, which simply uses the physics results found
|
||||
in \ref{sub:resolution}.
|
||||
|
||||
\subsection{Optimization}
|
||||
|
||||
In order to optimise the collision detection and to avoid having to do a check
|
||||
whether each vertex of every polygon was colliding with each edge of every other
|
||||
polygon we decided to apply the bounding box acceleration structure.
|
||||
|
||||
The bounding box of an object is, in two dimensions, the rectangle that contains
|
||||
the object and which the sides are aligned with axis of the coordinate system.
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\inputtikz[.7]{bounding_box}
|
||||
\caption{Bounding box example}
|
||||
\label{fig:bounding-box}
|
||||
\end{figure}
|
||||
|
||||
To get the four coordinates that compose, we just perform a linear scan through
|
||||
all the points that compose a polygon, record the minimum and maximum of both
|
||||
they $x$ and $y$ coordinate.
|
||||
|
||||
Once those points have been found, we just perform some basic axis-aligned
|
||||
rectangle collision detection, which is much less computationally expensive than
|
||||
checking each vertex-edge pair.
|
||||
|
||||
The complexity of the collision detection algorithm went from $\mathcal{O}(n^2)$
|
||||
to $\mathcal{O}(n)$, where $n$ is the total number of vertices across all
|
||||
polygons in the simulation.
|
||||
|
||||
|
||||
\subsection{Known issues}
|
||||
|
||||
The simulation has one major flaw, and it is inherent to the last part of
|
||||
section \ref{sub:implementation-collision-detection} where we talk about the
|
||||
\lstinline{overlap} vector: we said that this vector allows to displace both
|
||||
polygons so that and the end of the collision resolution, they are not
|
||||
overlapping anymore. This was done to avoid issues where the collision
|
||||
resolution had taken place in one frame, but the polygons were still overlapping
|
||||
in the following frame, which lead to polygons getting stuck together.
|
||||
|
||||
The unfortunate consequence of such a decision is that, when we start playing
|
||||
around with the restitution coefficient and all the shapes start falling to the
|
||||
ground, the shapes are not able to rest still. Since there is gravity, at each
|
||||
frame their speed get updated to simulate its effect, but since they are lying
|
||||
on the floor, the collision detection algorithm kicks in right away, resulting
|
||||
in the polygon getting pushed completely up, since the ground has infinite mass.
|
||||
This results in the polygon bouncing on the floor instead of lying down.
|
||||
|
71
tikzs/bounding_box.tikz
Normal file
71
tikzs/bounding_box.tikz
Normal file
@ -0,0 +1,71 @@
|
||||
\begin{tikzpicture}
|
||||
\begin{pgfonlayer}{nodelayer}
|
||||
\node [style=none] (0) at (0, -1) {};
|
||||
\node [style=none] (1) at (0, 8) {};
|
||||
\node [style=none] (2) at (-1, 0) {};
|
||||
\node [style=none] (3) at (15, 0) {};
|
||||
\node [style=none] (4) at (4, 4) {};
|
||||
\node [style=none] (5) at (2, 1) {};
|
||||
\node [style=none] (6) at (6, 3) {};
|
||||
\node [style=none] (7) at (9, 3) {};
|
||||
\node [style=none] (8) at (11, 2) {};
|
||||
\node [style=none] (9) at (10.5, 6.5) {};
|
||||
\node [style=none] (10) at (12.75, 5.75) {};
|
||||
\node [style=none] (11) at (13.5, 3.5) {};
|
||||
\node [style=none] (12) at (8, 5) {};
|
||||
\node [style=none] (13) at (2, 4) {};
|
||||
\node [style=none] (14) at (6, 1) {};
|
||||
\node [style=none] (15) at (6, 4) {};
|
||||
\node [style=none] (16) at (8, 2) {};
|
||||
\node [style=none] (17) at (8, 6.5) {};
|
||||
\node [style=none] (18) at (13.5, 6.5) {};
|
||||
\node [style=none] (19) at (13.5, 2) {};
|
||||
\node [style=none] (20) at (2, 0) {};
|
||||
\node [style=none] (21) at (6, 0) {};
|
||||
\node [style=none] (22) at (0, 4) {};
|
||||
\node [style=none] (23) at (0, 1) {};
|
||||
\node [style=none] (24) at (-0.25, 1) {$A_{y_{\min}}$};
|
||||
\node [style=none] (25) at (-0.25, 4) {$A_{y_{\max}}$};
|
||||
\node [style=none] (26) at (2, -0.25) {$A_{x_{\min}}$};
|
||||
\node [style=none] (27) at (6, -0.25) {$A_{x_{\max}}$};
|
||||
\node [style=none] (28) at (8, 0) {};
|
||||
\node [style=none] (29) at (8, -0.25) {$B_{x_{\min}}$};
|
||||
\node [style=none] (30) at (13.5, 0) {};
|
||||
\node [style=none] (31) at (13.5, -0.25) {$B_{x_{\max}}$};
|
||||
\node [style=none] (32) at (0, 2) {};
|
||||
\node [style=none] (33) at (-0.25, 2) {$B_{y_{\min}}$};
|
||||
\node [style=none] (34) at (0, 6.5) {};
|
||||
\node [style=none] (35) at (-0.25, 6.5) {$B_{y_{\max}}$};
|
||||
\node [style=none] (36) at (4, 2.75) {$A$};
|
||||
\node [style=none] (37) at (10.5, 4.5) {$B$};
|
||||
\end{pgfonlayer}
|
||||
\begin{pgfonlayer}{edgelayer}
|
||||
\draw [style=Vector] (0.center) to (1.center);
|
||||
\draw [style=Vector] (2.center) to (3.center);
|
||||
\draw (5.center) to (4.center);
|
||||
\draw (4.center) to (6.center);
|
||||
\draw (6.center) to (5.center);
|
||||
\draw (12.center) to (9.center);
|
||||
\draw (9.center) to (10.center);
|
||||
\draw (10.center) to (11.center);
|
||||
\draw (11.center) to (8.center);
|
||||
\draw (8.center) to (7.center);
|
||||
\draw (7.center) to (12.center);
|
||||
\draw [style=Dashed] (13.center) to (5.center);
|
||||
\draw [style=Dashed] (5.center) to (14.center);
|
||||
\draw [style=Dashed] (14.center) to (15.center);
|
||||
\draw [style=Dashed] (15.center) to (13.center);
|
||||
\draw [style=Dashed] (16.center) to (17.center);
|
||||
\draw [style=Dashed] (17.center) to (18.center);
|
||||
\draw [style=Dashed] (18.center) to (19.center);
|
||||
\draw [style=Dashed] (19.center) to (16.center);
|
||||
\draw [style=Dotted] (19.center) to (30.center);
|
||||
\draw [style=Dotted] (16.center) to (28.center);
|
||||
\draw [style=Dotted] (16.center) to (32.center);
|
||||
\draw [style=Dotted] (13.center) to (22.center);
|
||||
\draw [style=Dotted] (17.center) to (34.center);
|
||||
\draw [style=Dotted] (5.center) to (20.center);
|
||||
\draw [style=Dotted] (5.center) to (23.center);
|
||||
\draw [style=Dotted] (14.center) to (21.center);
|
||||
\end{pgfonlayer}
|
||||
\end{tikzpicture}
|
Loading…
Reference in New Issue
Block a user