A service by

The central philosophy of Æxis (anglicized as Aexis) is the marriage between logic and art. Aesthetics on an axis. Few things capture this unity better than Bézier curves. They are highly customizable curves that, when strung together, give you the means to create arbitrary, smooth shapes. They conform to their rigid mathematical definition, and yet bend to the creative will of the hand that draws them.

Another important pillar of graphic design is vector-based imagery. Many pictures ought to be scalable and zoomable—some images, more than others. Below is a portfolio piece by Æxis, showing the power of using LaTeX to mathematically render a fractal as a .svg file, allowing for an "endless" zoom.

Instead of creating the Fibonacci spiral the easy way, by concatenating quarter circles, we used cubic Bézier curves to approximate them, allowing us to denote their anchor and control points in the diagram. Art demands imperfection. The final zoom reaches 2,699x. This is for a Fibonacci tiling with twenty-one iterations. More iterations were not possible due to exceeding the max size limit in TeX. Below you can see the LaTeX code used to recursively generate the above image:

fibonacci_tiling.tex

  \documentclass[margin=2mm]{standalone}
  \usepackage{tikz}
  \usepackage{xparse}
  \usepackage{xcolor}
  \usepackage{graphicx}
  \usepackage{adjustbox}
  \usepackage{amssymb}

  % Colors set to the brand's palette
  \definecolor{fibwhite}{HTML}{FFFFFF}
  \definecolor{baseblue}{HTML}{6C63FF}
  \definecolor{fiblightgray}{HTML}{F2F2F2}
  \definecolor{fibgray}{HTML}{D6D6E3}

  % I used a lighter blue to make the dashed lines more visible
  \colorlet{fibblue}{baseblue!60!white}

  \ExplSyntaxOn

  % Constant for the best Bezier approximation of a quarter circle
  \fp_new:N \c__fibtile_k_fp
  \fp_set:Nn \c__fibtile_k_fp { 0.27614 }

  % Ink scaling constant and variable in order to keep the Bezier notation well-sized relative to the tile it is on
  \fp_new:N \c_fibtile_ink_base_fp
  \fp_set:Nn \c_fibtile_ink_base_fp { 0.00665 }
  \fp_new:N \l_fibtile_local_scale_fp

  % Variables for rotation and coloring
  \int_new:N \l_fibtile_rotation_int
  \tl_new:N \l_fibtile_current_color_tl

  % #1, #2, #3 and #4 corresponds to x- and y-coordinates of the
  % start- and end-points of the Bezier curve, respectively.
  % #5 corresponds to the reverse index
  \cs_new:Nn \fibtile_draw_bezier_curve:nnnnn
  {
  \fp_set:Nn \l_tmpa_fp { (#3) - (#1) }
  \fp_set:Nn \l_tmpb_fp { (#4) - (#2) }

  % Defining the x- and y-coordinates of the control points
  \fp_set:Nn \l_tmpc_fp { #1 + \c__fibtile_k_fp * (\l_tmpa_fp - (-1) * \l_tmpb_fp) }
  \fp_set:Nn \l_tmpd_fp { #2 + \c__fibtile_k_fp * ((-1) * \l_tmpa_fp + \l_tmpb_fp) }
  \fp_set:Nn \l_tmpe_fp { #3 + \c__fibtile_k_fp * (-\l_tmpa_fp - (-1) * \l_tmpb_fp) }
  \fp_set:Nn \l_tmpf_fp { #4 + \c__fibtile_k_fp * ((-1) * \l_tmpa_fp - \l_tmpb_fp) }

  % Drawing the Bezier curve
  \draw [black, line~width=0.8 * \fp_use:N \l_fibtile_local_scale_fp pt]
  (#1, #2) .. controls (\fp_use:N \l_tmpc_fp, \fp_use:N \l_tmpd_fp)
  ~and~ (\fp_use:N \l_tmpe_fp, \fp_use:N \l_tmpf_fp) .. (#3, #4);

  % Drawing the control lines
  \draw [black!55, line~width= 1.2 * \fp_use:N \l_fibtile_local_scale_fp pt, dash~pattern=on~ 8 * \fp_use:N
  \l_fibtile_local_scale_fp pt~off~ 4 * \fp_use:N \l_fibtile_local_scale_fp pt]
  (#1, #2) -- (\fp_use:N \l_tmpc_fp, \fp_use:N \l_tmpd_fp) -- (\fp_use:N \l_tmpe_fp, \fp_use:N \l_tmpf_fp) -- (#3, #4);

  % Recording the anchor points
  \coordinate~ (P_#5_0-3)~ at~ (#3,~ #4);

  % Recording the control points
  \coordinate~ (P_#5_1)~ at~ (\fp_use:N \l_tmpe_fp,~ \fp_use:N \l_tmpf_fp);
  \coordinate~ (P_#5_2)~ at~ (\fp_use:N \l_tmpc_fp,~ \fp_use:N \l_tmpd_fp);
  }

  % Creating the variables necessary for the loop that generates the Fibonacci sequence
  \int_new:N \l_fibtile_vara_int
  \int_new:N \l_fibtile_varb_int
  \int_new:N \l_fibtile_vartemp_int

  \seq_new:N \l_fibtile_numbers_seq

  % I used the sliding window algorithm because recursion is very slow in TeX
  \cs_new:Nn \fibtile_generate_seq:n
  {
  \int_set:Nn \l_fibtile_vara_int { 1 }
  \int_set:Nn \l_fibtile_varb_int { 1 }
  \seq_clear:N \l_fibtile_numbers_seq
  \int_step_inline:nn { #1 }
  {
  \seq_put_right:NV \l_fibtile_numbers_seq \l_fibtile_vara_int
  \int_set_eq:NN \l_fibtile_vartemp_int \l_fibtile_varb_int
  \int_set:Nn \l_fibtile_varb_int { \l_fibtile_vara_int + \l_fibtile_varb_int }
  \int_set_eq:NN \l_fibtile_vara_int \l_fibtile_vartemp_int
  }
  }

  %Creating the variables necessary to keep track of the Fibonacci tiling's corners
  \int_new:N \l_fibtile_xmin_int
  \int_new:N \l_fibtile_xmax_int
  \int_new:N \l_fibtile_ymin_int
  \int_new:N \l_fibtile_ymax_int

  \cs_new:Nn \fibtile_draw_tiling:n
  {
  \fibtile_generate_seq:n { #1 }

  \seq_map_indexed_inline:Nn \l_fibtile_numbers_seq
  {
  % The ink is scaled relative to the iteration, but the rightmost coefficient is there to account for
  % the total canvas scaling that happens based on the total number of iterations
  % (see the adjustbox in the \NewDocumentCommand at the end)

  \fp_set:Nn \l_fibtile_local_scale_fp { ##2 * \c_fibtile_ink_base_fp * 1.618034^(12 - #1) }

  % I map colors to the (n - i) mod 4 value for the iteration, so that it cycles through colors
  % along the reverse index, thus giving the largest squares a consistent appearance for all inputs
  \tl_set:Nn \l_fibtile_current_color_tl
  {
  \int_case:nn { \int_mod:nn { #1 - ##1 } { 4 } }
  {
  { 0 } { fibgray }
  { 1 } { fibblue }
  { 2 } { fiblightgray }
  { 3 } { fibwhite }
  }
  }

  \int_case:nn { \int_mod:nn { ##1 } { 4 } }
  {
  { 1 }
  {
  % The innermost square must be dealt with separately, as it is the seed.
  \int_compare:nTF { ##1 == 1 }
  {
  \draw[black, fill=\l_fibtile_current_color_tl, line~width=\fp_eval:n{0.4 * \l_fibtile_local_scale_fp}pt]
  (0,0) rectangle (##2, ##2);

  % I draw the first curve from the top-left to bottom-right corner to match the counter-clockwise rotation of my tiling
  \fibtile_draw_bezier_curve:nnnnn { 0 } { ##2 } { ##2 } { 0 }
  { \int_eval:n { #1 - ##1 } }

  \int_set:Nn \l_fibtile_xmax_int { ##2 }
  \int_set:Nn \l_fibtile_ymax_int { ##2 }

  % Recording the innermost anchor point
  \coordinate~ (P_#1_0-3)~ at~ (0, \l_fibtile_ymax_int);
  }
  {
  \draw[black, fill=\l_fibtile_current_color_tl, line~width=\fp_eval:n{0.4 * \l_fibtile_local_scale_fp}pt]
  (\l_fibtile_xmin_int, \l_fibtile_ymin_int - ##2) rectangle (\l_fibtile_xmax_int, \l_fibtile_ymin_int);

  \int_set:Nn \l_fibtile_ymin_int { \l_fibtile_ymin_int - ##2 }

  \fibtile_draw_bezier_curve:nnnnn { \l_fibtile_xmin_int } { \l_fibtile_ymin_int + ##2 } { \l_fibtile_xmax_int } {
  \l_fibtile_ymin_int }
  { \int_eval:n { #1 - ##1 } }
  }
  }
  { 2 }
  {
  \draw[black, fill=\l_fibtile_current_color_tl, line~width=\fp_eval:n{0.4 * \l_fibtile_local_scale_fp}pt]
  (\l_fibtile_xmax_int, \l_fibtile_ymin_int) rectangle (\l_fibtile_xmax_int + ##2, \l_fibtile_ymax_int);

  \int_set:Nn \l_fibtile_xmax_int { \l_fibtile_xmax_int + ##2 }

  \fibtile_draw_bezier_curve:nnnnn { \l_fibtile_xmax_int - ##2 } { \l_fibtile_ymin_int } { \l_fibtile_xmax_int } {
  \l_fibtile_ymax_int }
  { \int_eval:n { #1 - ##1 } }
  }
  { 3 }
  {
  \draw[black, fill=\l_fibtile_current_color_tl, line~width=\fp_eval:n{0.4 * \l_fibtile_local_scale_fp}pt]
  (\l_fibtile_xmin_int, \l_fibtile_ymax_int) rectangle (\l_fibtile_xmax_int, \l_fibtile_ymax_int + ##2);

  \int_set:Nn \l_fibtile_ymax_int { \l_fibtile_ymax_int + ##2 }

  \fibtile_draw_bezier_curve:nnnnn { \l_fibtile_xmax_int } { \l_fibtile_ymax_int - ##2 } { \l_fibtile_xmax_int - ##2 } {
  \l_fibtile_ymax_int }
  { \int_eval:n { #1 - ##1 } }
  }
  { 0 }
  {
  \draw[black, fill=\l_fibtile_current_color_tl, line~width=\fp_eval:n{0.4 * \l_fibtile_local_scale_fp}pt]
  (\l_fibtile_xmin_int - ##2, \l_fibtile_ymin_int) rectangle (\l_fibtile_xmin_int, \l_fibtile_ymax_int);

  \int_set:Nn \l_fibtile_xmin_int { \l_fibtile_xmin_int - ##2 }

  \fibtile_draw_bezier_curve:nnnnn { \l_fibtile_xmin_int + ##2 } { \l_fibtile_ymax_int } { \l_fibtile_xmin_int } {
  \l_fibtile_ymin_int }
  { \int_eval:n { #1 - ##1 } }
  }
  }

  % I put my preferred version of Binet's formula in the final, largest square
  \int_compare:nT { ##1 == #1 }
  {
  % Calculating the positional variables based on the mod 4 value of the final iteration
  \int_case:nn { \int_mod:nn { ##1 } { 4 } }
  {
  { 1 }
  {
  \fp_set:Nn \l_tmpa_fp { \l_fibtile_xmin_int + ##2 * 0.7284 }
  \fp_set:Nn \l_tmpb_fp { \l_fibtile_ymin_int + ##2 * 0.5320 }
  }
  { 2 }
  {
  \fp_set:Nn \l_tmpa_fp { \l_fibtile_xmax_int - ##2 * 0.5320 }
  \fp_set:Nn \l_tmpb_fp { \l_fibtile_ymin_int + ##2 * 0.7284 }
  }
  { 3 }
  {
  \fp_set:Nn \l_tmpa_fp { \l_fibtile_xmax_int - ##2 * 0.7284 }
  \fp_set:Nn \l_tmpb_fp { \l_fibtile_ymax_int - ##2 * 0.5320 }
  }
  { 0 }
  {
  \fp_set:Nn \l_tmpa_fp { \l_fibtile_xmin_int + ##2 * 0.5320 }
  \fp_set:Nn \l_tmpb_fp { \l_fibtile_ymax_int - ##2 * 0.7284 }
  }
  }

  \node[
  scale = \fp_eval:n { \l_fibtile_local_scale_fp * 1.3 },
  transform~shape,
  rotate = - \l_fibtile_rotation_int
  ] at ( \fp_use:N \l_tmpa_fp, \fp_use:N \l_tmpb_fp )
  { $ F\sb {n} = \frac{\varphi^n - (-\varphi)^{-n}}{\sqrt{5}} $ };
  }
  }
  }

  % At every iteration the tiling grows in a direction rotated 90 degrees from the direction along which it grew last.
  % At iteration values divisible by four, the tiling puts its largest square to the left, which looks the best.
  % Therefore, I counter-rotate the entire tiling based on the i mod 4 value
  \cs_new:Nn \fibtile_rotate_tiling:n
  {
  \int_set:Nn \l_fibtile_rotation_int { \int_mod:nn { #1 } { 4 } * -90 }

  \begin{scope}[rotate = \l_fibtile_rotation_int]
  \fibtile_draw_tiling:n { #1 }
  \end{scope}
  }

  \cs_new:Nn \fibtile_draw_points:n
  {
  \fibtile_generate_seq:n { #1 }

  \seq_map_indexed_inline:Nn \l_fibtile_numbers_seq
  {
  \fp_set:Nn \l_fibtile_local_scale_fp { ##2 * \c_fibtile_ink_base_fp * 1.618034^(12 - #1) }

  % tmpa corresponds to the label position for the anchor points and the first control points
  % (except for the innermost anchor point)
  \tl_set:Ne \l_tmpa_tl
  {
  \int_case:nn { \int_mod:nn { \int_eval:n { #1 - ##1 } } { 4 } }
  {
  { 0 } { left }
  { 1 } { above }
  { 2 } { right }
  { 3 } { below }
  }
  }

  % tmpb corresponds to the label position of the second control points
  \tl_set:Nn \l_tmpb_tl
  {
  \int_case:nn { \int_mod:nn { \int_eval:n { #1 - ##1 } } { 4 } }
  {
  { 0 } { above }
  { 1 } { right }
  { 2 } { below }
  { 3 } { left }
  }
  }

  % The innermost anchor point
  \int_compare:nT { ##1 == 1 }
  {
  \fill[black]
  (P_\int_eval:n { #1 } _0-3)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpb_tl
  ] {$P \sb {3, \int_eval:n { #1 }}$};
  }

  % The rest of the anchor points;
  % All of them, except for P_0,0, are showing the equality between the last anchor point of one iteration with the first
  of another.
  \int_compare:nTF { ##1 == #1 }
  {
  \fill[black]
  (P_\int_eval:n { #1 - ##1 }_0-3)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpa_tl
  ] {$P \sb {0, \int_eval:n { #1 - ##1 }}$};
  }
  {
  % I change the order of the equality for when the label is below, to make it read in the right direction
  \tl_if_eq:NnTF \l_tmpa_tl { below }
  {
  \fill[black]
  (P_\int_eval:n { #1 - ##1 }_0-3)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpa_tl
  ] {$ P \sb {0, \int_eval:n { #1 - ##1 + 1 }} = P \sb {3, \int_eval:n { #1 - ##1 }}$};
  }
  {
  \fill[black]
  (P_\int_eval:n { #1 - ##1 }_0-3)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpa_tl
  ] {$P \sb {3, \int_eval:n { #1 - ##1 }} = P \sb {0, \int_eval:n { #1 - ##1 + 1 }}$};
  }
  }

  % Control points
  \draw[fill=white, line~width= 0.4 * \fp_use:N \l_fibtile_local_scale_fp pt]
  (P_\int_eval:n { #1 - ##1 }_1)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpa_tl
  ] {$P \sb {1, \int_eval:n { #1 - ##1 }}$};

  \draw[fill=white, line~width= 0.4 * \fp_use:N \l_fibtile_local_scale_fp pt]
  (P_\int_eval:n { #1 - ##1 }_2)~ circle~ (\fp_eval:n{2 * \l_fibtile_local_scale_fp}pt)
  node[
  scale = \fp_use:N \l_fibtile_local_scale_fp,
  \l_tmpb_tl
  ] {$P \sb {2, \int_eval:n { #1 - ##1 }}$};
  }
  }

  \NewDocumentCommand{\FibonacciTiling}{ O{1} m }
  {
  % When drawing with points as the unit, I found that #2 = 12 looked the best.
  % Since the side length of the square of each iteration is bigger/smaller by a factor roughly equal to the golden
  ratio,
  % the points are scaled like this
  \begin{adjustbox}{scale=#1}
  \begin{tikzpicture}[
  x = \fp_eval:n { 1.618034^(12 - #2) } pt,
  y = \fp_eval:n { 1.618034^(12 - #2) } pt
  ]

  \fibtile_rotate_tiling:n { #2 }

  \fibtile_draw_points:n { #2 }

  \end{tikzpicture}
  \end{adjustbox}
  }

  \ExplSyntaxOff

  \begin{document}

  \FibonacciTiling[2]{21}

  \end{document}
  

Æxis also made all the logos of Kvåle Solutions, ensuring that they were both crisp .svgs and in-line with the larger brand.

TeXtract Logo Føx Logo Æxis Logo Annex2 Logo

If your task requires more than just graphic design, like typesetting, web development or various language services, then you can keep it all within Kvåle Solutions. Our different services will seamlessly collaborate to solve all aspects of your task, all while you deal with a single point of contact (SPOC).