可视化方程解析器

· · 科技·工程

我或许应该发在这个区,没什么用,挺好玩的?

设一个多项式 f(x),系数 a_i \in \mathbb{C}。平面上的每一个点当成一个复数的输入 in,设 out = f(in),那么 out 的俯角会被转化成 HSV 颜色,模长的倒数会转化成亮度(显然如果为 0 就是白色)。

你可以调整系数与多项式次数。直接拖动即可,毕竟这是复平面。

预编译:蓝奏云。

密码:67dg。

演示:

code

使用 ege 实现(编译记得开 release)。

#define SHOW_CONSOLE 

#include <graphics.h>
#include <iostream>
#include <complex>
#include <cmath>
#include <thread>
#include <mutex>
#include <atomic>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <cstdio>

using std::cin;
using std::vector;
using std::string;
using std::complex;
using std::mutex;
using std::lock_guard;

const int width = 1280;
const int height = 1280;
const double math_pi = 3.14159265358979323846;

const double x_min = -5.0, x_max = 5.0;
const double y_min = -5.0, y_max = 5.0;

const double vanish_rate = 0.3;

std::vector<std::complex<double>> coeffs = {
    { -1.0, 0.0 },
    {  0.0, 0.0 },
    {  0.0, 0.0 },
    {  1.0, 0.0 }
};

std::mutex coeff_mutex;
std::atomic<bool> running(true);
std::atomic<bool> need_redraw(true);
int dragged_idx = -1;

std::complex<double> screen_to_complex(int sx, int sy) {
    double rx = x_min + (x_max - x_min) * sx / width;
    double ry = y_max - (y_max - y_min) * sy / height;
    return std::complex<double>(rx, ry);
}

void complex_to_screen(std::complex<double> z, int &sx, int &sy) {
    sx = (int)((z.real() - x_min) / (x_max - x_min) * width);
    sy = (int)((y_max - z.imag()) / (y_max - y_min) * height);
}

std::complex<double> evaluate_polynomial(std::complex<double> z) {
    if (coeffs.empty()) return 0.0;
    std::complex<double> result = coeffs.back();
    for (int i = (int)coeffs.size() - 2; i >= 0; --i) {
        result = result * z + coeffs[i];
    }
    return result;
}

void console_input_thread() {
    std::string line;
    while (running) {
        printf("\n==================================================\n");
        printf(" [当前状态] 多项式次数 n = %d\n", (int)coeffs.size() - 1);
        printf("表达式: f(z) = ");
        for (int i = (int)coeffs.size() - 1; i >= 0; --i) {
            printf("(%g + %gi)", coeffs[i].real(), coeffs[i].imag());
            if (i > 0) printf("*z^%d + ", i);
        }
        printf("\n--------------------------------------------------\n");
        printf(" [可用指令示例]: \n");
        printf(" 1. 设置次数:  n = 4  (新高次项系数默认为 1) \n");
        printf(" 2. 设置系数:  a0 = 1.5 -2.0  或  a2 = 1.0\n");
        printf("--------------------------------------------------\n");
        printf("请输入指令: ");

        if (!std::getline(cin, line)) break;

        for (char &ch : line) {
            if (ch == '=') ch = ' ';
        }

        std::stringstream ss(line);
        std::string token;
        if (!(ss >> token)) continue;

        if (token == "n") {
            int new_n;
            if (ss >> new_n) {
                if (new_n >= 0 && new_n <= 30) { 
                    std::lock_guard<std::mutex> lock(coeff_mutex);
                    dragged_idx = -1;

                    int old_size = coeffs.size();
                    int new_size = new_n + 1;
                    coeffs.resize(new_size);

                    if (new_size > old_size) {
                        for (int i = old_size; i < new_size; ++i) {
                            coeffs[i] = { 1.0, 0.0 };
                        }
                    }
                    need_redraw = true;
                    printf(">> 成功: 方程次数已更新为 %d\n", new_n);
                } else {
                    printf(">> 错误: 次数限制在 0 到 30 之间. \n");
                }
            } else {
                printf(">> 错误: 格式不正确, 请输入类似 n = 4\n");
            }
        }
        else if (token[0] == 'a') {
            try {
                int idx = std::stoi(token.substr(1));
                double re = 0.0, im = 0.0;
                if (ss >> re) {
                    if (!(ss >> im)) {
                        im = 0.0;
                    }
                    std::lock_guard<std::mutex> lock(coeff_mutex);
                    if (idx >= 0 && idx < (int)coeffs.size()) {
                        coeffs[idx] = { re, im };
                        need_redraw = true;
                        printf(">> 成功: a%d 已更新为 %g + %gi\n", idx, re, im);
                    } else {
                        printf(">> 错误: 系数 a%d 越界, 当前最大可用系数为 a%d\n", idx, (int)coeffs.size() - 1);
                    }
                } else {
                    printf(">> 错误: 未检测到有效的实部数值. \n");
                }
            }
            catch (...) {
                printf(">> 错误: 无法解析系数指令, 请检查格式. \n");
            }
        }
        else {
            printf(">> 错误: 无法识别的指令. \n");
        }
    }
}

int main() {
    initgraph(width, height, INIT_RENDERMANUAL);
    setcaption("交互式多项式可视化 (单调变暗+动态次数) ");

    std::thread console_thread(console_input_thread);
    console_thread.detach();

    color_t *buffer = getbuffer((PIMAGE)NULL);

    while (is_run()) {
        while (mousemsg()) {
            mouse_msg msg = getmouse();
            std::complex<double> mouse_z = screen_to_complex(msg.x, msg.y);

            if (msg.is_down() && msg.is_left()) {
                std::lock_guard<std::mutex> lock(coeff_mutex);
                for (int i = 0; i < (int)coeffs.size(); ++i) {
                    if (abs(coeffs[i] - mouse_z) < 0.15) { 
                        dragged_idx = i;
                        break;
                    }
                }
            }
            else if (msg.is_up() && msg.is_left()) {
                dragged_idx = -1;
            }
            else if (msg.is_move() && dragged_idx != -1) {
                std::lock_guard<std::mutex> lock(coeff_mutex);
                coeffs[dragged_idx] = mouse_z;
                need_redraw = true;
            }
        }

        if (need_redraw) {
            std::lock_guard<std::mutex> lock(coeff_mutex);

            for (int py = 0; py < height; ++py) {
                for (int px = 0; px < width; ++px) {
                    std::complex<double> z = screen_to_complex(px, py);
                    std::complex<double> w = evaluate_polynomial(z);

                    double r = abs(w);
                    double theta = arg(w);

                    float h = (float)((theta + math_pi) / (2.0 * math_pi) * 360.0);
                    if (h >= 360.0f) h = 0.0f;

                    float s = (float)(1.0 - 1.0 / (1.0 + r * r * 80.0));

                    float v = (float)(1.0 / (1.0 + vanish_rate * r)); 

                    if (s < 0.0f) s = 0.0f; if (s > 1.0f) s = 1.0f;
                    if (v < 0.0f) v = 0.0f; if (v > 1.0f) v = 1.0f;

                    buffer[py * width + px] = hsv2rgb(h, s, v);
                }
            }

            for (int i = 0; i < (int)coeffs.size(); ++i) {
                int sx, sy;
                complex_to_screen(coeffs[i], sx, sy);
                setfillcolor(EGERGB(255, 255, 255));
                setcolor(EGERGB(0, 0, 0));
                fillcircle(sx, sy, 6);

                setcolor(EGERGB(255, 255, 255));
                char label[8];
                sprintf(label, "a%d", i);
                outtextxy(sx + 8, sy - 8, label);
            }

            need_redraw = false;
        }

        delay_fps(60);
    }

    running = false;
    closegraph();
    return 0;
}