Optical Illusions
Generating a Pair of Optical Illusions in SVG Format
main.cpp
Go to the documentation of this file.
1 
4 
5 // MIT License
6 //
7 // Copyright (c) 2020 Ian Parberry
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to
11 // deal in the Software without restriction, including without limitation the
12 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 // sell copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 // IN THE SOFTWARE.
26 
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29 
30 #include <stdio.h>
31 #include <string>
32 
33 const float PI = 3.14159265358979323846f;
34 
36 // Helper fuctions.
37 
38 #pragma region helpers
39 
49 
50 bool OpenSVG(FILE*& output, const std::string& fname, size_t w, size_t h){
51  const std::string s = fname + ".svg";
52 
53 #ifdef _MSC_VER //Visual Studio
54  fopen_s(&output, s.c_str(), "wt");
55 #else
56  output = fopen(s.c_str(), "wt");
57 #endif
58 
59  if(output != nullptr){ //write header to file
60  fprintf(output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); //xml tag
61 
62  fprintf(output, "<svg width=\"%lu\" height=\"%lu\" ", w, w); //svg tag
63  fprintf(output, "viewBox=\"0 0 %lu %lu\" ", w, w);
64  fprintf(output, "xmlns=\"http://www.w3.org/2000/svg\">\n");
65 
66  fprintf(output, "<!-- Created by Ian Parberry -->\n"); //author comment
67 
68  return true; //success
69  } //if
70 
71  return false; //failure
72 } //OpenSVG
73 
78 
79 void CloseSVG(FILE*& output){
80  if(output != nullptr){
81  fprintf(output, "</svg>\n"); //close the svg tag
82  fclose(output);
83  output = nullptr;
84  } //if
85 } //CloseSVG
86 
87 #pragma endregion helpers
88 
90 // Optical Illusion 1 - circles of squares.
91 
92 #pragma region Illusion1
93 
112 
113 void DrawCircleOfSquares(FILE* output, size_t cx, size_t cy, float r,
114  size_t sw, bool parity)
115 {
116  //number of squares on circle, must be even
117  const size_t n = (size_t)ceil((2*PI*r)/(1.5f*sw)) & 0xFFFFFFFE;
118 
119  const float dtheta = 2*PI/n; //angle delta to next square
120  float theta = 0; //angle to current square
121 
122  for(size_t i=0; i<n; i++){ //for each square
123  const float x = r*cosf(theta); //square center x
124  const float y = r*sinf(theta); //square center y
125  const float phi = 12*(parity? 1: -1) + 180*theta/PI; //square orientation
126 
127  fprintf(output, "<g transform=\"translate(%0.1f %0.1f)", x + sw/2, y + sw/2); //translate
128  fprintf(output, "rotate(%0.1f %lu %lu)\">", phi, cx, cy); //rotate
129  fprintf(output, "<rect width=\"%lu\" height=\"%lu\" ", sw, sw); //rectangle
130  if(i&1)fprintf(output, "class=\"b\""); //black
131  else fprintf(output, "class=\"w\""); //white
132  fprintf(output, "/>"); //close rect tag
133  fprintf(output, "</g>\n"); //close group
134 
135  theta += dtheta; //next square
136  } //for
137 } //DrawCircleOfSquares
138 
158 
159 void OpticalIllusion1(const std::string& fname, size_t w, size_t n,
160  float r0, float dr, size_t sw, const char dark[], const char light[],
161  const char bgclr[])
162 {
163  const size_t cx = w/2 - sw/2; //center x coordinate
164  const size_t cy = cx; //center y coordinate
165  FILE* output = nullptr; //output file pointer
166 
167  if(OpenSVG(output, fname, w, w)){
168  //style tag
169  fprintf(output, "<style>"); //open style tag
170  fprintf(output, "rect{fill:none;stroke-width:3}"); //rectangle
171  fprintf(output, "rect.b{x:%lu;y:%lu;stroke:%s;}", cx, cy, dark); //black rectangle
172  fprintf(output, "rect.w{x:%lu;y:%lu;stroke:%s;}", cx, cy, light); //white rectangle
173  fprintf(output, "</style>\n"); //close style tag
174 
175  //background
176  fprintf(output, "<rect width=\"%lu\" height=\"%lu\" ", w, w); //rectangle
177  fprintf(output, "style=\"fill:%s\"/>\n", bgclr); //fill
178 
179  for(size_t i=0; i<n; i++) //for each circle of squares
180  DrawCircleOfSquares(output, cx, cy, r0 + i*dr, sw, i&1); //draw it
181 
182  CloseSVG(output); //clean up and exit
183  } //if
184 } //OpticalIllusion1
185 
186 #pragma endregion Illusion1
187 
189 // Optical Illusion 2 - circles of circles of ellipses.
190 
191 #pragma region Illusion2
192 
203 
204 void SelectEllipseColor(FILE* output, size_t i, bool parity){
205  const size_t j = i%4;
206  if((parity && j == 0) || (!parity && j == 2))
207  fprintf(output, "class=\"b\""); //black ellipse
208  else if((parity && j == 2) || (!parity && j == 0))
209  fprintf(output, "class=\"w\""); //white ellipse
210 } //SelectEllipseColor
211 
231 
232 void DrawCircleOfEllipses(FILE* output, size_t cx, size_t cy, float r,
233  float r0, float r1, size_t n, float theta, float dtheta, bool parity,
234  size_t flip=999999)
235 {
236  for(size_t i=0; i<n; i++){ //for each ellipse
237  const float x = r*cosf(theta); //ellipse center x
238  const float y = r*sinf(theta); //ellipse center y
239  const float phi = 90 + 180*theta/PI; //ellipse orientation
240 
241  fprintf(output, "<g transform=\"translate(%0.1f %0.1f)", x, y); //translate
242  fprintf(output, "rotate(%0.1f %lu %lu)\">", phi, cx, cy); //rotate
243  fprintf(output, "<ellipse cx=\"400\" cy=\"400\" "); //ellipse
244  fprintf(output, "rx=\"%0.1f\" ry=\"%0.1f\" ", r0, r1); //ellipse
245 
246  SelectEllipseColor(output, i, parity);
247 
248  fprintf(output, "/>"); //close rect tag
249  fprintf(output, "</g>\n"); //close group
250 
251  theta += dtheta; //next ellipse
252  if(i == flip)parity = !parity; //flip parity if we need to
253  } //for
254 } //DrawCircleOfEllipses
255 
292 
293 void DrawTripleCircle(FILE* output, size_t cx, size_t cy, float r,
294  float r0, float r1, size_t n, bool flip=false)
295 {
296  const float dtheta = PI/n; //angle delta to next ellipse
297  float theta = (flip? PI: -PI)/2; //angle to next ellipse
298  n *= 2; //include spaces
299 
300  DrawCircleOfEllipses(output, cx, cy, r, r0, r1, n, theta, dtheta, true);
301  DrawCircleOfEllipses(output, cx, cy, r - r1, r0, r1, n,
302  theta + dtheta, dtheta, true, n/2 - 1);
303  DrawCircleOfEllipses(output, cx, cy, r + r1, r0, r1, n,
304  theta + dtheta, dtheta, false, n/2 - 2);
305 } //DrawTripleCircle
306 
326 
327 void OpticalIllusion2(const std::string& fname, size_t w, size_t n,
328  float r, float r0, float r1, const char dark[], const char light[],
329  const char bgclr[])
330 {
331  const size_t cx = w/2; //center x coordinate
332  const size_t cy = cx; //center y coordinate
333  FILE* output = nullptr; //output file pointer
334 
335  if(OpenSVG(output, fname, w, w)){
336  //style tag
337  fprintf(output, "<style>"); //open style tag
338  fprintf(output, "ellipse{fill:none;stroke-width:3}"); //ellipse
339  fprintf(output, "ellipse.b{cx:%lu;cy:%lu;stroke:none;fill:%s;}",
340  cx, cy, dark); //dark ellipse
341  fprintf(output, "ellipse.w{cx:%lu;cy:%lu;stroke:none;fill:%s;}",
342  cx, cy, light); //light ellipse
343  fprintf(output, "</style>\n"); //close style tag
344 
345  //background
346  fprintf(output, "<rect width=\"%lu\" height=\"%lu\" ", w, w); //rectangle
347  fprintf(output, "style=\"fill:%s\"/>\n", bgclr); //fill
348 
349  DrawTripleCircle(output, cx, cy, r, r0, r1, 36);
350  DrawTripleCircle(output, cx, cy, r - 64, 0.8f*r0, 0.8f*r1, 36, true);
351 
352  CloseSVG(output); //clean up and exit
353  } //if
354 } //OpticalIllusion2
355 
356 #pragma region Illusion2
357 
359 
366 
367 int main(){
368  OpticalIllusion1("output1", 800, 4, 100.0f, 72.0f, 24,
369  "black", "white", "gray");
370  OpticalIllusion1("output1a", 800, 4, 100.0f, 72.0f, 24,
371  "blue", "yellow", "forestgreen");
372  OpticalIllusion2("output2", 800, 3, 300.0f, 12.0f, 6.0f,
373  "black", "white", "gray");
374  OpticalIllusion2("output2a", 800, 3, 300.0f, 12.0f, 6.0f,
375  "blue", "yellow", "forestgreen");
376 
377  return 0;
378 } //main
void DrawCircleOfSquares(FILE *output, size_t cx, size_t cy, float r, size_t sw, bool parity)
Draw a circle of squares to a file in SVG format.
Definition: main.cpp:113
void DrawTripleCircle(FILE *output, size_t cx, size_t cy, float r, float r0, float r1, size_t n, bool flip=false)
Draw 3 concentric circles of ellipses to a file in SVG format.
Definition: main.cpp:293
void OpticalIllusion1(const std::string &fname, size_t w, size_t n, float r0, float dr, size_t sw, const char dark[], const char light[], const char bgclr[])
Draw the first optical illusion to a file in SVG format.
Definition: main.cpp:159
void OpticalIllusion2(const std::string &fname, size_t w, size_t n, float r, float r0, float r1, const char dark[], const char light[], const char bgclr[])
Draw the second optical illusion to a file in SVG format.
Definition: main.cpp:327
int main()
Main.
Definition: main.cpp:367
bool OpenSVG(FILE *&output, const std::string &fname, size_t w, size_t h)
Open SVG file.
Definition: main.cpp:50
void SelectEllipseColor(FILE *output, size_t i, bool parity)
Select ellipse color based on index.
Definition: main.cpp:204
const float PI
Pi.
Definition: main.cpp:33
void DrawCircleOfEllipses(FILE *output, size_t cx, size_t cy, float r, float r0, float r1, size_t n, float theta, float dtheta, bool parity, size_t flip=999999)
Draw circle of ellipses to a file in SVG format.
Definition: main.cpp:232
void CloseSVG(FILE *&output)
Close SVG file.
Definition: main.cpp:79