Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
arb/examples/complex_plot.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
684 lines (580 sloc)
16.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* This file is public domain. Author: Fredrik Johansson. */ | |
| #include <string.h> | |
| #include "acb.h" | |
| #include "acb_hypgeom.h" | |
| #include "acb_modular.h" | |
| #include "acb_elliptic.h" | |
| #include "flint/profiler.h" | |
| /* some useful color operations */ | |
| #define CLAMP(y) FLINT_MAX(0.0, FLINT_MIN((y), 1.0)) | |
| #define BLEND(x,y) (0.5*(x) + 0.5*(y)) | |
| #define DODGE(a,b) ((a) / ((1.0 - (b)) + 1/256.0)) | |
| /* HLS algorithm from python's colorsys module */ | |
| static double | |
| vv(double m1, double m2, double hue) | |
| { | |
| hue = hue - floor(hue); | |
| if (hue < 1/6.) | |
| return m1 + (m2-m1)*hue*6.0; | |
| if (hue < 0.5) | |
| return m2; | |
| if (hue < 2/3.) | |
| return m1 + (m2-m1)*(2/3.-hue)*6.0; | |
| return m1; | |
| } | |
| static void | |
| hls_to_rgb(double *R, double *G, double *B, double h, double l, double s) | |
| { | |
| double m1, m2; | |
| if (s == 0.0) | |
| { | |
| *R = *G = *B = l; | |
| return; | |
| } | |
| if (l <= 0.5) | |
| m2 = l * (1.0+s); | |
| else | |
| m2 = l+s-(l*s); | |
| m1 = 2.0*l - m2; | |
| *R = vv(m1, m2, h + 1/3.); | |
| *G = vv(m1, m2, h); | |
| *B = vv(m1, m2, h - 1/3.); | |
| } | |
| static void | |
| rgb_to_hls(double *H, double *L, double *S, double R, double G, double B) | |
| { | |
| double h, l, s, hi, lo, d; | |
| hi = FLINT_MAX(FLINT_MAX(R, G), B); | |
| lo = FLINT_MIN(FLINT_MIN(R, G), B); | |
| l = 0.5 * (lo + hi); | |
| d = hi - lo; | |
| if (hi == lo) | |
| { | |
| s = 0.0; | |
| h = 0.0; | |
| } | |
| else | |
| { | |
| if (l <= 0.5) | |
| s = d / (hi + lo); | |
| else | |
| s = d / (2.0 - hi - lo); | |
| if (d == 0.0) | |
| d = 1.0; | |
| if (R == hi) | |
| h = (G - B) / d; | |
| else if (G == hi) | |
| h = (B - R) / d + 2.0; | |
| else | |
| h = (R - G) / d + 4.0; | |
| h = h / 6.0; | |
| if (h < 0.0) | |
| h += 1.0; | |
| } | |
| *H = h; | |
| *L = l; | |
| *S = s; | |
| } | |
| /* color balance algorithm from gimp */ | |
| static double balance_channel(double value, double l, | |
| double shadows, double midtones, double highlights) | |
| { | |
| double a = 0.25, b = 0.333, scale = 0.7; | |
| shadows *= CLAMP((l - b) / (-a) + 0.5) * scale; | |
| midtones *= CLAMP((l - b) / ( a) + 0.5) * | |
| CLAMP((l + b - 1.0) / (-a) + 0.5) * scale; | |
| highlights *= CLAMP((l + b - 1.0) / ( a) + 0.5) * scale; | |
| value += shadows; | |
| value += midtones; | |
| value += highlights; | |
| return CLAMP(value); | |
| } | |
| static void balance(double * R, double * G, double * B, | |
| double ra, double rb, double rc, /* red shadows, midtones, highlights */ | |
| double ga, double gb, double gc, /* green */ | |
| double ba, double bb, double bc) /* blue */ | |
| { | |
| double h, l, s; | |
| double h2, l2, s2; | |
| rgb_to_hls(&h, &l, &s, *R, *G, *B); | |
| *R = balance_channel(*R, *R, ra, rb, rc); | |
| *G = balance_channel(*G, *G, ga, gb, gc); | |
| *B = balance_channel(*B, *B, ba, bb, bc); | |
| /* preserve lightness */ | |
| rgb_to_hls(&h2, &l2, &s2, *R, *G, *B); | |
| hls_to_rgb(R, G, B, h2, l, s2); | |
| } | |
| #define PI 3.1415926535898 | |
| const double blue_orange_colors[][4] = { | |
| {-1.0, 0.0, 0.0, 0.0}, | |
| {-0.95, 0.1, 0.2, 0.5}, | |
| {-0.5, 0.0, 0.5, 1.0}, | |
| {-0.05, 0.4, 0.8, 0.8}, | |
| { 0.0, 1.0, 1.0, 1.0}, | |
| { 0.05, 1.0, 0.9, 0.3}, | |
| { 0.5, 0.9, 0.5, 0.0}, | |
| { 0.95, 0.7, 0.1, 0.0}, | |
| { 1.0, 0.0, 0.0, 0.0}, | |
| { 2.0, 0.0, 0.0, 0.0}, | |
| }; | |
| void | |
| color_function(double * R, double * G, double * B, const acb_t z, int mode) | |
| { | |
| double H, L, S; | |
| slong prec, i; | |
| arb_t t, u; | |
| if (!acb_is_finite(z) || acb_rel_accuracy_bits(z) < 4) | |
| { | |
| *R = *G = *B = 0.5; | |
| return; | |
| } | |
| if (mode >= 2) | |
| { | |
| double R1, G1, B1; | |
| double R2, G2, B2; | |
| /* combine both color functions */ | |
| color_function(&R1, &G1, &B1, z, 0); | |
| color_function(&R2, &G2, &B2, z, 1); | |
| *R = BLEND(R1, CLAMP(DODGE(R1, R2))); | |
| *G = BLEND(G1, CLAMP(DODGE(G1, G2))); | |
| *B = BLEND(B1, CLAMP(DODGE(B1, B2))); | |
| /* then play with the levels */ | |
| if (mode == 3) | |
| balance(R, G, B, 0.0, -0.5, 0.2, 0.0, 0.0, -0.1, 0.0, -1.0, -0.2); | |
| else if (mode == 4) | |
| balance(R, G, B, 0.0, -0.5, 0.2, 0.0, 0.5, -0.1, 0.0, -0.3, -1.0); | |
| else if (mode == 5) | |
| balance(R, G, B, 0.0, -0.5, -1.0, 0.0, -0.1, -0.67, 0.0, -0.55, -0.12); | |
| else if (mode == 6) | |
| balance(R, G, B, 0.86, 0.0, 0.13, 0.57, 0.19, -0.52, 0.31, -0.30, -0.94); | |
| return; | |
| } | |
| arb_init(t); | |
| arb_init(u); | |
| prec = 32; | |
| arf_set_round(arb_midref(t), arb_midref(acb_realref(z)), prec, ARF_RND_DOWN); | |
| arf_set_round(arb_midref(u), arb_midref(acb_imagref(z)), prec, ARF_RND_DOWN); | |
| arb_atan2(t, u, t, prec); | |
| H = arf_get_d(arb_midref(t), ARF_RND_DOWN); | |
| if (mode == 0) | |
| { | |
| H = (H + PI) / (2 * PI) + 0.5; | |
| H = H - floor(H); | |
| acb_abs(t, z, prec); | |
| if (arf_cmpabs_2exp_si(arb_midref(t), 200) > 0) | |
| { | |
| L = 1.0; | |
| } | |
| else if (arf_cmpabs_2exp_si(arb_midref(t), -200) < 0) | |
| { | |
| L = 0.0; | |
| } | |
| else | |
| { | |
| L = arf_get_d(arb_midref(t), ARF_RND_DOWN); | |
| L = 1.0 - 1.0/(1.0 + pow(L, 0.2)); | |
| } | |
| S = 0.8; | |
| hls_to_rgb(R, G, B, H, L, S); | |
| } | |
| else | |
| { | |
| H = H / PI; | |
| H = FLINT_MAX(FLINT_MIN(H, 1.0), -1.0); | |
| for (i = 1; ; i++) | |
| { | |
| if (blue_orange_colors[i][0] > H) | |
| { | |
| double a, ra, ga, ba, b, rb, gb, bb, s; | |
| a = blue_orange_colors[i-1][0]; | |
| ra = blue_orange_colors[i-1][1]; | |
| ga = blue_orange_colors[i-1][2]; | |
| ba = blue_orange_colors[i-1][3]; | |
| b = blue_orange_colors[i][0]; | |
| rb = blue_orange_colors[i][1]; | |
| gb = blue_orange_colors[i][2]; | |
| bb = blue_orange_colors[i][3]; | |
| s = (H - a) / (b - a); | |
| *R = ra + (rb - ra) * s; | |
| *G = ga + (gb - ga) * s; | |
| *B = ba + (bb - ba) * s; | |
| break; | |
| } | |
| } | |
| } | |
| arb_clear(t); | |
| arb_clear(u); | |
| } | |
| typedef void (*func_ptr)(acb_t, const acb_t, slong); | |
| void | |
| ai(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_hypgeom_airy(res, NULL, NULL, NULL, z, prec); | |
| } | |
| void | |
| bi(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_hypgeom_airy(NULL, NULL, res, NULL, z, prec); | |
| } | |
| void | |
| besselj(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t nu; | |
| acb_init(nu); | |
| acb_hypgeom_bessel_j(res, nu, z, prec); | |
| acb_clear(nu); | |
| } | |
| void | |
| bessely(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t nu; | |
| acb_init(nu); | |
| acb_hypgeom_bessel_y(res, nu, z, prec); | |
| acb_clear(nu); | |
| } | |
| void | |
| besseli(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t nu; | |
| acb_init(nu); | |
| acb_hypgeom_bessel_i(res, nu, z, prec); | |
| acb_clear(nu); | |
| } | |
| void | |
| besselk(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t nu; | |
| acb_init(nu); | |
| acb_hypgeom_bessel_k(res, nu, z, prec); | |
| acb_clear(nu); | |
| } | |
| /* this function looks better when scaled */ | |
| void | |
| modj(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_modular_j(res, z, prec); | |
| acb_div_ui(res, res, 1728, prec); | |
| } | |
| void | |
| modjq(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t t; | |
| acb_init(t); | |
| acb_log(res, z, prec); | |
| acb_const_pi(t, prec); | |
| acb_div(res, res, t, prec); | |
| acb_mul_2exp_si(res, res, -1); | |
| acb_div_onei(res, res); | |
| acb_modular_j(res, res, prec); | |
| acb_div_ui(res, res, 1728, prec); | |
| acb_clear(t); | |
| } | |
| void | |
| modetaq(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t t; | |
| acb_init(t); | |
| acb_log(res, z, prec); | |
| acb_const_pi(t, prec); | |
| acb_div(res, res, t, prec); | |
| acb_mul_2exp_si(res, res, -1); | |
| acb_div_onei(res, res); | |
| acb_modular_eta(res, res, prec); | |
| acb_clear(t); | |
| } | |
| void | |
| modlambdaq(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_t t; | |
| acb_init(t); | |
| acb_log(res, z, prec); | |
| acb_const_pi(t, prec); | |
| acb_div(res, res, t, prec); | |
| acb_mul_2exp_si(res, res, -1); | |
| acb_div_onei(res, res); | |
| acb_modular_lambda(res, res, prec); | |
| acb_clear(t); | |
| } | |
| void | |
| ellipp(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_onei(res); | |
| acb_elliptic_p(res, z, res, prec); | |
| } | |
| void | |
| ellipzeta(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_onei(res); | |
| acb_elliptic_zeta(res, z, res, prec); | |
| } | |
| void | |
| ellipsigma(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_onei(res); | |
| acb_elliptic_sigma(res, z, res, prec); | |
| } | |
| void | |
| fresnels(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_hypgeom_fresnel(res, NULL, z, 0, prec); | |
| } | |
| void | |
| fresnelc(acb_t res, const acb_t z, slong prec) | |
| { | |
| acb_hypgeom_fresnel(NULL, res, z, 0, prec); | |
| } | |
| typedef struct | |
| { | |
| arf_ptr xa; | |
| arf_ptr xb; | |
| arf_ptr ya; | |
| arf_ptr yb; | |
| slong xnum; | |
| slong ynum; | |
| slong y; | |
| func_ptr func; | |
| unsigned char * buf; | |
| int color_mode; | |
| } | |
| work_t; | |
| void worker(slong x, work_t * work) | |
| { | |
| slong prec; | |
| acb_t z, w; | |
| arf_ptr xa, xb, ya, yb; | |
| slong xnum, ynum, y; | |
| double R, G, B; | |
| acb_init(z); | |
| acb_init(w); | |
| xa = work->xa; | |
| xb = work->xb; | |
| ya = work->ya; | |
| yb = work->yb; | |
| xnum = work->xnum; | |
| ynum = work->ynum; | |
| ynum = ynum; /* unused */ | |
| y = work->y; | |
| for (prec = 30; prec < 500; prec *= 2) | |
| { | |
| arf_sub(arb_midref(acb_imagref(z)), yb, ya, prec, ARF_RND_DOWN); | |
| arf_mul_ui(arb_midref(acb_imagref(z)), | |
| arb_midref(acb_imagref(z)), y, prec, ARF_RND_DOWN); | |
| arf_div_ui(arb_midref(acb_imagref(z)), | |
| arb_midref(acb_imagref(z)), ynum - 1, prec, ARF_RND_DOWN); | |
| arf_add(arb_midref(acb_imagref(z)), | |
| arb_midref(acb_imagref(z)), ya, prec, ARF_RND_DOWN); | |
| arf_sub(arb_midref(acb_realref(z)), xb, xa, prec, ARF_RND_DOWN); | |
| arf_mul_ui(arb_midref(acb_realref(z)), | |
| arb_midref(acb_realref(z)), x, prec, ARF_RND_DOWN); | |
| arf_div_ui(arb_midref(acb_realref(z)), | |
| arb_midref(acb_realref(z)), xnum - 1, prec, ARF_RND_DOWN); | |
| arf_add(arb_midref(acb_realref(z)), | |
| arb_midref(acb_realref(z)), xa, prec, ARF_RND_DOWN); | |
| work->func(w, z, prec); | |
| if (acb_rel_accuracy_bits(w) > 4) | |
| break; | |
| } | |
| color_function(&R, &G, &B, w, work->color_mode); | |
| work->buf[3 * (y * xnum + x) + 0] = FLINT_MIN(255, floor(R * 255)); | |
| work->buf[3 * (y * xnum + x) + 1] = FLINT_MIN(255, floor(G * 255)); | |
| work->buf[3 * (y * xnum + x) + 2] = FLINT_MIN(255, floor(B * 255)); | |
| acb_clear(z); | |
| acb_clear(w); | |
| } | |
| int main(int argc, char *argv[]) | |
| { | |
| slong x, y, xnum, ynum, i; | |
| double dxa, dxb, dya, dyb; | |
| FILE * fp; | |
| arf_t xa, xb, ya, yb; | |
| acb_t z, w; | |
| func_ptr func; | |
| int color_mode; | |
| unsigned char * buf; | |
| slong num_threads; | |
| if (argc < 2) | |
| { | |
| printf("complex_plot [-range xa xb ya yb] [-size xn yn] [-color n] [-threads n] <func>\n\n"); | |
| printf("Plots one of the predefined functions on [xa,xb] + [ya,yb]i\n"); | |
| printf("using domain coloring, at a resolution of xn by yn pixels.\n\n"); | |
| printf("Defaults parameters are [-10,10] + [-10,10]i and xn = yn = 512.\n\n"); | |
| printf("A color function can be selected with -color. The choices are:\n"); | |
| printf("0 phase=hue, magnitude=brightness\n"); | |
| printf("1 phase only, white-gold-black-blue-white counterclockwise\n"); | |
| printf("2 0+1 (dodge filter)\n"); | |
| printf("3 0+1, shiny\n"); | |
| printf("4 0+1, warm\n"); | |
| printf("5 0+1, cold\n"); | |
| printf("6 0+1, tomato\n\n"); | |
| printf("The output is written to arbplot.ppm. If you have ImageMagick,\n"); | |
| printf("run [convert arbplot.ppm arbplot.png] to get a PNG.\n\n"); | |
| printf("Function codes <func> are:\n"); | |
| printf(" sin - Sine\n"); | |
| printf(" gamma - Gamma function\n"); | |
| printf(" digamma - Digamma function\n"); | |
| printf(" lgamma - Logarithmic gamma function\n"); | |
| printf(" zeta - Riemann zeta function\n"); | |
| printf(" erf - Error function\n"); | |
| printf(" ai - Airy function Ai\n"); | |
| printf(" bi - Airy function Bi\n"); | |
| printf(" besselj - Bessel function J_0\n"); | |
| printf(" bessely - Bessel function Y_0\n"); | |
| printf(" besseli - Bessel function I_0\n"); | |
| printf(" besselk - Bessel function K_0\n"); | |
| printf(" modj - Modular j-function\n"); | |
| printf(" modjq - Modular j-function (as function of q)\n"); | |
| printf(" modeta - Dedekind eta function\n"); | |
| printf(" modetaq - Dedekind eta function (as function of q)\n"); | |
| printf(" modlambda - Modular lambda function\n"); | |
| printf(" modlambdaq - Modular lambda function (as function of q)\n"); | |
| printf(" ellipp - Weierstrass elliptic function (on square lattice)\n"); | |
| printf(" ellipzeta - Weierstrass elliptic function (on square lattice)\n"); | |
| printf(" ellipsigma - Weierstrass elliptic function (on square lattice)\n"); | |
| printf(" barnesg - Barnes G-function\n"); | |
| printf(" agm - Arithmetic geometric mean\n"); | |
| printf(" fresnels - Fresnel integral S\n"); | |
| printf(" fresnelc - Fresnel integral C\n\n"); | |
| return 1; | |
| } | |
| xnum = 512; | |
| ynum = 512; | |
| dxa = dya = -10; | |
| dxb = dyb = 10; | |
| func = acb_gamma; | |
| color_mode = 0; | |
| num_threads = 1; | |
| for (i = 1; i < argc; i++) | |
| { | |
| if (!strcmp(argv[i], "-size")) | |
| { | |
| xnum = atol(argv[i+1]); | |
| ynum = atol(argv[i+2]); | |
| i += 2; | |
| } | |
| else if (!strcmp(argv[i], "-range")) | |
| { | |
| dxa = atof(argv[i+1]); | |
| dxb = atof(argv[i+2]); | |
| dya = atof(argv[i+3]); | |
| dyb = atof(argv[i+4]); | |
| i += 4; | |
| } | |
| else if (!strcmp(argv[i], "-color")) | |
| { | |
| color_mode = atoi(argv[i+1]); | |
| i++; | |
| } | |
| else if (!strcmp(argv[i], "-threads")) | |
| { | |
| num_threads = atol(argv[i+1]); | |
| i++; | |
| } | |
| else if (!strcmp(argv[i], "sin")) | |
| func = acb_sin; | |
| else if (!strcmp(argv[i], "gamma")) | |
| func = acb_gamma; | |
| else if (!strcmp(argv[i], "digamma")) | |
| func = acb_digamma; | |
| else if (!strcmp(argv[i], "lgamma")) | |
| func = acb_lgamma; | |
| else if (!strcmp(argv[i], "zeta")) | |
| func = acb_zeta; | |
| else if (!strcmp(argv[i], "erf")) | |
| func = acb_hypgeom_erf; | |
| else if (!strcmp(argv[i], "ai")) | |
| func = ai; | |
| else if (!strcmp(argv[i], "bi")) | |
| func = bi; | |
| else if (!strcmp(argv[i], "besselj")) | |
| func = besselj; | |
| else if (!strcmp(argv[i], "bessely")) | |
| func = bessely; | |
| else if (!strcmp(argv[i], "besseli")) | |
| func = besseli; | |
| else if (!strcmp(argv[i], "besselk")) | |
| func = besselk; | |
| else if (!strcmp(argv[i], "modj")) | |
| func = modj; | |
| else if (!strcmp(argv[i], "modjq")) | |
| func = modjq; | |
| else if (!strcmp(argv[i], "modeta")) | |
| func = acb_modular_eta; | |
| else if (!strcmp(argv[i], "modetaq")) | |
| func = modetaq; | |
| else if (!strcmp(argv[i], "modlambda")) | |
| func = acb_modular_lambda; | |
| else if (!strcmp(argv[i], "modlambdaq")) | |
| func = modlambdaq; | |
| else if (!strcmp(argv[i], "ellipp")) | |
| func = ellipp; | |
| else if (!strcmp(argv[i], "ellipzeta")) | |
| func = ellipzeta; | |
| else if (!strcmp(argv[i], "ellipsigma")) | |
| func = ellipsigma; | |
| else if (!strcmp(argv[i], "barnesg")) | |
| func = acb_barnes_g; | |
| else if (!strcmp(argv[i], "agm")) | |
| func = acb_agm1; | |
| else if (!strcmp(argv[i], "fresnels")) | |
| func = fresnels; | |
| else if (!strcmp(argv[i], "fresnelc")) | |
| func = fresnelc; | |
| else | |
| { | |
| printf("unknown option: %s\n", argv[i]); | |
| return 1; | |
| } | |
| } | |
| acb_init(z); | |
| acb_init(w); | |
| arf_init(xa); | |
| arf_init(xb); | |
| arf_init(ya); | |
| arf_init(yb); | |
| arf_set_d(xa, dxa); | |
| arf_set_d(xb, dxb); | |
| arf_set_d(ya, dya); | |
| arf_set_d(yb, dyb); | |
| buf = flint_malloc(3 * xnum * ynum); | |
| flint_set_num_threads(num_threads); | |
| TIMEIT_ONCE_START | |
| for (y = ynum - 1; y >= 0; y--) | |
| { | |
| work_t work; | |
| if (y % (ynum / 16) == 0) | |
| printf("row %ld\n", y); | |
| work.xa = xa; | |
| work.xb = xb; | |
| work.ya = ya; | |
| work.yb = yb; | |
| work.xnum = xnum; | |
| work.ynum = ynum; | |
| work.y = y; | |
| work.func = func; | |
| work.buf = buf; | |
| work.color_mode = color_mode; | |
| flint_parallel_do((do_func_t) worker, &work, xnum, num_threads, FLINT_PARALLEL_STRIDED); | |
| } | |
| TIMEIT_ONCE_STOP | |
| fp = fopen("arbplot.ppm", "w"); | |
| fprintf(fp, "P6\n%ld %ld 255\n", xnum, ynum); | |
| for (y = ynum - 1; y >= 0; y--) | |
| { | |
| for (x = 0; x < xnum; x++) | |
| { | |
| fputc(buf[3 * (y * xnum + x) + 0], fp); | |
| fputc(buf[3 * (y * xnum + x) + 1], fp); | |
| fputc(buf[3 * (y * xnum + x) + 2], fp); | |
| } | |
| } | |
| flint_free(buf); | |
| fclose(fp); | |
| arf_clear(xa); | |
| arf_clear(xb); | |
| arf_clear(ya); | |
| arf_clear(yb); | |
| acb_clear(z); | |
| acb_clear(w); | |
| flint_cleanup_master(); | |
| return 0; | |
| } | |