MRaster examples 21.0.0.0
Image Processing Library
Loading...
Searching...
No Matches
mandelbrot_triangle.cpp
Go to the documentation of this file.
1// -*- Mode:C++; Coding:us-ascii-unix; fill-column:158 -*-
2/*******************************************************************************************************************************************************.H.S.**/
3/**
4 @file mandelbrot_triangle.cpp
5 @author Mitch Richling <https://www.mitchr.me>
6 @brief Draw a Mandelbrot set using an edge detection algorithm.@EOL
7 @std C++20
8 @copyright
9 @parblock
10 Copyright (c) 1988-2015, Mitchell Jay Richling <https://www.mitchr.me> All rights reserved.
11
12 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
13
14 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18
19 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
20 without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27 DAMAGE.
28 @endparblock
29 @filedetails
30
31 The ramCanvas library has a very rich (some would say bloated) collection of functions that do essentially the same thing just in slightly different ways.
32 The reason for this is so that the library can support legacy code directly, and generally bend itself to different programming styles and needs.
33
34 One example of this are the "point objects" and associated methods. Of the nice things about the point objects defined in the ramCanvas object is that they
35 are binary compatible with the most common point and complex formats used -- including the complex that are contained in the C and C++ standards and the
36 complex used in Fortran. As a good example, the findAlphaTriangle, traceBoundry, and inSet functions existed long before the ramCanvas library, yet one is
37 able to directly make use of the preexisting data types.
38
39*/
40/*******************************************************************************************************************************************************.H.E.**/
41/** @cond exj */
42
43//--------------------------------------------------------------------------------------------------------------------------------------------------------------
44#include "ramCanvas.hpp"
45
46//--------------------------------------------------------------------------------------------------------------------------------------------------------------
47typedef struct { double x; double y; } complex;
48
49int MAXCOUNT;
50
51const int MAXTRCNT = 250000;
52complex thePath[MAXTRCNT];
53
54//--------------------------------------------------------------------------------------------------------------------------------------------------------------
55int inSet(complex tstPt);
56int findAlphaTriangle(int maxCnt, int ptA, int ptB, double slop, complex triangle[3]);
57int traceBoundry(int maxCnt, double epsilon, int goOtherWay, complex alphaTriangle[3], complex thePath[], int *pathLen);
58int orbCmp(complex tstPt);
59
60//--------------------------------------------------------------------------------------------------------------------------------------------------------------
61int main(void) {
62 std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
63
64 // Pick a triangle type and size..
65// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.50, 0.0}, {0.500, 0.50} }; // Big right triangles
66// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.10, 0.0}, {0.100, 0.10} }; // Small right triangles
67// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.01, 0.0}, {0.010, 0.01} }; // Tiny right triangles
68// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.50, 0.0}, {0.240, 0.50} }; // Big non-right triangles
69// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.10, 0.0}, {0.050, 0.10} }; // Small non-right triangles
70// complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.01, 0.0}, {0.001, 0.01} }; // Tiny non-right triangles
71 complex protoAlphaTriangle[3] = { {0.0, 0.0}, {0.001, 0.0}, {0.0001, 0.001} }; // Tiny-Tiny non-right triangles
72
73 mjr::ramCanvas3c8b theRamCanvas(7680/4, 7680/4, -2.1, 1.1, -1.5, 1.5);
74
75 // First we draw a greyscale Mandelbrot set for reference.
76 if(true) {
77 MAXCOUNT = 255;
78 std::cout << "INFO(main): Draw reference set via fill algorithm." << std::endl;
79 for(int yy=0;yy<theRamCanvas.getNumPixY();yy++) {
80 for(int xx=0;xx<theRamCanvas.getNumPixX();xx++) {
81 complex tpt;
82 tpt.x = theRamCanvas.int2realX(xx);
83 tpt.y = theRamCanvas.int2realY(yy);
84 int clr = orbCmp(tpt);
85 theRamCanvas.drawPoint(xx, yy, mjr::ramCanvas3c8b::colorType::csPGrey3x::c(static_cast<mjr::ramCanvas3c8b::csIntType>((100*clr)%768)));
86 }
87 }
88 }
89
90 // Now we trace several set boundaries
91 for(MAXCOUNT = 1; MAXCOUNT < 10; MAXCOUNT++) {
92 std::cout << "INFO(main): Curve: " << MAXCOUNT << std::endl;
93 // Find alpha
94 complex alphaTriangle[3];
95 for(int i=0; i<3; i++)
96 alphaTriangle[i] = protoAlphaTriangle[i];
97 if(findAlphaTriangle(MAXTRCNT, 0, 1, 0.0, alphaTriangle)) {
98 int thePathLen;
99 traceBoundry(MAXTRCNT, 0.00003, 0, alphaTriangle, thePath, &thePathLen);
100 theRamCanvas.drawPLCurve(thePathLen+1, (mjr::ramCanvas3c8b::pointFltType *)thePath, mjr::ramCanvas3c8b::colorType(255, 0, 255));
101 }
102 }
103
104 theRamCanvas.writeTIFFfile("mandelbrot_triangle.tiff");
105 std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime;
106 std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl;
107}
108
109//--------------------------------------------------------------------------------------------------------------------------------------------------------------
110int orbCmp(complex tstPt) {
111 mjr::ramCanvas3c8b::coordFltType zx = 0.0;
112 mjr::ramCanvas3c8b::coordFltType zy = 0.0;
113 int count = 0;
114 mjr::ramCanvas3c8b::coordFltType tempx;
115 while(1) {
116 if(zx * zx + zy * zy >= 4)
117 return count;
118 if(count > MAXCOUNT)
119 return 0;
120 tempx = zx * zx - zy * zy + tstPt.x;
121 zy = 2 * zx * zy + tstPt.y;
122 zx = tempx;
123 count++;
124 }
125}
126
127//--------------------------------------------------------------------------------------------------------------------------------------------------------------
128int inSet(complex tstPt) {
129 if(orbCmp(tstPt)) // Transform "count" => 1 or zero for inSet return
130 return 1;
131 else
132 return 0;
133}
134
135//--------------------------------------------------------------------------------------------------------------------------------------------------------------
136/* Return 1 if found, return 0 other wise. */
137int findAlphaTriangle(int maxCnt, int ptA, int ptB, double slop, complex triangle[3]) {
138 int printDebug=2;
139 int hitCount;
140 int count = 0;
141 mjr::ramCanvas3c8b::coordFltType xDelta = triangle[ptB].x - triangle[ptA].x;
142 mjr::ramCanvas3c8b::coordFltType yDelta = triangle[ptB].y - triangle[ptA].y;
143
144 // Shrink the delta some....
145 xDelta = (xDelta - xDelta * slop);
146 yDelta = (yDelta - yDelta * slop);
147
148 while(1) {
149 /* Check the current triangle -- note we recheck many points, but we will fix this later... */
150 hitCount = inSet(triangle[0])+inSet(triangle[1])+inSet(triangle[2]);
151 if( (hitCount >= 1) && (hitCount <= 2) ) {
152 if(printDebug>1)
153 std::cerr << "INFO(findAlphaTriangle): Found alpha triangle!" << std::endl;
154 return 1;
155 } else {
156 /* Slide the current triangle over a bit... */
157 for(int i=0; i<3; i++) {
158 triangle[i].x = triangle[i].x + xDelta;
159 triangle[i].y = triangle[i].y + yDelta;
160 }
161 }
162 count++;
163 if(count > maxCnt) {
164 if(printDebug)
165 std::cerr << "ERROR(findAlphaTriangle): Too many iterations!" << std::endl;
166 return 0;
167 }
168 }
169}
170
171//--------------------------------------------------------------------------------------------------------------------------------------------------------------
172/* This function finds an approximation to the boundary of a region in the plane. The region is defined by an boolean-like valued function that accepting a
173 point in the plane --- i.e. it returns 1 if the point is in the set, and 0 otherwise. The approximation to the boundary is based upon a triangulation of
174 the plane -- the triangulation is induced by the alphaTriangle argument. The return is a list of line segments, each traversing individual triangles in
175 the given triangulation -- each segment has endpoints at the midpoints of triangle edges. The alphaTriangle must have at least on vertex in the set and
176 one outside of the set. The function then finds a neighboring triangle in the triangulation that has the same characteristic -- i.e. one point in and one
177 point out. The function then repeats the process. The function ends when maxCnt triangles have been found or until the path gets within epsilon of its
178 starting point.
179
180 @param maxCnt The maximum number of iterations before the function will exit.
181 @param epsilon If the boundary traced comes within epsilon of the original point, the function will return.
182 @param goOtherWay If the alphaTriangle has two points in the region and this argument is non-zero, then the boundary trace will proceed in the opposite
183 direction it normally would. Useful for tracing boundaries that are not simple closed curves.
184 @param alphaTriangle The first triangle. It must have at least one vertex in the region and one vertex not in the region.
185 @param thePath array holding the path upon exit. This must have at least maxCnt elements.
186 @param pathLen contains max index of a point in thePath upon exit -- not the number of elements. It will be -1 if no points are in the array.
187 @return 0 The triangle was entire in or out of the region.
188 @return 1 Boundary came back upon self
189 @return 2 Max iteration count reached
190 @return 3 goOtherWay==1, and was of no use */
191int traceBoundry(int maxCnt, double epsilon, int goOtherWay, complex alphaTriangle[3], complex thePath[], int *pathLen) {
192 complex curTriangle[3];
193 int printDebug=2;
194 int pInState[3], pInStateSum;
195 int insIdx, unkIdx, outIdx, tmpIdx;
196
197 /* Initialize the first triangle, and get in/out of set info. */
198 for(int i=0; i<3; i++) {
199 curTriangle[i] = alphaTriangle[i];
200 pInState[i] = inSet(curTriangle[i]);
201 } /* end for */
202
203 // Make sure we have an appropriate number of "inSet" vertexes
204 *pathLen = -1; // Set now in case of return
205 pInStateSum = 0;
206 for(int i=0; i<3; i++)
207 pInStateSum += pInState[i];
208 if(pInStateSum >= 3) {
209 if(printDebug)
210 std::cerr << "ERROR(traceBoundry): All vertexes of alpha triangle in set!" << std::endl;
211 return 0;
212 } /* end if */
213 if(pInStateSum <= 0) {
214 if(printDebug)
215 std::cerr << "ERROR(traceBoundry): No vertexes of alpha triangle in set!" << std::endl;
216 return 0;
217 } /* end if */
218
219 // Set insIdx, unkIdx, and outIdx for the first triangle
220 insIdx = unkIdx = outIdx = -1;
221 for(int i=0; i<3; i++) {
222 if(pInState[i]) {
223 if(insIdx == -1) {
224 insIdx = i;
225 } else {
226 if(goOtherWay) {
227 unkIdx = insIdx;
228 insIdx = i;
229 } else {
230 unkIdx = i;
231 } /* end if/else */
232 } /* end if/else */
233 } else {
234 if(outIdx == -1) {
235 outIdx = i;
236 } else {
237 if(goOtherWay) {
238 if(printDebug>1)
239 std::cerr << "WARNING(traceBoundry): goOtherWay is ineffective in this case!" << std::endl;
240 return 3;
241 } /* end if */
242 unkIdx = i;
243 } /* end if/else */
244 } /* end if/else */
245 } /* end for */
246
247 // Put the first point into the path array and increment pathLen.
248 *pathLen = 0;
249 thePath[*pathLen].x = (curTriangle[insIdx].x + curTriangle[outIdx].x)/2;
250 thePath[*pathLen].y = (curTriangle[insIdx].y + curTriangle[outIdx].y)/2;
251
252 // Main iteration loop
253 for(int count=0;(count<(maxCnt-1))||(maxCnt==0);count++) {
254 //Update the coordinates for our next triangle
255 curTriangle[unkIdx].x = curTriangle[insIdx].x + curTriangle[outIdx].x - curTriangle[unkIdx].x;
256 curTriangle[unkIdx].y = curTriangle[insIdx].y + curTriangle[outIdx].y - curTriangle[unkIdx].y;
257
258 // Fix insIdx, outIdx, and unkIdx for the new triangle..
259 if(inSet(curTriangle[unkIdx])) {
260 tmpIdx = insIdx;
261 insIdx = unkIdx;
262 unkIdx = tmpIdx;
263 } else {
264 tmpIdx = outIdx;
265 outIdx = unkIdx;
266 unkIdx = tmpIdx;
267 } /* end if/else */
268
269 // Update the path array
270 (*pathLen)++;
271 thePath[*pathLen].x = (curTriangle[insIdx].x + curTriangle[outIdx].x)/2;
272 thePath[*pathLen].y = (curTriangle[insIdx].y + curTriangle[outIdx].y)/2;
273
274 // Figure out if we have come back upon the alpha triangle.
275 if( (fabs(thePath[*pathLen].x - thePath[0].x) + fabs(thePath[*pathLen].y - thePath[0].y)) < epsilon) {
276 if(printDebug>1)
277 std::cerr << "INFO(traceBoundry): Came back upon self (itr: " << count << "). Done." << std::endl;
278 return 1;
279 }
280
281 } /* end for */
282 if(printDebug>1)
283 std::cerr << "INFO(traceBoundry): Max iteration count reached. Done." << std::endl;
284 return 2;
285} /* end func traceBoundry */
286/** @endcond */
int main(int argc, char *argv[])