/** * File: Fractal.cc * Copyright (C) 2025 Tyler Triplett * License: GNU GPL 3.0 or later <https://www.gnu.org/licenses/gpl-3.0.html> * * This is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "generators/Fractal.hh" #include "utils/safeJsonParse.hh" #include <cmath> namespace ty { std::vector<Setting> Fractal::getSettings() { return { { "zf", &zoomFactor_ }, { "rc", &realCenter_ }, { "ic", &imagCenter_ }, { "it", &maxIter_ }, }; } void Fractal::init(const json& j) { Generator::init(j); const auto settings = getSettings(); applyJson(j, settings); /// finished loading, compute member variables const auto [rangeX, rangeY] = scaleRange(); maxIter_ = std::clamp(maxIter_, 0, ITERLIMIT); minX_ = realCenter_ - rangeX; minY_ = imagCenter_ - rangeY; scaleX_ = (2 * rangeX) / width_; scaleY_ = (2 * rangeY) / height_; } std::pair<double, double> Fractal::scaleRange() const { /// const double scaledRange = RANGE / zoomFactor_; const double scaledRange = RANGE / std::pow(2, zoomFactor_); const double doubleWidth = width_; const double doubleHeight = height_; if (width_ >= height_) { const double scaled = scaledRange * doubleWidth / doubleHeight; return { scaled, scaledRange }; } else { const double scaled = scaledRange * doubleHeight / doubleWidth; return { scaledRange, scaled }; } } Pixel Fractal::pixelFromIt(const int& it) const { const double t = static_cast<double>(it) / maxIter_; const unsigned char r = 9 * (1 - t) * t * t * t * UCMAX; const unsigned char g = 15 * (1 - t) * (1 - t) * t * t * UCMAX; const unsigned char b = 8.5 * (1 - t) * (1 - t) * (1 - t) * t * UCMAX; return Pixel{ r, g, b }; } std::vector<unsigned char> Fractal::runGen() { const int size = width_ * height_ * 4; std::vector<unsigned char> image(size); #pragma omp parallel for schedule(dynamic, 16) for (int py = 0; py < height_; py++) { for (int px = 0; px < width_; px++) { const int iteration = computeIteration(px, py); const auto [r, g, b] = pixelFromIt(iteration); const int i = (py * width_ + px) * 4; image[i] = r; image[i + 1] = g; image[i + 2] = b; image[i + 3] = UCMAX; } } return image; } Point Fractal::pixelTranslation(const int& px, const int& py) const { const double real = minX_ + px * scaleX_; const double imag = minY_ + py * scaleY_; return Point(real, imag); } int Fractal::computeIteration(const int& px, const int& py) const { const auto c = pixelTranslation(px, py); Point z(0.0, 0.0); int iteration = 0; for (; abs(z) <= 2.0 && iteration < maxIter_; iteration++) z = nextZ(z, c); return iteration; } } /// ty