& ranks, size_t width, std::mt19937& rng, const char* baseFileName)
358 | {
359 | ScopedTimer timer("Phase 1", false);
360 |
361 | #if 1
362 | // count how many ones there are
363 | struct P
364 | {
365 | size_t x;
366 | size_t y;
367 | };
368 | std::vector points;
369 | for (size_t i = 0; i < binaryPattern.size(); ++i)
370 | {
371 | if (binaryPattern[i])
372 | points.push_back({ i % width, i / width });
373 | }
374 |
375 | // Shuffling will break patterns and ties better
376 | std::shuffle(points.begin(), points.end(), rng);
377 |
378 | // Use a "mitchell's worst candidate" to make a sequence out of the initial binary pattern
379 | std::vector pointScores(points.size());
380 | size_t startingPoints = points.size();
381 | while (!points.empty())
382 | {
383 | printf("\r%i%%", int(100.0f * (1.0f - float(points.size()) / float(startingPoints))));
384 |
385 | // score the points. Score = distance to closest other point.
386 | #pragma omp parallel for
387 | for (int srcPointIndex = 0; srcPointIndex < (int)points.size(); ++srcPointIndex)
388 | {
389 | const P& srcPoint = points[srcPointIndex];
390 | float minDist = FLT_MAX;
391 |
392 | for (int destPointIndex = 0; destPointIndex < (int)points.size(); ++destPointIndex)
393 | {
394 | if (srcPointIndex == destPointIndex)
395 | continue;
396 |
397 | const P& destPoint = points[destPointIndex];
398 |
399 | // Calculate toroidal distance. Closest distance is the score
400 | size_t dx = (srcPoint.x >= destPoint.x) ? srcPoint.x - destPoint.x : destPoint.x - srcPoint.x;
401 | if (dx > width / 2)
402 | dx = width - dx;
403 |
404 | size_t dy = (srcPoint.y >= destPoint.y) ? srcPoint.y - destPoint.y : destPoint.y - srcPoint.y;
405 | if (dy > width / 2)
406 | dy = width - dy;
407 |
408 | float distance = std::sqrt(float(dx) * float(dx) + float(dy) * float(dy));
409 | minDist = std::min(minDist, distance);
410 | }
411 |
412 | pointScores[srcPointIndex] = minDist;
413 | }
414 |
415 | // Find the worst scoring point
416 | float worstPointScore = FLT_MAX;
417 | size_t worstPointIndex = 0;
418 | for (int pointIndex = 0; pointIndex < (int)points.size(); ++pointIndex)
419 | {
420 | if (pointScores[pointIndex] >= worstPointScore)
421 | continue;
422 |
423 | worstPointScore = pointScores[pointIndex];
424 | worstPointIndex = pointIndex;
425 | }
426 |
427 | // Remove the point with the lowest score, as the next point
428 | const P& worstP = points[worstPointIndex];
429 | binaryPattern[worstP.y * width + worstP.x] = false;
430 | ranks[worstP.y * width + worstP.x] = points.size();
431 | points.erase(points.begin() + worstPointIndex);
432 | }
433 | printf("\n");
434 | #else
435 | size_t ones = 0;
436 | for (bool b : binaryPattern)
437 | {
438 | if (b)
439 | ones++;
440 | }
441 | size_t startingOnes = ones;
442 |
443 | // remove the tightest cluster repeatedly
444 | while (ones > 0)
445 | {
446 | printf("\r%i%%", int(100.0f * (1.0f - float(ones) / float(startingOnes))));
447 |
448 | int bestX, bestY;
449 | FindTightestClusterLUT(LUT, binaryPattern, width, bestX, bestY, rng);
450 | binaryPattern[bestY * width + bestX] = false;
451 | WriteLUTValue(LUT, width, false, bestX, bestY);
452 | ones--;
453 | ranks[bestY*width + bestX] = ones;
454 |
455 | #if SAVE_VOIDCLUSTER_PHASE1()
456 | // save the binary pattern out for debug purposes
457 | SaveBinaryPattern(binaryPattern, width, baseFileName, int(startingOnes - ones), bestX, bestY, -1, -1);
458 | #endif
459 | }
460 | printf("\n");
461 | #endif
462 | }
463 |
464 | struct Point
465 | {
466 | size_t x;
467 | size_t y;
468 | };
469 | typedef std::vector TPoints;
470 | typedef std::vector TPointGrid;
471 |
472 | static bool DistanceSqToClosestPoint(const TPoints& points, const Point& point, float& minDistSq, size_t width)
473 | {
474 | if (points.size() == 0)
475 | return false;
476 |
477 | // calculate the closest distance from this point to an existing sample
478 | for (const Point& p : points)
479 | {
480 | float distx = std::abs(float(p.x) - float(point.x));
481 | float disty = std::abs(float(p.y) - float(point.y));
482 |
483 | if (distx > float(width) / 2.0f)
484 | distx = float(width) - distx;
485 |
486 | if (disty > float(width) / 2.0f)
487 | disty = float(width) - disty;
488 |
489 | float distSq = distx * distx + disty * disty;
490 | if (distSq < minDistSq)
491 | minDistSq = distSq;
492 | }
493 | return true;
494 | }
495 |
496 | static float DistanceSqToClosestPoint(const TPointGrid& grid, size_t cellCount, size_t cellSize, const Point& point, size_t width)
497 | {
498 | const int basex = int(point.x / cellSize);
499 | const int basey = int(point.y / cellSize);
500 |
501 | const int maxRadius = int(cellCount / 2);
502 |
503 | float minDistSq = FLT_MAX;
504 | bool foundAPoint = false;
505 | bool didAnExtraRing = false;
506 |
507 | for (int radius = 0; radius <= maxRadius; ++radius)
508 | {
509 | // top and bottom rows
510 | {
511 | for (int offsetX = -radius; offsetX <= radius; ++offsetX)
512 | {
513 | int x = int(basex + offsetX + cellCount) % int(cellCount);
514 |
515 | int offsetY = -radius;
516 | int y = int(basey + offsetY + cellCount) % int(cellCount);
517 | foundAPoint |= DistanceSqToClosestPoint(grid[y*cellCount + x], point, minDistSq, width);
518 |
519 | offsetY = radius;
520 | y = int(basey + offsetY + cellCount) % int(cellCount);
521 | foundAPoint |= DistanceSqToClosestPoint(grid[y*cellCount + x], point, minDistSq, width);
522 | }
523 | }
524 |
525 | // left and right
526 | {
527 | for (int offsetY = -radius + 1; offsetY <= radius - 1; ++offsetY)
528 | {
529 | int y = int(basey + offsetY + cellCount) % int(cellCount);
530 |
531 | int offsetX = -radius;
532 | int x = int(basex + offsetX + cellCount) % int(cellCount);
533 | foundAPoint |= DistanceSqToClosestPoint(grid[y*cellCount + x], point, minDistSq, width);
534 |
535 | offsetX = +radius;
536 | x = int(basex + offsetX + cellCount) % int(cellCount);
537 | foundAPoint |= DistanceSqToClosestPoint(grid[y*cellCount + x], point, minDistSq, width);
538 | }
539 | }
540 |
541 | // we stop when we've found a point, then do another ring to make sure there isn't something closer to what we found.
542 | if (foundAPoint)
543 | {
544 | if (didAnExtraRing)
545 | break;
546 | else
547 | didAnExtraRing = true;
548 | }
549 | }
550 |
551 | return minDistSq;
552 | }
553 |
554 | static void AddPointToPointGrid(TPointGrid& grid, size_t cellCount, size_t cellSize, const Point& point)
555 | {
556 | Point cell;
557 | cell.x = point.x / cellSize;
558 | cell.y = point.y / cellSize;
559 | grid[cell.y * cellCount + cell.x].push_back(point);
560 | }
561 |
562 | // This replaces "Initial Binary Pattern" and "Phase 1" in the void and cluster algorithm.
563 | // Initial binary pattern makes blue noise distributed points.
564 | // Phase 1 makes them be progressive, so any points from 0 to N are blue noise.
565 | // Mitchell's best candidate algorithm makes progressive blue noise so can be used instead of those 2 steps.
566 | // https://blog.demofox.org/2017/10/20/generating-blue-noise-sample-points-with-mitchells-best-candidate-algorithm/
567 | static void MitchellsBestCandidate(std::vector& binaryPattern, std::vector& ranks, size_t width)
568 | {
569 | ScopedTimer timer("Mitchells Best Candidate", false);
570 |
571 | std::mt19937 rng(GetRNGSeed());
572 | std::uniform_int_distribution dist(0, width*width - 1);
573 |
574 | binaryPattern.resize(width*width, false);
575 | ranks.resize(width*width, ~size_t(0));
576 |
577 | static const size_t gridCellCount = 32;
578 | TPointGrid grid(gridCellCount*gridCellCount);
579 | const size_t gridCellSize = width / gridCellCount;
580 |
581 | size_t ones = size_t(float(width * width)*0.1f);
582 | for (size_t i = 0; i < ones; ++i)
583 | {
584 | printf("\r%i%%", int(100.0f * float(i) / float(ones - 1)));
585 |
586 | // we scale up the candidates each iteration like in the paper, to keep frequency behavior consistent
587 | size_t numCandidates = i + 1;
588 |
589 | // keep the candidate that is farthest from the closest existing point
590 | float bestDistanceSq = 0.0f;
591 | Point best;
592 | for (size_t candidate = 0; candidate < numCandidates; ++candidate)
593 | {
594 | size_t index = dist(rng);
595 | Point c;
596 | c.x = index % width;
597 | c.y = index / width;
598 |
599 | float minDistSq = DistanceSqToClosestPoint(grid, gridCellCount, gridCellSize, c, width);
600 |
601 | if (minDistSq > bestDistanceSq)
602 | {
603 | bestDistanceSq = minDistSq;
604 | best = c;
605 | }
606 | }
607 |
608 | // take the best candidate
609 | binaryPattern[best.y * width + best.x] = true;
610 | ranks[best.y * width + best.x] = i;
611 | AddPointToPointGrid(grid, gridCellCount, gridCellSize, best);
612 | }
613 | printf("\n");
614 | }
615 |
616 | // Phase 2: Start with initial binary pattern and add points to the largest void until half the pixels are white, entering ranks for those pixels
617 | static void Phase2(std::vector& binaryPattern, std::vector& LUT, std::vector& ranks, size_t width, std::mt19937& rng)
618 | {
619 | ScopedTimer timer("Phase 2", false);
620 |
621 | // count how many ones there are
622 | size_t ones = 0;
623 | for (bool b : binaryPattern)
624 | {
625 | if (b)
626 | ones++;
627 | }
628 | size_t startingOnes = ones;
629 | size_t onesToDo = (width*width / 2) - startingOnes;
630 |
631 | // add to the largest void repeatedly
632 | while (ones <= (width*width/2))
633 | {
634 | size_t onesDone = ones - startingOnes;
635 | printf("\r%i%%", int(100.0f * float(onesDone) / float(onesToDo)));
636 |
637 | int bestX, bestY;
638 | FindLargestVoidLUT(LUT, binaryPattern, width, bestX, bestY, rng);
639 | binaryPattern[bestY * width + bestX] = true;
640 | WriteLUTValue(LUT, width, true, bestX, bestY);
641 | ranks[bestY*width + bestX] = ones;
642 | ones++;
643 | }
644 | printf("\n");
645 | }
646 |
647 | // Phase 3: Continue with the last binary pattern, repeatedly find the tightest cluster of 0s and insert a 1 into them
648 | static void Phase3(std::vector& binaryPattern, std::vector& LUT, std::vector& ranks, size_t width, std::mt19937& rng)
649 | {
650 | ScopedTimer timer("Phase 3", false);
651 |
652 | // count how many ones there are
653 | size_t ones = 0;
654 | for (bool b : binaryPattern)
655 | {
656 | if (b)
657 | ones++;
658 | }
659 | size_t startingOnes = ones;
660 | size_t onesToDo = (width*width) - startingOnes;
661 |
662 | // add 1 to the largest cluster of 0's repeatedly
663 | int bestX, bestY;
664 | while (FindLargestVoidLUT(LUT, binaryPattern, width, bestX, bestY, rng))
665 | {
666 | size_t onesDone = ones - startingOnes;
667 | printf("\r%i%%", int(100.0f * float(onesDone) / float(onesToDo)));
668 |
669 | WriteLUTValue(LUT, width, true, bestX, bestY);
670 | binaryPattern[bestY * width + bestX] = true;
671 | ranks[bestY*width + bestX] = ones;
672 | ones++;
673 | }
674 | printf("\n");
675 | }
676 |
677 | void GenerateBN_Void_Cluster(std::vector& blueNoise, size_t width, bool useMitchellsBestCandidate, const char* baseFileName)
678 | {
679 | std::mt19937 rng(GetRNGSeed());
680 |
681 | std::vector ranks(width*width, ~size_t(0));
682 |
683 | std::vector initialBinaryPattern;
684 | std::vector binaryPattern;
685 | std::vector initialLUT;
686 | std::vector LUT;
687 |
688 | if (!useMitchellsBestCandidate)
689 | {
690 | // make the initial binary pattern and initial LUT
691 | MakeInitialBinaryPattern(initialBinaryPattern, width, baseFileName, rng);
692 | MakeLUT(initialBinaryPattern, initialLUT, width, true);
693 |
694 | // Phase 1: Start with initial binary pattern and remove the tightest cluster until there are none left, entering ranks for those pixels
695 | binaryPattern = initialBinaryPattern;
696 | LUT = initialLUT;
697 | Phase1(binaryPattern, LUT, ranks, width, rng, baseFileName);
698 | }
699 | else
700 | {
701 | // replace initial binary pattern and phase 1 with Mitchell's best candidate algorithm, and then making the LUT
702 | MitchellsBestCandidate(initialBinaryPattern, ranks, width);
703 | MakeLUT(initialBinaryPattern, initialLUT, width, true);
704 |
705 | //SaveBinaryPattern(initialBinaryPattern, width, "out/_blah", 0, -1, -1, -1, -1);
706 | }
707 |
708 | #if SAVE_VOIDCLUSTER_INITIALPOINTS()
709 | SaveInitialPoints(ranks, width, useMitchellsBestCandidate);
710 | #endif
711 |
712 | // Phase 2: Start with initial binary pattern and add points to the largest void until half the pixels are white, entering ranks for those pixels
713 | binaryPattern = initialBinaryPattern;
714 | LUT = initialLUT;
715 | Phase2(binaryPattern, LUT, ranks, width, rng);
716 |
717 | // Phase 3: Continue with the last binary pattern, repeatedly find the tightest cluster of 0s and insert a 1 into them
718 | // Note: we do need to re-make the LUT, because we are writing 0s instead of 1s
719 | MakeLUT(binaryPattern, LUT, width, false);
720 | Phase3(binaryPattern, LUT, ranks, width, rng);
721 |
722 | // convert to U8
723 | {
724 | ScopedTimer timer("Converting to U8", false);
725 | blueNoise.resize(width*width);
726 | for (size_t index = 0; index < width*width; ++index)
727 | blueNoise[index] = uint8_t(ranks[index] * 256 / (width*width));
728 | }
729 | }
730 |
--------------------------------------------------------------------------------
/generatebn_void_cluster.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | // http://cv.ulichney.com/papers/1993-void-cluster.pdf
6 | void GenerateBN_Void_Cluster(std::vector& blueNoise, size_t width, bool useMitchellsBestCandidate, const char* baseFileName);
7 |
--------------------------------------------------------------------------------
/histogram.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | template
6 | void WriteHistogram(const std::vector& values, const char* fileName)
7 | {
8 | std::vector histogram;
9 | histogram.resize(std::numeric_limits::max() + 1, 0);
10 | for (const T& value : values)
11 | histogram[value]++;
12 |
13 | FILE * file = nullptr;
14 | fopen_s(&file, fileName, "w+t");
15 | fprintf(file, "\"Value\",\"Count\"\n");
16 | for (size_t index = 0, count = histogram.size(); index < count; ++index)
17 | fprintf(file, "\"%zu\",\"%zu\"\n", index, histogram[index]);
18 | fclose(file);
19 | }
--------------------------------------------------------------------------------
/image.cpp:
--------------------------------------------------------------------------------
1 | #include "image.h"
2 | #include
3 |
4 | void AppendImageHorizontal(
5 | const std::vector& srcA,
6 | size_t widthA,
7 | size_t heightA,
8 | const std::vector& srcB,
9 | size_t widthB,
10 | size_t heightB,
11 | std::vector& dest,
12 | size_t& destWidth,
13 | size_t& destHeight
14 | )
15 | {
16 | // calculate dims of desination and allocate it, filled with 0s
17 | destWidth = widthA + widthB;
18 | destHeight = std::max(heightA, heightB);
19 | dest.resize(destWidth*destHeight, 0);
20 |
21 | // copy srcA in
22 | for (size_t y = 0; y < heightA; ++y)
23 | {
24 | const uint8_t* srcptr = &srcA[y*widthA];
25 | uint8_t* destptr = &dest[y*destWidth];
26 |
27 | memcpy(destptr, srcptr, widthA);
28 | }
29 |
30 | // copy srcB in
31 | for (size_t y = 0; y < heightB; ++y)
32 | {
33 | const uint8_t* srcptr = &srcB[y*widthB];
34 | uint8_t* destptr = &dest[y*destWidth + widthA];
35 |
36 | memcpy(destptr, srcptr, widthA);
37 | }
38 | }
--------------------------------------------------------------------------------
/image.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | void AppendImageHorizontal(
6 | const std::vector& srcA,
7 | size_t widthA,
8 | size_t heightA,
9 | const std::vector& srcB,
10 | size_t widthB,
11 | size_t heightB,
12 | std::vector& dest,
13 | size_t& destWidth,
14 | size_t& destHeight
15 | );
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #define _CRT_SECURE_NO_WARNINGS
2 |
3 | #define THRESHOLD_SAMPLES() 11 // the number of samples for threshold testing.
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #include "convert.h"
10 | #include "histogram.h"
11 | #include "image.h"
12 | #include "scoped_timer.h"
13 | #include "generatebn_void_cluster.h"
14 | #include "dft.h"
15 |
16 | #define STB_IMAGE_WRITE_IMPLEMENTATION
17 | #include "stb/stb_image_write.h"
18 |
19 | #define STB_IMAGE_IMPLEMENTATION
20 | #include "stb/stb_image.h"
21 |
22 | void TestMask(const std::vector& noise, size_t noiseSize, const char* baseFileName)
23 | {
24 | std::vector thresholdImage(noise.size());
25 |
26 | for (size_t testIndex = 0; testIndex < THRESHOLD_SAMPLES(); ++testIndex)
27 | {
28 | float percent = float(testIndex) / float(THRESHOLD_SAMPLES() - 1);
29 | uint8_t thresholdValue = FromFloat(percent);
30 | if (thresholdValue == 0)
31 | thresholdValue = 1;
32 | else if (thresholdValue == 255)
33 | thresholdValue = 254;
34 |
35 | for (size_t pixelIndex = 0, pixelCount = noise.size(); pixelIndex < pixelCount; ++pixelIndex)
36 | thresholdImage[pixelIndex] = noise[pixelIndex] > thresholdValue ? 255 : 0;
37 |
38 | std::vector thresholdImageDFT;
39 | DFT(thresholdImage, thresholdImageDFT, noiseSize);
40 |
41 | std::vector noiseAndDFT;
42 | size_t noiseAndDFT_width = 0;
43 | size_t noiseAndDFT_height = 0;
44 | AppendImageHorizontal(thresholdImage, noiseSize, noiseSize, thresholdImageDFT, noiseSize, noiseSize, noiseAndDFT, noiseAndDFT_width, noiseAndDFT_height);
45 |
46 | char fileName[256];
47 | sprintf(fileName, "%s_%u.png", baseFileName, thresholdValue);
48 | stbi_write_png(fileName, int(noiseAndDFT_width), int(noiseAndDFT_height), 1, noiseAndDFT.data(), 0);
49 | }
50 | }
51 |
52 | void TestNoise(const std::vector& noise, size_t noiseSize, const char* baseFileName)
53 | {
54 | char fileName[256];
55 | sprintf(fileName, "%s.noise.png", baseFileName);
56 | stbi_write_png(fileName, int(noiseSize), int(noiseSize), 1, noise.data(), 0);
57 |
58 | sprintf(fileName, "%s.histogram.csv", baseFileName);
59 |
60 | WriteHistogram(noise, fileName);
61 | std::vector noiseDFT;
62 | DFT(noise, noiseDFT, noiseSize);
63 |
64 | std::vector noiseAndDFT;
65 | size_t noiseAndDFT_width = 0;
66 | size_t noiseAndDFT_height = 0;
67 | AppendImageHorizontal(noise, noiseSize, noiseSize, noiseDFT, noiseSize, noiseSize, noiseAndDFT, noiseAndDFT_width, noiseAndDFT_height);
68 |
69 | sprintf(fileName, "%s.png", baseFileName);
70 | stbi_write_png(fileName, int(noiseAndDFT_width), int(noiseAndDFT_height), 1, noiseAndDFT.data(), 0);
71 |
72 | TestMask(noise, noiseSize, baseFileName);
73 | }
74 |
75 | int main(int argc, char** argv)
76 | {
77 | _mkdir("out");
78 | _mkdir("debug");
79 |
80 | // generate blue noise using void and cluster
81 | {
82 | static size_t c_width = 256;
83 |
84 | std::vector noise;
85 |
86 | {
87 | ScopedTimer timer("Blue noise by void and cluster");
88 | GenerateBN_Void_Cluster(noise, c_width, false, "out/blueVC_1");
89 | }
90 |
91 | TestNoise(noise, c_width, "out/blueVC_1");
92 | }
93 |
94 | // generate blue noise using void and cluster but using mitchell's best candidate instead of initial binary pattern and phase 1
95 | {
96 | static size_t c_width = 256;
97 |
98 | std::vector noise;
99 |
100 | {
101 | ScopedTimer timer("Blue noise by void and cluster with Mitchells best candidate");
102 | GenerateBN_Void_Cluster(noise, c_width, true, "out/blueVC_1M");
103 | }
104 |
105 | TestNoise(noise, c_width, "out/blueVC_1M");
106 | }
107 |
108 | // load a blue noise texture
109 | {
110 | int width, height, channels;
111 |
112 | std::vector noise;
113 |
114 | {
115 | ScopedTimer timer("Blue noise by void and cluster from loading the texture");
116 | uint8_t* image = stbi_load("bluenoise256.png", &width, &height, &channels, 0);
117 |
118 | noise.reserve(width*height);
119 | for (int i = 0; i < width*height; ++i)
120 | noise.push_back(image[i*channels]);
121 |
122 | stbi_image_free(image);
123 | }
124 |
125 | TestNoise(noise, width, "out/blueVC_2");
126 | }
127 |
128 | system("pause");
129 |
130 | return 0;
131 | }
--------------------------------------------------------------------------------
/misc.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | template
4 | T Clamp(T min, T max, T value)
5 | {
6 | if (value < min)
7 | return min;
8 | else if (value > max)
9 | return max;
10 | else
11 | return value;
12 | }
13 |
14 | inline float Lerp(float a, float b, float t)
15 | {
16 | return a * (1.0f - t) + b * t;
17 | }
--------------------------------------------------------------------------------
/out/blueVC_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1.gif
--------------------------------------------------------------------------------
/out/blueVC_1.histogram.csv:
--------------------------------------------------------------------------------
1 | "Value","Count"
2 | "0","256"
3 | "1","256"
4 | "2","256"
5 | "3","256"
6 | "4","256"
7 | "5","256"
8 | "6","256"
9 | "7","256"
10 | "8","256"
11 | "9","256"
12 | "10","256"
13 | "11","256"
14 | "12","256"
15 | "13","256"
16 | "14","256"
17 | "15","256"
18 | "16","256"
19 | "17","256"
20 | "18","256"
21 | "19","256"
22 | "20","256"
23 | "21","256"
24 | "22","256"
25 | "23","256"
26 | "24","256"
27 | "25","256"
28 | "26","256"
29 | "27","256"
30 | "28","256"
31 | "29","256"
32 | "30","256"
33 | "31","256"
34 | "32","256"
35 | "33","256"
36 | "34","256"
37 | "35","256"
38 | "36","256"
39 | "37","256"
40 | "38","256"
41 | "39","256"
42 | "40","256"
43 | "41","256"
44 | "42","256"
45 | "43","256"
46 | "44","256"
47 | "45","256"
48 | "46","256"
49 | "47","256"
50 | "48","256"
51 | "49","256"
52 | "50","256"
53 | "51","256"
54 | "52","256"
55 | "53","256"
56 | "54","256"
57 | "55","256"
58 | "56","256"
59 | "57","256"
60 | "58","256"
61 | "59","256"
62 | "60","256"
63 | "61","256"
64 | "62","256"
65 | "63","256"
66 | "64","256"
67 | "65","256"
68 | "66","256"
69 | "67","256"
70 | "68","256"
71 | "69","256"
72 | "70","256"
73 | "71","256"
74 | "72","256"
75 | "73","256"
76 | "74","256"
77 | "75","256"
78 | "76","256"
79 | "77","256"
80 | "78","256"
81 | "79","256"
82 | "80","256"
83 | "81","256"
84 | "82","256"
85 | "83","256"
86 | "84","256"
87 | "85","256"
88 | "86","256"
89 | "87","256"
90 | "88","256"
91 | "89","256"
92 | "90","256"
93 | "91","256"
94 | "92","256"
95 | "93","256"
96 | "94","256"
97 | "95","256"
98 | "96","256"
99 | "97","256"
100 | "98","256"
101 | "99","256"
102 | "100","256"
103 | "101","256"
104 | "102","256"
105 | "103","256"
106 | "104","256"
107 | "105","256"
108 | "106","256"
109 | "107","256"
110 | "108","256"
111 | "109","256"
112 | "110","256"
113 | "111","256"
114 | "112","256"
115 | "113","256"
116 | "114","256"
117 | "115","256"
118 | "116","256"
119 | "117","256"
120 | "118","256"
121 | "119","256"
122 | "120","256"
123 | "121","256"
124 | "122","256"
125 | "123","256"
126 | "124","256"
127 | "125","256"
128 | "126","256"
129 | "127","256"
130 | "128","256"
131 | "129","256"
132 | "130","256"
133 | "131","256"
134 | "132","256"
135 | "133","256"
136 | "134","256"
137 | "135","256"
138 | "136","256"
139 | "137","256"
140 | "138","256"
141 | "139","256"
142 | "140","256"
143 | "141","256"
144 | "142","256"
145 | "143","256"
146 | "144","256"
147 | "145","256"
148 | "146","256"
149 | "147","256"
150 | "148","256"
151 | "149","256"
152 | "150","256"
153 | "151","256"
154 | "152","256"
155 | "153","256"
156 | "154","256"
157 | "155","256"
158 | "156","256"
159 | "157","256"
160 | "158","256"
161 | "159","256"
162 | "160","256"
163 | "161","256"
164 | "162","256"
165 | "163","256"
166 | "164","256"
167 | "165","256"
168 | "166","256"
169 | "167","256"
170 | "168","256"
171 | "169","256"
172 | "170","256"
173 | "171","256"
174 | "172","256"
175 | "173","256"
176 | "174","256"
177 | "175","256"
178 | "176","256"
179 | "177","256"
180 | "178","256"
181 | "179","256"
182 | "180","256"
183 | "181","256"
184 | "182","256"
185 | "183","256"
186 | "184","256"
187 | "185","256"
188 | "186","256"
189 | "187","256"
190 | "188","256"
191 | "189","256"
192 | "190","256"
193 | "191","256"
194 | "192","256"
195 | "193","256"
196 | "194","256"
197 | "195","256"
198 | "196","256"
199 | "197","256"
200 | "198","256"
201 | "199","256"
202 | "200","256"
203 | "201","256"
204 | "202","256"
205 | "203","256"
206 | "204","256"
207 | "205","256"
208 | "206","256"
209 | "207","256"
210 | "208","256"
211 | "209","256"
212 | "210","256"
213 | "211","256"
214 | "212","256"
215 | "213","256"
216 | "214","256"
217 | "215","256"
218 | "216","256"
219 | "217","256"
220 | "218","256"
221 | "219","256"
222 | "220","256"
223 | "221","256"
224 | "222","256"
225 | "223","256"
226 | "224","256"
227 | "225","256"
228 | "226","256"
229 | "227","256"
230 | "228","256"
231 | "229","256"
232 | "230","256"
233 | "231","256"
234 | "232","256"
235 | "233","256"
236 | "234","256"
237 | "235","256"
238 | "236","256"
239 | "237","256"
240 | "238","256"
241 | "239","256"
242 | "240","256"
243 | "241","256"
244 | "242","256"
245 | "243","256"
246 | "244","256"
247 | "245","256"
248 | "246","256"
249 | "247","256"
250 | "248","256"
251 | "249","256"
252 | "250","256"
253 | "251","256"
254 | "252","256"
255 | "253","256"
256 | "254","256"
257 | "255","256"
258 |
--------------------------------------------------------------------------------
/out/blueVC_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1.png
--------------------------------------------------------------------------------
/out/blueVC_1M.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M.gif
--------------------------------------------------------------------------------
/out/blueVC_1M.histogram.csv:
--------------------------------------------------------------------------------
1 | "Value","Count"
2 | "0","256"
3 | "1","256"
4 | "2","256"
5 | "3","256"
6 | "4","256"
7 | "5","256"
8 | "6","256"
9 | "7","256"
10 | "8","256"
11 | "9","256"
12 | "10","256"
13 | "11","256"
14 | "12","256"
15 | "13","256"
16 | "14","256"
17 | "15","256"
18 | "16","256"
19 | "17","256"
20 | "18","256"
21 | "19","256"
22 | "20","256"
23 | "21","256"
24 | "22","256"
25 | "23","256"
26 | "24","256"
27 | "25","256"
28 | "26","256"
29 | "27","256"
30 | "28","256"
31 | "29","256"
32 | "30","256"
33 | "31","256"
34 | "32","256"
35 | "33","256"
36 | "34","256"
37 | "35","256"
38 | "36","256"
39 | "37","256"
40 | "38","256"
41 | "39","256"
42 | "40","256"
43 | "41","256"
44 | "42","256"
45 | "43","256"
46 | "44","256"
47 | "45","256"
48 | "46","256"
49 | "47","256"
50 | "48","256"
51 | "49","256"
52 | "50","256"
53 | "51","256"
54 | "52","256"
55 | "53","256"
56 | "54","256"
57 | "55","256"
58 | "56","256"
59 | "57","256"
60 | "58","256"
61 | "59","256"
62 | "60","256"
63 | "61","256"
64 | "62","256"
65 | "63","256"
66 | "64","256"
67 | "65","256"
68 | "66","256"
69 | "67","256"
70 | "68","256"
71 | "69","256"
72 | "70","256"
73 | "71","256"
74 | "72","256"
75 | "73","256"
76 | "74","256"
77 | "75","256"
78 | "76","256"
79 | "77","256"
80 | "78","256"
81 | "79","256"
82 | "80","256"
83 | "81","256"
84 | "82","256"
85 | "83","256"
86 | "84","256"
87 | "85","256"
88 | "86","256"
89 | "87","256"
90 | "88","256"
91 | "89","256"
92 | "90","256"
93 | "91","256"
94 | "92","256"
95 | "93","256"
96 | "94","256"
97 | "95","256"
98 | "96","256"
99 | "97","256"
100 | "98","256"
101 | "99","256"
102 | "100","256"
103 | "101","256"
104 | "102","256"
105 | "103","256"
106 | "104","256"
107 | "105","256"
108 | "106","256"
109 | "107","256"
110 | "108","256"
111 | "109","256"
112 | "110","256"
113 | "111","256"
114 | "112","256"
115 | "113","256"
116 | "114","256"
117 | "115","256"
118 | "116","256"
119 | "117","256"
120 | "118","256"
121 | "119","256"
122 | "120","256"
123 | "121","256"
124 | "122","256"
125 | "123","256"
126 | "124","256"
127 | "125","256"
128 | "126","256"
129 | "127","256"
130 | "128","256"
131 | "129","256"
132 | "130","256"
133 | "131","256"
134 | "132","256"
135 | "133","256"
136 | "134","256"
137 | "135","256"
138 | "136","256"
139 | "137","256"
140 | "138","256"
141 | "139","256"
142 | "140","256"
143 | "141","256"
144 | "142","256"
145 | "143","256"
146 | "144","256"
147 | "145","256"
148 | "146","256"
149 | "147","256"
150 | "148","256"
151 | "149","256"
152 | "150","256"
153 | "151","256"
154 | "152","256"
155 | "153","256"
156 | "154","256"
157 | "155","256"
158 | "156","256"
159 | "157","256"
160 | "158","256"
161 | "159","256"
162 | "160","256"
163 | "161","256"
164 | "162","256"
165 | "163","256"
166 | "164","256"
167 | "165","256"
168 | "166","256"
169 | "167","256"
170 | "168","256"
171 | "169","256"
172 | "170","256"
173 | "171","256"
174 | "172","256"
175 | "173","256"
176 | "174","256"
177 | "175","256"
178 | "176","256"
179 | "177","256"
180 | "178","256"
181 | "179","256"
182 | "180","256"
183 | "181","256"
184 | "182","256"
185 | "183","256"
186 | "184","256"
187 | "185","256"
188 | "186","256"
189 | "187","256"
190 | "188","256"
191 | "189","256"
192 | "190","256"
193 | "191","256"
194 | "192","256"
195 | "193","256"
196 | "194","256"
197 | "195","256"
198 | "196","256"
199 | "197","256"
200 | "198","256"
201 | "199","256"
202 | "200","256"
203 | "201","256"
204 | "202","256"
205 | "203","256"
206 | "204","256"
207 | "205","256"
208 | "206","256"
209 | "207","256"
210 | "208","256"
211 | "209","256"
212 | "210","256"
213 | "211","256"
214 | "212","256"
215 | "213","256"
216 | "214","256"
217 | "215","256"
218 | "216","256"
219 | "217","256"
220 | "218","256"
221 | "219","256"
222 | "220","256"
223 | "221","256"
224 | "222","256"
225 | "223","256"
226 | "224","256"
227 | "225","256"
228 | "226","256"
229 | "227","256"
230 | "228","256"
231 | "229","256"
232 | "230","256"
233 | "231","256"
234 | "232","256"
235 | "233","256"
236 | "234","256"
237 | "235","256"
238 | "236","256"
239 | "237","256"
240 | "238","256"
241 | "239","256"
242 | "240","256"
243 | "241","256"
244 | "242","256"
245 | "243","256"
246 | "244","256"
247 | "245","256"
248 | "246","256"
249 | "247","256"
250 | "248","256"
251 | "249","256"
252 | "250","256"
253 | "251","256"
254 | "252","256"
255 | "253","256"
256 | "254","256"
257 | "255","256"
258 |
--------------------------------------------------------------------------------
/out/blueVC_1M.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M.png
--------------------------------------------------------------------------------
/out/blueVC_1M_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_1.png
--------------------------------------------------------------------------------
/out/blueVC_1M_102.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_102.png
--------------------------------------------------------------------------------
/out/blueVC_1M_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_128.png
--------------------------------------------------------------------------------
/out/blueVC_1M_153.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_153.png
--------------------------------------------------------------------------------
/out/blueVC_1M_179.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_179.png
--------------------------------------------------------------------------------
/out/blueVC_1M_204.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_204.png
--------------------------------------------------------------------------------
/out/blueVC_1M_230.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_230.png
--------------------------------------------------------------------------------
/out/blueVC_1M_25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_25.png
--------------------------------------------------------------------------------
/out/blueVC_1M_254.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_254.png
--------------------------------------------------------------------------------
/out/blueVC_1M_51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_51.png
--------------------------------------------------------------------------------
/out/blueVC_1M_76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1M_76.png
--------------------------------------------------------------------------------
/out/blueVC_1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_1.png
--------------------------------------------------------------------------------
/out/blueVC_1_102.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_102.png
--------------------------------------------------------------------------------
/out/blueVC_1_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_128.png
--------------------------------------------------------------------------------
/out/blueVC_1_153.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_153.png
--------------------------------------------------------------------------------
/out/blueVC_1_179.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_179.png
--------------------------------------------------------------------------------
/out/blueVC_1_204.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_204.png
--------------------------------------------------------------------------------
/out/blueVC_1_230.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_230.png
--------------------------------------------------------------------------------
/out/blueVC_1_25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_25.png
--------------------------------------------------------------------------------
/out/blueVC_1_254.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_254.png
--------------------------------------------------------------------------------
/out/blueVC_1_51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_51.png
--------------------------------------------------------------------------------
/out/blueVC_1_76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_1_76.png
--------------------------------------------------------------------------------
/out/blueVC_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2.gif
--------------------------------------------------------------------------------
/out/blueVC_2.histogram.csv:
--------------------------------------------------------------------------------
1 | "Value","Count"
2 | "0","256"
3 | "1","256"
4 | "2","256"
5 | "3","256"
6 | "4","256"
7 | "5","256"
8 | "6","256"
9 | "7","256"
10 | "8","256"
11 | "9","256"
12 | "10","256"
13 | "11","256"
14 | "12","256"
15 | "13","256"
16 | "14","256"
17 | "15","256"
18 | "16","256"
19 | "17","256"
20 | "18","256"
21 | "19","256"
22 | "20","256"
23 | "21","256"
24 | "22","256"
25 | "23","256"
26 | "24","256"
27 | "25","256"
28 | "26","256"
29 | "27","256"
30 | "28","256"
31 | "29","256"
32 | "30","256"
33 | "31","256"
34 | "32","256"
35 | "33","256"
36 | "34","256"
37 | "35","256"
38 | "36","256"
39 | "37","256"
40 | "38","256"
41 | "39","256"
42 | "40","256"
43 | "41","256"
44 | "42","256"
45 | "43","256"
46 | "44","256"
47 | "45","256"
48 | "46","256"
49 | "47","256"
50 | "48","256"
51 | "49","256"
52 | "50","256"
53 | "51","256"
54 | "52","256"
55 | "53","256"
56 | "54","256"
57 | "55","256"
58 | "56","256"
59 | "57","256"
60 | "58","256"
61 | "59","256"
62 | "60","256"
63 | "61","256"
64 | "62","256"
65 | "63","256"
66 | "64","256"
67 | "65","256"
68 | "66","256"
69 | "67","256"
70 | "68","256"
71 | "69","256"
72 | "70","256"
73 | "71","256"
74 | "72","256"
75 | "73","256"
76 | "74","256"
77 | "75","256"
78 | "76","256"
79 | "77","256"
80 | "78","256"
81 | "79","256"
82 | "80","256"
83 | "81","256"
84 | "82","256"
85 | "83","256"
86 | "84","256"
87 | "85","256"
88 | "86","256"
89 | "87","256"
90 | "88","256"
91 | "89","256"
92 | "90","256"
93 | "91","256"
94 | "92","256"
95 | "93","256"
96 | "94","256"
97 | "95","256"
98 | "96","256"
99 | "97","256"
100 | "98","256"
101 | "99","256"
102 | "100","256"
103 | "101","256"
104 | "102","256"
105 | "103","256"
106 | "104","256"
107 | "105","256"
108 | "106","256"
109 | "107","256"
110 | "108","256"
111 | "109","256"
112 | "110","256"
113 | "111","256"
114 | "112","256"
115 | "113","256"
116 | "114","256"
117 | "115","256"
118 | "116","256"
119 | "117","256"
120 | "118","256"
121 | "119","256"
122 | "120","256"
123 | "121","256"
124 | "122","256"
125 | "123","256"
126 | "124","256"
127 | "125","256"
128 | "126","256"
129 | "127","256"
130 | "128","256"
131 | "129","256"
132 | "130","256"
133 | "131","256"
134 | "132","256"
135 | "133","256"
136 | "134","256"
137 | "135","256"
138 | "136","256"
139 | "137","256"
140 | "138","256"
141 | "139","256"
142 | "140","256"
143 | "141","256"
144 | "142","256"
145 | "143","256"
146 | "144","256"
147 | "145","256"
148 | "146","256"
149 | "147","256"
150 | "148","256"
151 | "149","256"
152 | "150","256"
153 | "151","256"
154 | "152","256"
155 | "153","256"
156 | "154","256"
157 | "155","256"
158 | "156","256"
159 | "157","256"
160 | "158","256"
161 | "159","256"
162 | "160","256"
163 | "161","256"
164 | "162","256"
165 | "163","256"
166 | "164","256"
167 | "165","256"
168 | "166","256"
169 | "167","256"
170 | "168","256"
171 | "169","256"
172 | "170","256"
173 | "171","256"
174 | "172","256"
175 | "173","256"
176 | "174","256"
177 | "175","256"
178 | "176","256"
179 | "177","256"
180 | "178","256"
181 | "179","256"
182 | "180","256"
183 | "181","256"
184 | "182","256"
185 | "183","256"
186 | "184","256"
187 | "185","256"
188 | "186","256"
189 | "187","256"
190 | "188","256"
191 | "189","256"
192 | "190","256"
193 | "191","256"
194 | "192","256"
195 | "193","256"
196 | "194","256"
197 | "195","256"
198 | "196","256"
199 | "197","256"
200 | "198","256"
201 | "199","256"
202 | "200","256"
203 | "201","256"
204 | "202","256"
205 | "203","256"
206 | "204","256"
207 | "205","256"
208 | "206","256"
209 | "207","256"
210 | "208","256"
211 | "209","256"
212 | "210","256"
213 | "211","256"
214 | "212","256"
215 | "213","256"
216 | "214","256"
217 | "215","256"
218 | "216","256"
219 | "217","256"
220 | "218","256"
221 | "219","256"
222 | "220","256"
223 | "221","256"
224 | "222","256"
225 | "223","256"
226 | "224","256"
227 | "225","256"
228 | "226","256"
229 | "227","256"
230 | "228","256"
231 | "229","256"
232 | "230","256"
233 | "231","256"
234 | "232","256"
235 | "233","256"
236 | "234","256"
237 | "235","256"
238 | "236","256"
239 | "237","256"
240 | "238","256"
241 | "239","256"
242 | "240","256"
243 | "241","256"
244 | "242","256"
245 | "243","256"
246 | "244","256"
247 | "245","256"
248 | "246","256"
249 | "247","256"
250 | "248","256"
251 | "249","256"
252 | "250","256"
253 | "251","256"
254 | "252","256"
255 | "253","256"
256 | "254","256"
257 | "255","256"
258 |
--------------------------------------------------------------------------------
/out/blueVC_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2.png
--------------------------------------------------------------------------------
/out/blueVC_2_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_1.png
--------------------------------------------------------------------------------
/out/blueVC_2_102.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_102.png
--------------------------------------------------------------------------------
/out/blueVC_2_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_128.png
--------------------------------------------------------------------------------
/out/blueVC_2_153.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_153.png
--------------------------------------------------------------------------------
/out/blueVC_2_179.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_179.png
--------------------------------------------------------------------------------
/out/blueVC_2_204.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_204.png
--------------------------------------------------------------------------------
/out/blueVC_2_230.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_230.png
--------------------------------------------------------------------------------
/out/blueVC_2_25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_25.png
--------------------------------------------------------------------------------
/out/blueVC_2_254.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_254.png
--------------------------------------------------------------------------------
/out/blueVC_2_51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_51.png
--------------------------------------------------------------------------------
/out/blueVC_2_76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atrix256/VoidAndCluster/1e5be47eb4eba2c87926d00907c2b027bd1a8942/out/blueVC_2_76.png
--------------------------------------------------------------------------------
/scoped_timer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct ScopedTimer
6 | {
7 | ScopedTimer(const char* label, bool twoNewLines = true)
8 | : m_twoNewLines(twoNewLines)
9 | {
10 | m_start = std::chrono::high_resolution_clock::now();
11 | printf("%s...\n", label);
12 | }
13 |
14 | ~ScopedTimer()
15 | {
16 | std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
17 | std::chrono::duration time_span = std::chrono::duration_cast>(end - m_start);
18 | printf("%f ms\n", time_span.count() * 1000.0f);
19 | if (m_twoNewLines)
20 | printf("\n");
21 | }
22 |
23 | bool m_twoNewLines;
24 | std::chrono::high_resolution_clock::time_point m_start;
25 | };
--------------------------------------------------------------------------------
/settings.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #define DETERMINISTIC() true // if true, will use the seed below for everything, else will randomly generate a seed.
5 |
6 | #define DETERMINISTIC_SEED() unsigned(783104853), unsigned(4213684301), unsigned(3526061164), unsigned(614346169), unsigned(478811579), unsigned(2044310268), unsigned(3671768129), unsigned(206439072)
7 |
8 | #define THRESHOLD_SAMPLES() 11 // the number of samples for threshold testing.
9 |
10 | #define SAVE_VOIDCLUSTER_INITIALBP() false
11 | #define SAVE_VOIDCLUSTER_PHASE1() false
12 | #define SAVE_VOIDCLUSTER_INITIALPOINTS() false
13 |
--------------------------------------------------------------------------------
/simple_fft/check_fft.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __SIMPLE_FFT__CHECK_FFT_HPP__
2 | #define __SIMPLE_FFT__CHECK_FFT_HPP__
3 |
4 | #include "fft_settings.h"
5 | #include "error_handling.hpp"
6 | #include "copy_array.hpp"
7 | #include
8 | #include
9 | #include
10 |
11 | using std::size_t;
12 |
13 | namespace simple_fft {
14 | namespace check_fft_private {
15 |
16 | enum CheckMode
17 | {
18 | CHECK_FFT_PARSEVAL,
19 | CHECK_FFT_ENERGY,
20 | CHECK_FFT_EQUALITY
21 | };
22 |
23 | template
24 | void getMaxAbsoluteAndRelativeErrorNorms(const TArray1D & array1,
25 | const TComplexArray1D & array2, const size_t size,
26 | real_type & max_absolute_error_norm,
27 | real_type & max_relative_error_norm)
28 | {
29 | using std::abs;
30 |
31 | real_type current_error;
32 |
33 | // NOTE: no parallelization here, it is a completely sequential loop!
34 | for(size_t i = 0; i < size; ++i) {
35 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
36 | current_error = abs(array1[i] - array2[i]);
37 | #else
38 | current_error = abs(array1(i) - array2(i));
39 | #endif
40 | if (current_error > max_absolute_error_norm) {
41 | max_absolute_error_norm = current_error;
42 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
43 | if (abs(array1[i]) > abs(array2[i])) {
44 | max_relative_error_norm = (abs(array1[i]) > 1e-20
45 | ? max_absolute_error_norm / abs(array1[i])
46 | : 0.0);
47 | }
48 | else {
49 | max_relative_error_norm = (abs(array2[i]) > 1e-20
50 | ? max_absolute_error_norm / abs(array2[i])
51 | : 0.0);
52 | }
53 | #else
54 | if (abs(array1(i)) > abs(array2(i))) {
55 | max_relative_error_norm = (abs(array1(i)) > 1e-20
56 | ? max_absolute_error_norm / abs(array1(i))
57 | : 0.0);
58 | }
59 | else {
60 | max_relative_error_norm = (abs(array2(i)) > 1e-20
61 | ? max_absolute_error_norm / abs(array2(i))
62 | : 0.0);
63 | }
64 | #endif
65 | }
66 | }
67 | }
68 |
69 | template
70 | void getMaxAbsoluteAndRelativeErrorNorms(const TArray2D & array1,
71 | const TComplexArray2D & array2,
72 | const size_t size1, const size_t size2,
73 | real_type & max_absolute_error_norm,
74 | real_type & max_relative_error_norm)
75 | {
76 | using std::abs;
77 |
78 | real_type current_error;
79 |
80 | // NOTE: no parallelization here, it is a completely sequential loop!
81 | for(int i = 0; i < static_cast(size1); ++i) {
82 | for(int j = 0; j < static_cast(size2); ++j) {
83 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
84 | current_error = abs(array1[i][j] - array2[i][j]);
85 | #else
86 | current_error = abs(array1(i,j) - array2(i,j));
87 | #endif
88 | if (current_error > max_absolute_error_norm) {
89 | max_absolute_error_norm = current_error;
90 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
91 | if (abs(array1[i][j]) > abs(array2[i][j])) {
92 | max_relative_error_norm = (abs(array1[i][j]) > 1e-20
93 | ? max_absolute_error_norm / abs(array1[i][j])
94 | : 0.0);
95 | }
96 | else {
97 | max_relative_error_norm = (abs(array2[i][j]) > 1e-20
98 | ? max_absolute_error_norm / abs(array2[i][j])
99 | : 0.0);
100 | }
101 | #else
102 | if (abs(array1(i,j)) > abs(array2(i,j))) {
103 | max_relative_error_norm = (abs(array1(i,j)) > 1e-20
104 | ? max_absolute_error_norm / abs(array1(i,j))
105 | : 0.0);
106 | }
107 | else {
108 | max_relative_error_norm = (abs(array2(i,j)) > 1e-20
109 | ? max_absolute_error_norm / abs(array2(i,j))
110 | : 0.0);
111 | }
112 | #endif
113 | }
114 | }
115 | }
116 | }
117 |
118 | template
119 | void getMaxAbsoluteAndRelativeErrorNorms(const TArray3D & array1, const TComplexArray3D & array2,
120 | const size_t size1, const size_t size2,
121 | const size_t size3, real_type & max_absolute_error_norm,
122 | real_type & max_relative_error_norm)
123 | {
124 | using std::abs;
125 |
126 | real_type current_error;
127 |
128 | // NOTE: no parallelization here, it is a completely sequential loop!
129 | for(int i = 0; i < static_cast(size1); ++i) {
130 | for(int j = 0; j < static_cast(size2); ++j) {
131 | for(int k = 0; k < static_cast(size3); ++k) {
132 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
133 | current_error = abs(array1[i][j][k] - array2[i][j][k]);
134 | #else
135 | current_error = abs(array1(i,j,k) - array2(i,j,k));
136 | #endif
137 | if (current_error > max_absolute_error_norm) {
138 | max_absolute_error_norm = current_error;
139 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
140 | if (abs(array1[i][j][k]) > abs(array2[i][j][k])) {
141 | max_relative_error_norm = (abs(array1[i][j][k]) > 1e-20
142 | ? max_absolute_error_norm / abs(array1[i][j][k])
143 | : 0.0);
144 | }
145 | else {
146 | max_relative_error_norm = (abs(array2[i][j][k]) > 1e-20
147 | ? max_absolute_error_norm / abs(array2[i][j][k])
148 | : 0.0);
149 | }
150 | #else
151 | if (abs(array1(i,j,k)) > abs(array2(i,j,k))) {
152 | max_relative_error_norm = (abs(array1(i,j,k)) > 1e-20
153 | ? max_absolute_error_norm / abs(array1(i,j,k))
154 | : 0.0);
155 | }
156 | else {
157 | max_relative_error_norm = (abs(array2(i,j,k)) > 1e-20
158 | ? max_absolute_error_norm / abs(array2(i,j,k))
159 | : 0.0);
160 | }
161 | #endif
162 | }
163 | }
164 | }
165 | }
166 | }
167 |
168 | template
169 | real_type squareAbsAccumulate(const TArray1D & array, const size_t size,
170 | const real_type init)
171 | {
172 | int size_signed = static_cast(size);
173 | real_type sum = init;
174 |
175 | using std::abs;
176 |
177 | #ifndef __clang__
178 | #ifdef __USE_OPENMP
179 | #pragma omp parallel for reduction(+:sum)
180 | #endif
181 | #endif
182 | for(int i = 0; i < size_signed; ++i) {
183 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
184 | sum += abs(array[i] * array[i]);
185 | #else
186 | sum += abs(array(i) * array(i));
187 | #endif
188 | }
189 |
190 | return sum;
191 | }
192 |
193 | template
194 | real_type squareAbsAccumulate(const TArray2D & array, const size_t size1,
195 | const size_t size2, const real_type init)
196 | {
197 | int size1_signed = static_cast(size1);
198 | int size2_signed = static_cast(size2);
199 | real_type sum = init;
200 |
201 | using std::abs;
202 |
203 | #ifndef __clang__
204 | #ifdef __USE_OPENMP
205 | #pragma omp parallel for reduction(+:sum)
206 | #endif
207 | #endif
208 | for(int i = 0; i < size1_signed; ++i) {
209 | for(int j = 0; j < size2_signed; ++j) {
210 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
211 | sum += abs(array[i][j] * array[i][j]);
212 | #else
213 | sum += abs(array(i,j) * array(i,j));
214 | #endif
215 | }
216 | }
217 |
218 | return sum;
219 | }
220 |
221 | template
222 | real_type squareAbsAccumulate(const TArray3D & array, const size_t size1,
223 | const size_t size2, const size_t size3,
224 | const real_type init)
225 | {
226 | int size1_signed = static_cast(size1);
227 | int size2_signed = static_cast(size2);
228 | int size3_signed = static_cast(size3);
229 | real_type sum = init;
230 |
231 | using std::abs;
232 |
233 | #ifndef __clang__
234 | #ifdef __USE_OPENMP
235 | #pragma omp parallel for reduction(+:sum)
236 | #endif
237 | #endif
238 | for(int i = 0; i < size1_signed; ++i) {
239 | for(int j = 0; j < size2_signed; ++j) {
240 | for(int k = 0; k < size3_signed; ++k) {
241 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
242 | sum += abs(array[i][j][k] * array[i][j][k]);
243 | #else
244 | sum += abs(array(i,j,k) * array(i,j,k));
245 | #endif
246 | }
247 | }
248 | }
249 |
250 | return sum;
251 | }
252 |
253 | // Generic template for CCheckFFT struct followed by its explicit specializations
254 | // for certain numbers of dimensions. TArray can be either of real or complex type.
255 | // The technique is similar to the one applied for CFFT struct.
256 | template
257 | struct CCheckFFT
258 | {};
259 |
260 | template
261 | struct CCheckFFT
262 | {
263 | static bool check_fft(const TArray1D & data_before,
264 | const TComplexArray1D & data_after,
265 | const size_t size, const real_type relative_tolerance,
266 | real_type & discrepancy, const CheckMode check_mode,
267 | const char *& error_description)
268 | {
269 | using namespace error_handling;
270 |
271 | if(0 == size) {
272 | GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
273 | return false;
274 | }
275 |
276 | if ( (CHECK_FFT_PARSEVAL != check_mode) &&
277 | (CHECK_FFT_ENERGY != check_mode) &&
278 | (CHECK_FFT_EQUALITY != check_mode) )
279 | {
280 | GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
281 | return false;
282 | }
283 |
284 | if (CHECK_FFT_EQUALITY != check_mode)
285 | {
286 | real_type sum_before = squareAbsAccumulate(data_before, size, 0.0);
287 | real_type sum_after = squareAbsAccumulate(data_after, size, 0.0);
288 |
289 | if (CHECK_FFT_PARSEVAL == check_mode) {
290 | sum_after /= size;
291 | }
292 |
293 | using std::abs;
294 |
295 | discrepancy = abs(sum_before - sum_after);
296 |
297 | if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
298 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
299 | return false;
300 | }
301 | else {
302 | return true;
303 | }
304 | }
305 | else {
306 | real_type relative_error;
307 | getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size,
308 | discrepancy, relative_error);
309 | if (relative_error < relative_tolerance) {
310 | return true;
311 | }
312 | else {
313 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
314 | return false;
315 | }
316 | }
317 | }
318 | };
319 |
320 | template
321 | struct CCheckFFT
322 | {
323 | static bool check_fft(const TArray2D & data_before,
324 | const TComplexArray2D & data_after,
325 | const size_t size1, const size_t size2,
326 | const real_type relative_tolerance, real_type & discrepancy,
327 | const CheckMode check_mode, const char *& error_description)
328 | {
329 | using namespace error_handling;
330 |
331 | if( (0 == size1) || (0 == size2) ) {
332 | GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
333 | return false;
334 | }
335 |
336 | if ( (CHECK_FFT_PARSEVAL != check_mode) &&
337 | (CHECK_FFT_ENERGY != check_mode) &&
338 | (CHECK_FFT_EQUALITY != check_mode) )
339 | {
340 | GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
341 | return false;
342 | }
343 |
344 | if (CHECK_FFT_EQUALITY != check_mode)
345 | {
346 | real_type sum_before = squareAbsAccumulate(data_before, size1, size2, 0.0);
347 | real_type sum_after = squareAbsAccumulate(data_after, size1, size2, 0.0);
348 |
349 | if (CHECK_FFT_PARSEVAL == check_mode) {
350 | sum_after /= size1 * size2;
351 | }
352 |
353 | using std::abs;
354 |
355 | discrepancy = abs(sum_before - sum_after);
356 |
357 | if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
358 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
359 | return false;
360 | }
361 | else {
362 | return true;
363 | }
364 | }
365 | else {
366 | real_type relative_error;
367 | getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
368 | size2, discrepancy, relative_error);
369 | if (relative_error < relative_tolerance) {
370 | return true;
371 | }
372 | else {
373 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
374 | return false;
375 | }
376 | }
377 | }
378 | };
379 |
380 | template
381 | struct CCheckFFT
382 | {
383 | static bool check_fft(const TArray3D & data_before,
384 | const TComplexArray3D & data_after,
385 | const size_t size1, const size_t size2, const size_t size3,
386 | const real_type relative_tolerance, real_type & discrepancy,
387 | const CheckMode check_mode, const char *& error_description)
388 | {
389 | using namespace error_handling;
390 |
391 | if( (0 == size1) || (0 == size2) || (0 == size3) ) {
392 | GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
393 | return false;
394 | }
395 |
396 | if ( (CHECK_FFT_PARSEVAL != check_mode) &&
397 | (CHECK_FFT_ENERGY != check_mode) &&
398 | (CHECK_FFT_EQUALITY != check_mode) )
399 | {
400 | GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
401 | return false;
402 | }
403 |
404 | if (CHECK_FFT_EQUALITY != check_mode)
405 | {
406 | real_type sum_before = squareAbsAccumulate(data_before, size1, size2, size3, 0.0);
407 | real_type sum_after = squareAbsAccumulate(data_after, size1, size2, size3, 0.0);
408 |
409 | if (CHECK_FFT_PARSEVAL == check_mode) {
410 | sum_after /= size1 * size2 * size3;
411 | }
412 |
413 | using std::abs;
414 |
415 | discrepancy = abs(sum_before - sum_after);
416 |
417 | if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
418 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
419 | return false;
420 | }
421 | else {
422 | return true;
423 | }
424 | }
425 | else {
426 | real_type relative_error;
427 | getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
428 | size2, size3, discrepancy, relative_error);
429 | if (relative_error < relative_tolerance) {
430 | return true;
431 | }
432 | else {
433 | GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
434 | return false;
435 | }
436 | }
437 | }
438 | };
439 |
440 | } // namespace check_fft_private
441 |
442 | namespace check_fft {
443 |
444 | template
445 | bool checkParsevalTheorem(const TArray1D & data_before_FFT,
446 | const TComplexArray1D & data_after_FFT,
447 | const size_t size, const real_type relative_tolerance,
448 | real_type & discrepancy, const char *& error_description)
449 | {
450 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
451 | data_after_FFT, size, relative_tolerance,
452 | discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
453 | error_description);
454 | }
455 |
456 | template
457 | bool checkParsevalTheorem(const TArray2D & data_before_FFT,
458 | const TComplexArray2D & data_after_FFT,
459 | const size_t size1, const size_t size2,
460 | const real_type relative_tolerance,
461 | real_type & discrepancy, const char *& error_description)
462 | {
463 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
464 | data_after_FFT, size1, size2, relative_tolerance,
465 | discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
466 | error_description);
467 | }
468 |
469 | template
470 | bool checkParsevalTheorem(const TArray3D & data_before_FFT,
471 | const TComplexArray3D & data_after_FFT,
472 | const size_t size1, const size_t size2, const size_t size3,
473 | const real_type relative_tolerance, real_type & discrepancy,
474 | const char *& error_description)
475 | {
476 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
477 | data_after_FFT, size1, size2, size3,
478 | relative_tolerance, discrepancy,
479 | check_fft_private::CHECK_FFT_PARSEVAL,
480 | error_description);
481 | }
482 |
483 | template
484 | bool checkEnergyConservation(const TArray1D & data_before_FFT,
485 | const TComplexArray1D & data_after_FFT_and_IFFT,
486 | const size_t size, const real_type relative_tolerance,
487 | real_type & discrepancy, const char *& error_description)
488 | {
489 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
490 | data_after_FFT_and_IFFT, size, relative_tolerance,
491 | discrepancy, check_fft_private::CHECK_FFT_ENERGY,
492 | error_description);
493 | }
494 |
495 | template
496 | bool checkEnergyConservation(const TArray2D & data_before_FFT,
497 | const TComplexArray2D & data_after_FFT_and_IFFT,
498 | const size_t size1, const size_t size2,
499 | const real_type relative_tolerance,
500 | real_type & discrepancy, const char *& error_description)
501 | {
502 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
503 | data_after_FFT_and_IFFT, size1, size2,
504 | relative_tolerance, discrepancy,
505 | check_fft_private::CHECK_FFT_ENERGY,
506 | error_description);
507 | }
508 |
509 | template
510 | bool checkEnergyConservation(const TArray3D & data_before_FFT,
511 | const TComplexArray3D & data_after_FFT_and_IFFT,
512 | const size_t size1, const size_t size2, const size_t size3,
513 | const real_type relative_tolerance, real_type & discrepancy,
514 | const char *& error_description)
515 | {
516 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
517 | data_after_FFT_and_IFFT, size1, size2,
518 | size3, relative_tolerance, discrepancy,
519 | check_fft_private::CHECK_FFT_ENERGY,
520 | error_description);
521 | }
522 |
523 | template
524 | bool checkEquality(const TArray1D & data_before_FFT,
525 | const TComplexArray1D & data_after_FFT_and_IFFT,
526 | const size_t size, const real_type relative_tolerance,
527 | real_type & discrepancy, const char *& error_description)
528 | {
529 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
530 | data_after_FFT_and_IFFT, size, relative_tolerance,
531 | discrepancy, check_fft_private::CHECK_FFT_EQUALITY,
532 | error_description);
533 | }
534 |
535 | template
536 | bool checkEquality(const TArray2D & data_before_FFT,
537 | const TComplexArray2D & data_after_FFT_and_IFFT, const size_t size1,
538 | const size_t size2, const real_type relative_tolerance,
539 | real_type & discrepancy, const char *& error_description)
540 | {
541 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
542 | data_after_FFT_and_IFFT, size1, size2,
543 | relative_tolerance, discrepancy,
544 | check_fft_private::CHECK_FFT_EQUALITY,
545 | error_description);
546 | }
547 |
548 | template
549 | bool checkEquality(const TArray3D & data_before_FFT,
550 | const TComplexArray3D & data_after_FFT_and_IFFT, const size_t size1,
551 | const size_t size2, const size_t size3, const real_type relative_tolerance,
552 | real_type & discrepancy, const char *& error_description)
553 | {
554 | return check_fft_private::CCheckFFT::check_fft(data_before_FFT,
555 | data_after_FFT_and_IFFT, size1, size2,
556 | size3, relative_tolerance, discrepancy,
557 | check_fft_private::CHECK_FFT_EQUALITY,
558 | error_description);
559 | }
560 |
561 | } // namespace check_fft
562 | } // namespace simple_fft
563 |
564 | #endif // __SIMPLE_FFT__CHECK_FFT_HPP__
565 |
--------------------------------------------------------------------------------
/simple_fft/copy_array.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __SIMPLE_FFT__COPY_ARRAY_HPP
2 | #define __SIMPLE_FFT__COPY_ARRAY_HPP
3 |
4 | #include "fft_settings.h"
5 | #include "error_handling.hpp"
6 | #include
7 |
8 | using std::size_t;
9 |
10 | namespace simple_fft {
11 | namespace copy_array {
12 |
13 | template
14 | void copyArray(const TComplexArray1D & data_from, TComplexArray1D & data_to,
15 | const size_t size)
16 | {
17 | int size_signed = static_cast(size);
18 |
19 | #ifndef __clang__
20 | #ifdef __USE_OPENMP
21 | #pragma omp parallel for
22 | #endif
23 | #endif
24 | for(int i = 0; i < size_signed; ++i) {
25 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
26 | data_to[i] = data_from[i];
27 | #else
28 | data_to(i) = data_from(i);
29 | #endif
30 | }
31 | }
32 |
33 | template
34 | void copyArray(const TRealArray1D & data_from, TComplexArray1D & data_to,
35 | const size_t size)
36 | {
37 | int size_signed = static_cast(size);
38 |
39 | // NOTE: user's complex type should have constructor like
40 | // "complex(real, imag)", where each of real and imag has
41 | // real type.
42 |
43 | #ifndef __clang__
44 | #ifdef __USE_OPENMP
45 | #pragma omp parallel for
46 | #endif
47 | #endif
48 | for(int i = 0; i < size_signed; ++i) {
49 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
50 | data_to[i] = complex_type(data_from[i], 0.0);
51 | #else
52 | data_to(i) = complex_type(data_from(i), 0.0);
53 | #endif
54 | }
55 | }
56 |
57 | template
58 | void copyArray(const TComplexArray2D & data_from, TComplexArray2D & data_to,
59 | const size_t size1, const size_t size2)
60 | {
61 | int size1_signed = static_cast(size1);
62 | int size2_signed = static_cast(size2);
63 |
64 | #ifndef __clang__
65 | #ifdef __USE_OPENMP
66 | #pragma omp parallel for
67 | #endif
68 | #endif
69 | for(int i = 0; i < size1_signed; ++i) {
70 | for(int j = 0; j < size2_signed; ++j) {
71 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
72 | data_to[i][j] = data_from[i][j];
73 | #else
74 | data_to(i,j) = data_from(i,j);
75 | #endif
76 | }
77 | }
78 | }
79 |
80 | template
81 | void copyArray(const TRealArray2D & data_from, TComplexArray2D & data_to,
82 | const size_t size1, const size_t size2)
83 | {
84 | int size1_signed = static_cast(size1);
85 | int size2_signed = static_cast(size2);
86 |
87 | // NOTE: user's complex type should have constructor like
88 | // "complex(real, imag)", where each of real and imag has
89 | // real type.
90 |
91 | #ifndef __clang__
92 | #ifdef __USE_OPENMP
93 | #pragma omp parallel for
94 | #endif
95 | #endif
96 | for(int i = 0; i < size1_signed; ++i) {
97 | for(int j = 0; j < size2_signed; ++j) {
98 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
99 | data_to[i][j] = complex_type(data_from[i][j], 0.0);
100 | #else
101 | data_to(i,j) = complex_type(data_from(i,j), 0.0);
102 | #endif
103 | }
104 | }
105 | }
106 |
107 | template
108 | void copyArray(const TComplexArray3D & data_from, TComplexArray3D & data_to,
109 | const size_t size1, const size_t size2, const size_t size3)
110 | {
111 | int size1_signed = static_cast(size1);
112 | int size2_signed = static_cast(size2);
113 | int size3_signed = static_cast(size3);
114 |
115 | #ifndef __clang__
116 | #ifdef __USE_OPENMP
117 | #pragma omp parallel for
118 | #endif
119 | #endif
120 | for(int i = 0; i < size1_signed; ++i) {
121 | for(int j = 0; j < size2_signed; ++j) {
122 | for(int k = 0; k < size3_signed; ++k) {
123 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
124 | data_to[i][j][k] = data_from[i][j][k];
125 | #else
126 | data_to(i,j,k) = data_from(i,j,k);
127 | #endif
128 | }
129 | }
130 | }
131 | }
132 |
133 | template
134 | void copyArray(const TRealArray3D & data_from, TComplexArray3D & data_to,
135 | const size_t size1, const size_t size2, const size_t size3)
136 | {
137 | int size1_signed = static_cast(size1);
138 | int size2_signed = static_cast(size2);
139 | int size3_signed = static_cast(size3);
140 |
141 | // NOTE: user's complex type should have constructor like
142 | // "complex(real, imag)", where each of real and imag has
143 | // real type.
144 |
145 | #ifndef __clang__
146 | #ifdef __USE_OPENMP
147 | #pragma omp parallel for
148 | #endif
149 | #endif
150 | for(int i = 0; i < size1_signed; ++i) {
151 | for(int j = 0; j < size2_signed; ++j) {
152 | for(int k = 0; k < size3_signed; ++k) {
153 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
154 | data_to[i][j][k] = complex_type(data_from[i][j][k], 0.0);
155 | #else
156 | data_to(i,j,k) = complex_type(data_from(i,j,k), 0.0);
157 | #endif
158 | }
159 | }
160 | }
161 | }
162 |
163 | } // namespace copy_array
164 | } // namespace simple_fft
165 |
166 | #endif // __SIMPLE_FFT__COPY_ARRAY_HPP
167 |
--------------------------------------------------------------------------------
/simple_fft/error_handling.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __SIMPLE_FFT__ERROR_HANDLING_HPP
2 | #define __SIMPLE_FFT__ERROR_HANDLING_HPP
3 |
4 | namespace simple_fft {
5 | namespace error_handling {
6 |
7 | enum EC_SimpleFFT
8 | {
9 | EC_SUCCESS = 0,
10 | EC_UNSUPPORTED_DIMENSIONALITY,
11 | EC_WRONG_FFT_DIRECTION,
12 | EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO,
13 | EC_NUM_OF_ELEMS_IS_ZERO,
14 | EC_WRONG_CHECK_FFT_MODE,
15 | EC_RELATIVE_ERROR_TOO_LARGE
16 | };
17 |
18 | inline void GetErrorDescription(const EC_SimpleFFT error_code,
19 | const char *& error_description)
20 | {
21 | switch(error_code)
22 | {
23 | case EC_SUCCESS:
24 | error_description = "Calculation was successful!";
25 | break;
26 | case EC_UNSUPPORTED_DIMENSIONALITY:
27 | error_description = "Unsupported dimensionality: currently only 1D, 2D "
28 | "and 3D arrays are supported";
29 | break;
30 | case EC_WRONG_FFT_DIRECTION:
31 | error_description = "Wrong direction for FFT was specified";
32 | break;
33 | case EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO:
34 | error_description = "Unsupported dimensionality: one of dimensions is not "
35 | "a power of 2";
36 | break;
37 | case EC_NUM_OF_ELEMS_IS_ZERO:
38 | error_description = "Number of elements for FFT or IFFT is zero!";
39 | break;
40 | case EC_WRONG_CHECK_FFT_MODE:
41 | error_description = "Wrong check FFT mode was specified (should be either "
42 | "Parseval theorem or energy conservation check";
43 | break;
44 | case EC_RELATIVE_ERROR_TOO_LARGE:
45 | error_description = "Relative error returned by FFT test exceeds specified "
46 | "relative tolerance";
47 | break;
48 | default:
49 | error_description = "Unknown error";
50 | break;
51 | }
52 | }
53 |
54 | } // namespace error_handling
55 | } // namespace simple_fft
56 |
57 | #endif // __SIMPLE_FFT__ERROR_HANDLING_HPP
58 |
--------------------------------------------------------------------------------
/simple_fft/fft.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ----------------------------------------------------------------------------
3 | * "THE BEER-WARE LICENSE" (Revision 42):
4 | * Dmitry Ivanov wrote this file. As long as you retain
5 | * this notice you can do whatever you want with this stuff. If we meet some day,
6 | * and you think this stuff is worth it, you can buy me a beer in return.
7 | * ----------------------------------------------------------------------------
8 | */
9 |
10 | #ifndef __SIMPLE_FFT__FFT_H__
11 | #define __SIMPLE_FFT__FFT_H__
12 |
13 | #include
14 |
15 | using std::size_t;
16 |
17 | /// The public API
18 | namespace simple_fft {
19 |
20 | /// FFT and IFFT functions
21 |
22 | // in-place, complex, forward
23 | template
24 | bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description);
25 |
26 | template
27 | bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,
28 | const char *& error_description);
29 |
30 | template
31 | bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
32 | const char *& error_description);
33 |
34 | // in-place, complex, inverse
35 | template
36 | bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description);
37 |
38 | template
39 | bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,
40 | const char *& error_description);
41 |
42 | template
43 | bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
44 | const char *& error_description);
45 |
46 | // not-in-place, complex, forward
47 | template
48 | bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
49 | const size_t size, const char *& error_description);
50 |
51 | template
52 | bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
53 | const size_t size1, const size_t size2, const char *& error_description);
54 |
55 | template
56 | bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
57 | const size_t size1, const size_t size2, const size_t size3,
58 | const char *& error_description);
59 |
60 | // not-in-place, complex, inverse
61 | template
62 | bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
63 | const size_t size, const char *& error_description);
64 |
65 | template
66 | bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
67 | const size_t size1, const size_t size2, const char *& error_description);
68 |
69 | template
70 | bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
71 | const size_t size1, const size_t size2, const size_t size3,
72 | const char *& error_description);
73 |
74 | // not-in-place, real, forward
75 | template
76 | bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,
77 | const size_t size, const char *& error_description);
78 |
79 | template
80 | bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,
81 | const size_t size1, const size_t size2, const char *& error_description);
82 |
83 | template
84 | bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,
85 | const size_t size1, const size_t size2, const size_t size3,
86 | const char *& error_description);
87 |
88 | // NOTE: There is no inverse transform from complex spectrum to real signal
89 | // because round-off errors during computation of inverse FFT lead to the appearance
90 | // of signal imaginary components even though they are small by absolute value.
91 | // These can be ignored but the author of this file thinks adding such an function
92 | // would be wrong methodogically: looking at complex result, you can estimate
93 | // the value of spurious imaginary part. Otherwise you may never know that IFFT
94 | // provides too large imaginary values due to too small grid size, for example.
95 |
96 | } // namespace simple_fft
97 |
98 | #endif // __SIMPLE_FFT__FFT_H__
99 |
100 | #include "fft.hpp"
101 |
--------------------------------------------------------------------------------
/simple_fft/fft.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __SIMPLE_FFT__FFT_HPP__
2 | #define __SIMPLE_FFT__FFT_HPP__
3 |
4 | #include "copy_array.hpp"
5 | #include "fft_impl.hpp"
6 |
7 | namespace simple_fft {
8 |
9 | // in-place, complex, forward
10 | template
11 | bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description)
12 | {
13 | return impl::CFFT::FFT_inplace(data, size, impl::FFT_FORWARD,
14 | error_description);
15 | }
16 |
17 | template
18 | bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,
19 | const char *& error_description)
20 | {
21 | return impl::CFFT::FFT_inplace(data, size1, size2, impl::FFT_FORWARD,
22 | error_description);
23 | }
24 |
25 | template
26 | bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
27 | const char *& error_description)
28 | {
29 | return impl::CFFT::FFT_inplace(data, size1, size2, size3,
30 | impl::FFT_FORWARD,
31 | error_description);
32 | }
33 |
34 | // in-place, complex, inverse
35 | template
36 | bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description)
37 | {
38 | return impl::CFFT::FFT_inplace(data, size, impl::FFT_BACKWARD,
39 | error_description);
40 | }
41 |
42 | template
43 | bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,
44 | const char *& error_description)
45 | {
46 | return impl::CFFT::FFT_inplace(data, size1, size2, impl::FFT_BACKWARD,
47 | error_description);
48 | }
49 |
50 | template
51 | bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
52 | const char *& error_description)
53 | {
54 | return impl::CFFT::FFT_inplace(data, size1, size2, size3,
55 | impl::FFT_BACKWARD,
56 | error_description);
57 | }
58 |
59 | // not-in-place, complex, forward
60 | template
61 | bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
62 | const size_t size, const char *& error_description)
63 | {
64 | copy_array::copyArray(data_in, data_out, size);
65 | return impl::CFFT::FFT_inplace(data_out, size, impl::FFT_FORWARD,
66 | error_description);
67 | }
68 |
69 | template
70 | bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
71 | const size_t size1, const size_t size2, const char *& error_description)
72 | {
73 | copy_array::copyArray(data_in, data_out, size1, size2);
74 | return impl::CFFT::FFT_inplace(data_out, size1, size2,
75 | impl::FFT_FORWARD,
76 | error_description);
77 | }
78 |
79 | template
80 | bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
81 | const size_t size1, const size_t size2, const size_t size3,
82 | const char *& error_description)
83 | {
84 | copy_array::copyArray(data_in, data_out, size1, size2, size3);
85 | return impl::CFFT::FFT_inplace(data_out, size1, size2, size3,
86 | impl::FFT_FORWARD,
87 | error_description);
88 | }
89 |
90 | // not-in-place, complex, inverse
91 | template
92 | bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
93 | const size_t size, const char *& error_description)
94 | {
95 | copy_array::copyArray(data_in, data_out, size);
96 | return impl::CFFT::FFT_inplace(data_out, size, impl::FFT_BACKWARD,
97 | error_description);
98 | }
99 |
100 | template
101 | bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
102 | const size_t size1, const size_t size2, const char *& error_description)
103 | {
104 | copy_array::copyArray(data_in, data_out, size1, size2);
105 | return impl::CFFT::FFT_inplace(data_out, size1, size2,
106 | impl::FFT_BACKWARD,
107 | error_description);
108 | }
109 |
110 | template
111 | bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
112 | const size_t size1, const size_t size2, const size_t size3,
113 | const char *& error_description)
114 | {
115 | copy_array::copyArray(data_in, data_out, size1, size2, size3);
116 | return impl::CFFT::FFT_inplace(data_out, size1, size2, size3,
117 | impl::FFT_BACKWARD,
118 | error_description);
119 | }
120 |
121 | // not-in-place, real, forward
122 | template
123 | bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,
124 | const size_t size, const char *& error_description)
125 | {
126 | copy_array::copyArray(data_in, data_out, size);
127 | return impl::CFFT::FFT_inplace(data_out, size,
128 | impl::FFT_FORWARD,
129 | error_description);
130 | }
131 |
132 | template
133 | bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,
134 | const size_t size1, const size_t size2, const char *& error_description)
135 | {
136 | copy_array::copyArray(data_in, data_out, size1, size2);
137 | return impl::CFFT::FFT_inplace(data_out, size1, size2,
138 | impl::FFT_FORWARD,
139 | error_description);
140 | }
141 |
142 | template
143 | bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,
144 | const size_t size1, const size_t size2, const size_t size3,
145 | const char *& error_description)
146 | {
147 | copy_array::copyArray(data_in, data_out, size1, size2, size3);
148 | return impl::CFFT::FFT_inplace(data_out, size1, size2, size3,
149 | impl::FFT_FORWARD,
150 | error_description);
151 | }
152 |
153 | } // simple_fft
154 |
155 | #endif // __SIMPLE_FFT__FFT_HPP__
156 |
--------------------------------------------------------------------------------
/simple_fft/fft_impl.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __SIMPLE_FFT__FFT_IMPL_HPP__
2 | #define __SIMPLE_FFT__FFT_IMPL_HPP__
3 |
4 | #include "fft_settings.h"
5 | #include "error_handling.hpp"
6 | #include
7 | #include
8 | #include
9 |
10 | using std::size_t;
11 |
12 | #ifndef M_PI
13 | #define M_PI 3.1415926535897932
14 | #endif
15 |
16 | namespace simple_fft {
17 | namespace impl {
18 |
19 | enum FFT_direction
20 | {
21 | FFT_FORWARD = 0,
22 | FFT_BACKWARD
23 | };
24 |
25 | // checking whether the size of array dimension is power of 2
26 | // via "complement and compare" method
27 | inline bool isPowerOfTwo(const size_t num)
28 | {
29 | if ((num == 0) || !(num & (~num + 1)))
30 | return false;
31 |
32 | return true;
33 | }
34 |
35 | inline bool checkNumElements(const size_t num_elements, const char *& error_description)
36 | {
37 | using namespace error_handling;
38 |
39 | if (!isPowerOfTwo(num_elements)) {
40 | GetErrorDescription(EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO, error_description);
41 | return false;
42 | }
43 |
44 | return true;
45 | }
46 |
47 | template
48 | inline void scaleValues(TComplexArray1D & data, const size_t num_elements)
49 | {
50 | real_type mult = 1.0 / num_elements;
51 | int num_elements_signed = static_cast(num_elements);
52 |
53 | #ifndef __clang__
54 | #ifdef __USE_OPENMP
55 | #pragma omp parallel for
56 | #endif
57 | #endif
58 | for(int i = 0; i < num_elements_signed; ++i) {
59 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
60 | data[i] *= mult;
61 | #else
62 | data(i) *= mult;
63 | #endif
64 | }
65 | }
66 |
67 | // NOTE: explicit template specialization for the case of std::vector
68 | // because it is used in 2D and 3D FFT for both array classes with square and round
69 | // brackets of element access operator; I need to guarantee that sub-FFT 1D will
70 | // use square brackets for element access operator anyway. It is pretty ugly
71 | // to duplicate the code but I haven't found more elegant solution.
72 | template <>
73 | inline void scaleValues >(std::vector & data,
74 | const size_t num_elements)
75 | {
76 | real_type mult = 1.0 / num_elements;
77 | int num_elements_signed = static_cast(num_elements);
78 |
79 | #ifndef __clang__
80 | #ifdef __USE_OPENMP
81 | #pragma omp parallel for
82 | #endif
83 | #endif
84 | for(int i = 0; i < num_elements_signed; ++i) {
85 | data[i] *= mult;
86 | }
87 | }
88 |
89 | template
90 | inline void bufferExchangeHelper(TComplexArray1D & data, const size_t index_from,
91 | const size_t index_to, complex_type & buf)
92 | {
93 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
94 | buf = data[index_from];
95 | data[index_from] = data[index_to];
96 | data[index_to]= buf;
97 | #else
98 | buf = data(index_from);
99 | data(index_from) = data(index_to);
100 | data(index_to)= buf;
101 | #endif
102 | }
103 |
104 | // NOTE: explicit template specialization for the case of std::vector
105 | // because it is used in 2D and 3D FFT for both array classes with square and round
106 | // brackets of element access operator; I need to guarantee that sub-FFT 1D will
107 | // use square brackets for element access operator anyway. It is pretty ugly
108 | // to duplicate the code but I haven't found more elegant solution.
109 | template <>
110 | inline void bufferExchangeHelper >(std::vector & data,
111 | const size_t index_from,
112 | const size_t index_to,
113 | complex_type & buf)
114 | {
115 | buf = data[index_from];
116 | data[index_from] = data[index_to];
117 | data[index_to]= buf;
118 | }
119 |
120 | template
121 | void rearrangeData(TComplexArray1D & data, const size_t num_elements)
122 | {
123 | complex_type buf;
124 |
125 | size_t target_index = 0;
126 | size_t bit_mask;
127 |
128 | for (size_t i = 0; i < num_elements; ++i)
129 | {
130 | if (target_index > i)
131 | {
132 | bufferExchangeHelper(data, target_index, i, buf);
133 | }
134 |
135 | // Initialize the bit mask
136 | bit_mask = num_elements;
137 |
138 | // While bit is 1
139 | while (target_index & (bit_mask >>= 1)) // bit_mask = bit_mask >> 1
140 | {
141 | // Drop bit:
142 | // & is bitwise AND,
143 | // ~ is bitwise NOT
144 | target_index &= ~bit_mask; // target_index = target_index & (~bit_mask)
145 | }
146 |
147 | // | is bitwise OR
148 | target_index |= bit_mask; // target_index = target_index | bit_mask
149 | }
150 | }
151 |
152 | template
153 | inline void fftTransformHelper(TComplexArray1D & data, const size_t match,
154 | const size_t k, complex_type & product,
155 | const complex_type factor)
156 | {
157 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
158 | product = data[match] * factor;
159 | data[match] = data[k] - product;
160 | data[k] += product;
161 | #else
162 | product = data(match) * factor;
163 | data(match) = data(k) - product;
164 | data(k) += product;
165 | #endif
166 | }
167 |
168 | // NOTE: explicit template specialization for the case of std::vector
169 | // because it is used in 2D and 3D FFT for both array classes with square and round
170 | // brackets of element access operator; I need to guarantee that sub-FFT 1D will
171 | // use square brackets for element access operator anyway. It is pretty ugly
172 | // to duplicate the code but I haven't found more elegant solution.
173 | template <>
174 | inline void fftTransformHelper >(std::vector & data,
175 | const size_t match,
176 | const size_t k,
177 | complex_type & product,
178 | const complex_type factor)
179 | {
180 | product = data[match] * factor;
181 | data[match] = data[k] - product;
182 | data[k] += product;
183 | }
184 |
185 | template
186 | bool makeTransform(TComplexArray1D & data, const size_t num_elements,
187 | const FFT_direction fft_direction, const char *& error_description)
188 | {
189 | using namespace error_handling;
190 | using std::sin;
191 |
192 | double local_pi;
193 | switch(fft_direction)
194 | {
195 | case(FFT_FORWARD):
196 | local_pi = -M_PI;
197 | break;
198 | case(FFT_BACKWARD):
199 | local_pi = M_PI;
200 | break;
201 | default:
202 | GetErrorDescription(EC_WRONG_FFT_DIRECTION, error_description);
203 | return false;
204 | }
205 |
206 | // declare variables to cycle the bits of initial signal
207 | size_t next, match;
208 | real_type sine;
209 | real_type delta;
210 | complex_type mult, factor, product;
211 |
212 | // NOTE: user's complex type should have constructor like
213 | // "complex(real, imag)", where each of real and imag has
214 | // real type.
215 |
216 | // cycle for all bit positions of initial signal
217 | for (size_t i = 1; i < num_elements; i <<= 1)
218 | {
219 | next = i << 1; // getting the next bit
220 | delta = local_pi / i; // angle increasing
221 | sine = sin(0.5 * delta); // supplementary sin
222 | // multiplier for trigonometric recurrence
223 | mult = complex_type(-2.0 * sine * sine, sin(delta));
224 | factor = 1.0; // start transform factor
225 |
226 | for (size_t j = 0; j < i; ++j) // iterations through groups
227 | // with different transform factors
228 | {
229 | for (size_t k = j; k < num_elements; k += next) // iterations through
230 | // pairs within group
231 | {
232 | match = k + i;
233 | fftTransformHelper(data, match, k, product, factor);
234 | }
235 | factor = mult * factor + factor;
236 | }
237 | }
238 |
239 | return true;
240 | }
241 |
242 | // Generic template for complex FFT followed by its explicit specializations
243 | template
244 | struct CFFT
245 | {};
246 |
247 | // 1D FFT:
248 | template
249 | struct CFFT
250 | {
251 | // NOTE: passing by pointer is needed to avoid using element access operator
252 | static bool FFT_inplace(TComplexArray1D & data, const size_t size,
253 | const FFT_direction fft_direction,
254 | const char *& error_description)
255 | {
256 | if(!checkNumElements(size, error_description)) {
257 | return false;
258 | }
259 |
260 | rearrangeData(data, size);
261 |
262 | if(!makeTransform(data, size, fft_direction, error_description)) {
263 | return false;
264 | }
265 |
266 | if (FFT_BACKWARD == fft_direction) {
267 | scaleValues(data, size);
268 | }
269 |
270 | return true;
271 | }
272 | };
273 |
274 | // 2D FFT
275 | template
276 | struct CFFT
277 | {
278 | static bool FFT_inplace(TComplexArray2D & data, const size_t size1, const size_t size2,
279 | const FFT_direction fft_direction, const char *& error_description)
280 | {
281 | int n_rows = static_cast(size1);
282 | int n_cols = static_cast(size2);
283 |
284 | // fft for columns
285 | std::vector subarray(n_rows); // each column has n_rows elements
286 |
287 | for(int j = 0; j < n_cols; ++j)
288 | {
289 | #ifndef __clang__
290 | #ifdef __USE_OPENMP
291 | #pragma omp parallel for
292 | #endif
293 | #endif
294 | for(int i = 0; i < n_rows; ++i) {
295 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
296 | subarray[i] = data[i][j];
297 | #else
298 | subarray[i] = data(i,j);
299 | #endif
300 | }
301 |
302 | if(!CFFT,1>::FFT_inplace(subarray, size1,
303 | fft_direction,
304 | error_description))
305 | {
306 | return false;
307 | }
308 |
309 | #ifndef __clang__
310 | #ifdef __USE_OPENMP
311 | #pragma omp parallel for
312 | #endif
313 | #endif
314 | for(int i = 0; i < n_rows; ++i) {
315 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
316 | data[i][j] = subarray[i];
317 | #else
318 | data(i,j) = subarray[i];
319 | #endif
320 | }
321 | }
322 |
323 | // fft for rows
324 | subarray.resize(n_cols); // each row has n_cols elements
325 |
326 | for(int i = 0; i < n_rows; ++i)
327 | {
328 | #ifndef __clang__
329 | #ifdef __USE_OPENMP
330 | #pragma omp parallel for
331 | #endif
332 | #endif
333 | for(int j = 0; j < n_cols; ++j) {
334 | #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
335 | subarray[j] = data[i][j];
336 | #else
337 | subarray[j] = data(i,j);
338 | #endif
339 | }
340 |
341 | if(!CFFT