* rename and pass flags to byte quoting functions: `byte_quote` -> `strquote_byte` * add `eb_print_style` to output data to a buffer in a given style * add `eb_print_field` to output a field name and value using styles * use styles in describe commands * improve byte stats and page layout output in `describe-buffer` * add `wall-chart` command (sorted list of bindings) * add `describe-help` command
1261 lines
40 KiB
C
1261 lines
40 KiB
C
/*
|
|
* QEmacs, character based fractal rendering
|
|
*
|
|
* Copyright (c) 2017-2024 Charlie Gordon.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include <math.h>
|
|
|
|
#include "qe.h"
|
|
#include "variables.h"
|
|
|
|
/*---------------- Fractint formula syntax ----------------*/
|
|
|
|
static const char fractint_keywords[] = {
|
|
"if|else|elseif|endif|pixel"
|
|
};
|
|
|
|
static const char fractint_types[] = {
|
|
""
|
|
};
|
|
|
|
enum {
|
|
FRACTINT_STYLE_DEFAULT = 0,
|
|
FRACTINT_STYLE_PREPROCESS = QE_STYLE_PREPROCESS,
|
|
FRACTINT_STYLE_COMMENT = QE_STYLE_COMMENT,
|
|
FRACTINT_STYLE_DEFINITION = QE_STYLE_TYPE,
|
|
FRACTINT_STYLE_NUMBER = QE_STYLE_NUMBER,
|
|
FRACTINT_STYLE_COLORS = QE_STYLE_STRING,
|
|
FRACTINT_STYLE_KEYWORD = QE_STYLE_KEYWORD,
|
|
FRACTINT_STYLE_FUNCTION = QE_STYLE_FUNCTION,
|
|
FRACTINT_STYLE_STRING = QE_STYLE_STRING,
|
|
FRACTINT_STYLE_STRING_Q = QE_STYLE_STRING_Q,
|
|
FRACTINT_STYLE_TYPE = QE_STYLE_TYPE,
|
|
};
|
|
|
|
/* fractint-mode colorization states */
|
|
enum {
|
|
IN_FRACTINT_COMMENT = 0x01, /* inside multiline comment */
|
|
IN_FRACTINT_BLOCK = 0x02, /* inside definition block */
|
|
IN_FRACTINT_COLORS = 0x04, /* inside a color palette definition */
|
|
IN_FRACTINT_STRING = 0x10, /* double-quoted string */
|
|
IN_FRACTINT_STRING_Q = 0x20, /* single-quoted string */
|
|
};
|
|
|
|
static void fractint_colorize_line(QEColorizeContext *cp,
|
|
const char32_t *str, int n,
|
|
QETermStyle *sbuf, ModeDef *syn)
|
|
{
|
|
int i = 0, start, indent, state, state1, style, klen;
|
|
char32_t c, delim;
|
|
char kbuf[64];
|
|
|
|
indent = cp_skip_blanks(str, 0, n);
|
|
state = cp->colorize_state;
|
|
start = i;
|
|
c = 0;
|
|
style = FRACTINT_STYLE_DEFAULT;
|
|
|
|
if (i >= n)
|
|
goto the_end;
|
|
|
|
if (state) {
|
|
/* if already in a state, go directly in the code parsing it */
|
|
if (state & IN_FRACTINT_COMMENT)
|
|
goto parse_comment;
|
|
if (state & IN_FRACTINT_COLORS) {
|
|
i = indent;
|
|
goto parse_colors;
|
|
}
|
|
if (state & IN_FRACTINT_STRING)
|
|
goto parse_string;
|
|
if (state & IN_FRACTINT_STRING_Q)
|
|
goto parse_string_q;
|
|
}
|
|
|
|
while (i < n) {
|
|
start = i;
|
|
c = str[i++];
|
|
switch (c) {
|
|
case ';': /* line comment */
|
|
style = FRACTINT_STYLE_COMMENT;
|
|
i = n;
|
|
break;
|
|
case ':': /* iteration definition */
|
|
style = FRACTINT_STYLE_KEYWORD;
|
|
break;
|
|
case '\'': /* character constant */
|
|
parse_string_q:
|
|
state1 = IN_FRACTINT_STRING_Q;
|
|
style = FRACTINT_STYLE_STRING_Q;
|
|
delim = '\'';
|
|
goto string;
|
|
|
|
case '\"': /* string literal */
|
|
parse_string:
|
|
state1 = IN_FRACTINT_STRING;
|
|
style = FRACTINT_STYLE_STRING;
|
|
delim = '\"';
|
|
string:
|
|
while (i < n) {
|
|
c = str[i++];
|
|
if (c == '\\') {
|
|
if (i >= n) {
|
|
/* continuation line */
|
|
state |= state1;
|
|
break;
|
|
}
|
|
i++;
|
|
} else
|
|
if (c == delim) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case '{':
|
|
if (state & IN_FRACTINT_BLOCK) {
|
|
/* a '{' inside a definition seems to start a comment */
|
|
/* consider it part of the comment, otherwise skip it */
|
|
goto parse_comment;
|
|
}
|
|
if (str[i] == '-' || str[i] == '=') {
|
|
/* invalid block, parse as comment */
|
|
start++;
|
|
goto parse_comment;
|
|
}
|
|
state |= IN_FRACTINT_BLOCK;
|
|
break;
|
|
case '}':
|
|
state &= ~IN_FRACTINT_COMMENT;
|
|
state &= ~IN_FRACTINT_BLOCK;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
continue;
|
|
default:
|
|
if (!(state & IN_FRACTINT_BLOCK)) {
|
|
klen = 0;
|
|
kbuf[klen++] = qe_tolower(c);
|
|
while (i < n && str[i] != '{') {
|
|
if (str[i] != ' ' && klen < countof(kbuf) - 1)
|
|
kbuf[klen++] = qe_tolower(str[i]);
|
|
i++;
|
|
}
|
|
if (kbuf[klen - 1] == '=')
|
|
klen--;
|
|
kbuf[klen] = '\0';
|
|
if (i >= n) {
|
|
style = FRACTINT_STYLE_COMMENT;
|
|
break;
|
|
}
|
|
if (strequal(kbuf, "comment")) {
|
|
SET_STYLE(sbuf, start, i, FRACTINT_STYLE_PREPROCESS);
|
|
start = i + 1;
|
|
parse_comment:
|
|
state |= IN_FRACTINT_COMMENT;
|
|
for (; i < n; i++) {
|
|
if (str[i] == '}') {
|
|
break;
|
|
}
|
|
}
|
|
style = FRACTINT_STYLE_COMMENT;
|
|
} else {
|
|
eb_add_tag(cp->b, cp->offset + start, kbuf);
|
|
style = FRACTINT_STYLE_DEFINITION;
|
|
}
|
|
break;
|
|
}
|
|
if (c == '.' || qe_isdigit(c)) {
|
|
int j;
|
|
// Integers:
|
|
// 0x[0-9a-fA-F]+ //
|
|
// [0-9][0-9]* //
|
|
// Floats:
|
|
// [0-9][0-9]*\.[0-9]*([eE][-\+]?[0-9]+)?
|
|
// [0-9][0-9]*(\.[0-9]*)?[eE][-\+]?[0-9]+
|
|
// number suffixes:
|
|
if (c == '0' && str[i] == 'x' && qe_isxdigit(str[i + 1])) {
|
|
for (i += 3; qe_isxdigit(str[i]); i++)
|
|
continue;
|
|
} else {
|
|
while (qe_isdigit(str[i]))
|
|
i++;
|
|
if (c != '.' && str[i] == '.' && qe_isdigit(str[i + 1])) {
|
|
for (i += 2; qe_isdigit(str[i]); i++)
|
|
continue;
|
|
}
|
|
if (str[i] == 'e' || str[i] == 'E') {
|
|
j = i + 1;
|
|
if (str[j] == '+' || str[j] == '-')
|
|
j++;
|
|
if (qe_isdigit(str[j])) {
|
|
for (i = j + 1; qe_isdigit(str[i]); i++)
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (str[i] == 'i' || str[i] == 'I') {
|
|
/* imaginary number */
|
|
i++;
|
|
}
|
|
if (!qe_isalpha_(str[i])) {
|
|
style = FRACTINT_STYLE_NUMBER;
|
|
break;
|
|
}
|
|
i = start + 1;
|
|
}
|
|
if (qe_isalpha_(c)) {
|
|
/* identifiers match:
|
|
* "[a-zA-Z_\x80-\xff][a-zA-Z_0-9\x80-\xff]*"
|
|
*/
|
|
klen = 0;
|
|
kbuf[klen++] = qe_tolower(c);
|
|
while (qe_isalnum_(str[i]) || str[i] == '.') {
|
|
if (klen < countof(kbuf) - 1)
|
|
kbuf[klen++] = qe_tolower(str[i]);
|
|
i++;
|
|
}
|
|
kbuf[klen] = '\0';
|
|
if (strfind(syn->keywords, kbuf)) {
|
|
style = FRACTINT_STYLE_KEYWORD;
|
|
break;
|
|
}
|
|
if (strfind(syn->types, kbuf)) {
|
|
style = FRACTINT_STYLE_TYPE;
|
|
break;
|
|
}
|
|
if (check_fcall(str, i)) {
|
|
/* function call */
|
|
style = FRACTINT_STYLE_FUNCTION;
|
|
break;
|
|
}
|
|
if (strequal(kbuf, "colors") && str[i] == '=') {
|
|
start = ++i;
|
|
parse_colors:
|
|
state &= ~IN_FRACTINT_COLORS;
|
|
for (; i < n; i++) {
|
|
c = str[i];
|
|
if (!qe_isalnum_(c) && c != '`' && c != '<' && c != '>')
|
|
break;
|
|
}
|
|
if (i == n - 1 && str[i] == '\\') {
|
|
state |= IN_FRACTINT_COLORS;
|
|
i++;
|
|
}
|
|
style = FRACTINT_STYLE_COLORS;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (style) {
|
|
SET_STYLE(sbuf, start, i, style);
|
|
style = 0;
|
|
}
|
|
}
|
|
the_end:
|
|
/* set style on eol char */
|
|
SET_STYLE1(sbuf, n, style);
|
|
|
|
cp->colorize_state = state;
|
|
}
|
|
|
|
static int fractint_mode_probe(ModeDef *mode, ModeProbeData *pd)
|
|
{
|
|
if (match_extension(pd->filename, mode->extensions)) {
|
|
/* This is a quick and dirty hack: assume Fractint formula
|
|
* files are located somewhere below a directory with a
|
|
* name relating to fractals.
|
|
*/
|
|
if (strstr(pd->real_filename, "frac")) {
|
|
/* Favor Fractint mode for formula files */
|
|
return 82;
|
|
} else {
|
|
/* Favor Visual Basic Form mode */
|
|
return 78;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static ModeDef fractint_mode = {
|
|
.name = "Fractint",
|
|
.extensions = "frm|par|ifs|l",
|
|
.mode_probe = fractint_mode_probe,
|
|
.colorize_func = fractint_colorize_line,
|
|
.keywords = fractint_keywords,
|
|
.types = fractint_types,
|
|
.fallback = &c_mode,
|
|
};
|
|
|
|
/*---------------- Interactive fractal explorer ----------------*/
|
|
|
|
#define USE_BITMAP_API 0 /* Using device bitmap API */
|
|
#define USE_DRAW_PICTURE 1 /* Using qe_draw_picture() */
|
|
|
|
static ModeDef fractal_mode;
|
|
|
|
#if 1
|
|
typedef long double fnum_t;
|
|
#define MFT "%.21Lg"
|
|
#else
|
|
typedef double fnum_t;
|
|
#define MFT "%.16g"
|
|
#endif
|
|
|
|
typedef struct { fnum_t a, b; } cnum_t;
|
|
|
|
typedef struct FractalState FractalState;
|
|
|
|
struct FractalState {
|
|
QEModeData base;
|
|
|
|
int width, height; /* fractal size in pixels */
|
|
int type; /* fractal type 0..8 */
|
|
int maxiter; /* maximum iteration number */
|
|
int cb, nc; /* color palette base and length */
|
|
int rot; /* rotation in degrees */
|
|
int zoom; /* zoom level in dB */
|
|
fnum_t scale; /* zoom factor = pow(10, -mzoom/10) */
|
|
fnum_t bailout; /* maximum squared module (default 4.0) */
|
|
fnum_t x, y; /* center position */
|
|
fnum_t m0, m1, m2, m3; /* rotation matrix */
|
|
int shift; /* color animation base */
|
|
QEColor colors[256]; /* color palette */
|
|
QEditScreen *screen; /* for bmp_free() */
|
|
QEBitmap *disp_bmp; /* device image */
|
|
#if USE_DRAW_PICTURE
|
|
QEPicture *ip;
|
|
#endif
|
|
};
|
|
|
|
static const char fractal_default_parameters[] = {
|
|
" type=0"
|
|
" maxiter=215"
|
|
" rot=0"
|
|
" zoom=0"
|
|
" bailout=4"
|
|
// This place zooms to level 180, scale=1e-18:
|
|
" x=-0.747698434201463097446 y=0.0794508470293983774563"
|
|
// This place on the X axis is interesting:
|
|
//" x=-1.78935604483808219844, y=0"
|
|
};
|
|
|
|
static inline FractalState *fractal_get_state(EditState *e, int status)
|
|
{
|
|
return qe_get_buffer_mode_data(e->b, &fractal_mode, status ? e : NULL);
|
|
}
|
|
|
|
static fnum_t cmod2(cnum_t z) {
|
|
return z.a * z.a + z.b * z.b;
|
|
}
|
|
|
|
static cnum_t cpower(cnum_t z, int exp) {
|
|
cnum_t r = { 1, 0 };
|
|
fnum_t a;
|
|
|
|
while (exp > 0) {
|
|
if (exp & 1) {
|
|
a = r.a;
|
|
r.a = a * z.a - r.b * z.b;
|
|
r.b = a * z.b + r.b * z.a;
|
|
}
|
|
exp >>= 1;
|
|
a = z.a;
|
|
z.a = a * a - z.b * z.b;
|
|
z.b = 2 * a * z.b;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int mandelbrot_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
fnum_t a, b, c;
|
|
int i;
|
|
for (a = b = 0, i = maxiter; a * a + b * b <= bailout && --i > 0;) {
|
|
c = a;
|
|
a = a * a - b * b + x;
|
|
b = 2 * c * b + y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot3_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
fnum_t a, b, c;
|
|
int i;
|
|
for (a = b = 0, i = maxiter; a * a + b * b <= bailout && --i > 0;) {
|
|
c = a;
|
|
a = a * a * a - 3 * a * b * b + x;
|
|
b = 3 * c * c * b - b * b * b + y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot4_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
fnum_t a, b, a2, b2;
|
|
int i;
|
|
for (a = b = 0, i = maxiter; a * a + b * b <= bailout && --i > 0;) {
|
|
a2 = a * a - b * b;
|
|
b2 = 2 * a * b;
|
|
a = a2 * a2 - b2 * b2 + x;
|
|
b = 2 * a2 * b2 + y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot5_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
fnum_t a, b, a2, b2, a3, b3;
|
|
int i;
|
|
for (a = b = 0, i = maxiter; a * a + b * b <= bailout && --i > 0;) {
|
|
a3 = a * a * a - 3 * a * b * b;
|
|
b3 = 3 * a * a * b - b * b * b;
|
|
a2 = a * a - b * b;
|
|
b2 = 2 * a * b;
|
|
a = a2 * a3 - b2 * b3 + x;
|
|
b = b2 * a3 + a2 * b3 + y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot6_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
fnum_t a, b, a3, b3;
|
|
int i;
|
|
for (a = b = 0, i = maxiter; a * a + b * b <= bailout && --i > 0;) {
|
|
a3 = a * a * a - 3 * a * b * b;
|
|
b3 = 3 * a * a * b - b * b * b;
|
|
a = a3 * a3 - b3 * b3 + x;
|
|
b = 2 * a3 * b3 + y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot7_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
cnum_t z = { 0, 0 };
|
|
int i;
|
|
for (i = maxiter; cmod2(z) <= bailout && --i > 0;) {
|
|
z = cpower(z, 7);
|
|
z.a += x;
|
|
z.b += y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot8_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
cnum_t z = { 0, 0 };
|
|
int i;
|
|
for (i = maxiter; cmod2(z) <= bailout && --i > 0;) {
|
|
z = cpower(z, 8);
|
|
z.a += x;
|
|
z.b += y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot9_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
cnum_t z = { 0, 0 };
|
|
int i;
|
|
for (i = maxiter; cmod2(z) <= bailout && --i > 0;) {
|
|
z = cpower(z, 9);
|
|
z.a += x;
|
|
z.b += y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static int mandelbrot10_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
cnum_t z = { 0, 0 };
|
|
int i;
|
|
for (i = maxiter; cmod2(z) <= bailout && --i > 0;) {
|
|
z = cpower(z, 10);
|
|
z.a += x;
|
|
z.b += y;
|
|
}
|
|
return maxiter - i;
|
|
}
|
|
|
|
static cnum_t newton_next(cnum_t z) {
|
|
fnum_t x2 = z.a * z.a;
|
|
fnum_t y2 = z.b * z.b;
|
|
fnum_t temp_deno = 3 * (x2 + y2) * (x2 + y2);
|
|
return (cnum_t){
|
|
z.a * 2 / 3 - (y2 - x2) / temp_deno,
|
|
z.b * 2 / 3 - (2 * z.a * z.b) / temp_deno
|
|
};
|
|
}
|
|
|
|
static int newton_func(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) {
|
|
cnum_t z = { x, y };
|
|
#define COS_PI_6 0.866025403784439 // sqrt(3) / 2
|
|
static cnum_t const roots[3] = {{ 1, 0 }, { -0.5, COS_PI_6 }, { -0.5, -COS_PI_6 }};
|
|
fnum_t min_dist = 1e-11;
|
|
int i;
|
|
for (i = 0; i < maxiter; i++) {
|
|
z = newton_next(z);
|
|
if (fabsl(z.a - roots[0].a) < min_dist
|
|
|| fabsl(z.b - roots[0].b) < min_dist
|
|
|| fabsl(z.a - roots[1].a) < min_dist
|
|
|| fabsl(z.b - roots[1].b) < min_dist
|
|
|| fabsl(z.a - roots[2].a) < min_dist
|
|
|| fabsl(z.b - roots[2].b) < min_dist)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static struct FractalType {
|
|
const char *name;
|
|
const char *formula;
|
|
int (*func)(fnum_t x, fnum_t y, fnum_t bailout, int maxiter);
|
|
} const fractal_type[] = {
|
|
{ "Mandelbrot", "z=z^2+c", mandelbrot_func },
|
|
{ "Mandelbrot3", "z=z^3+c", mandelbrot3_func },
|
|
{ "Mandelbrot4", "z=z^4+c", mandelbrot4_func },
|
|
{ "Mandelbrot5", "z=z^5+c", mandelbrot5_func },
|
|
{ "Mandelbrot6", "z=z^6+c", mandelbrot6_func },
|
|
{ "Mandelbrot7", "z=z^7+c", mandelbrot7_func },
|
|
{ "Mandelbrot8", "z=z^8+c", mandelbrot8_func },
|
|
{ "Mandelbrot9", "z=x^9+c", mandelbrot9_func },
|
|
{ "Mandelbrot10", "z=x^10+c", mandelbrot10_func },
|
|
{ "Newton", "z=(z^3-1)/(3*z^2)", newton_func },
|
|
};
|
|
|
|
static void fractal_invalidate(FractalState *ms) {
|
|
/* This will force fractal image recomputation */
|
|
/* XXX: color changes should not cause recomputation
|
|
if the fractal is computed as a paletted image */
|
|
ms->width = ms->height = 0;
|
|
}
|
|
|
|
static void fractal_set_rotation(FractalState *ms, int rot) {
|
|
ms->rot = rot;
|
|
/* compute rotation matrix */
|
|
ms->m0 = (fnum_t)cos(-ms->rot * M_PI / 180.0);
|
|
ms->m1 = (fnum_t)sin(-ms->rot * M_PI / 180.0);
|
|
ms->m2 = -ms->m1;
|
|
ms->m3 = ms->m0;
|
|
fractal_invalidate(ms);
|
|
}
|
|
|
|
static void fractal_set_zoom(FractalState *ms, int level) {
|
|
ms->zoom = level;
|
|
ms->scale = (fnum_t)pow(10.0, -ms->zoom / 10.0);
|
|
fractal_invalidate(ms);
|
|
}
|
|
|
|
static int fractal_get_color(const char *p, int *dac) {
|
|
/* convert a fractint 3 character color spec: 0-9A-Z_-z -> 0..63 */
|
|
int i;
|
|
for (i = 0; i < 3; i++) {
|
|
int c = p[i];
|
|
if (c >= '0' && c <= '9')
|
|
c -= '0';
|
|
else
|
|
if (c >= 'A' && c <= 'Z')
|
|
c -= 'A' - 10;
|
|
else
|
|
if (c >= '_' && c <= 'z')
|
|
c -= '_' - 36;
|
|
else
|
|
return 0;
|
|
dac[i] = (c << 2) | (c >> 4);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int fractal_set_colors(FractalState *ms, const char *p, const char **pp) {
|
|
/* Set the default colors */
|
|
blockcpy(ms->colors, xterm_colors, 256);
|
|
ms->cb = 16;
|
|
ms->nc = 216;
|
|
fractal_invalidate(ms);
|
|
|
|
if (p) {
|
|
if (strmatchword(p, "gray256", pp)) {
|
|
int c;
|
|
for (c = 1; c < 256; c++) {
|
|
ms->colors[256 - c] = QERGB(c, c, c);
|
|
}
|
|
ms->cb = 1;
|
|
ms->nc = 255;
|
|
} else
|
|
if (strmatchword(p, "gray", pp)) {
|
|
ms->cb = 232;
|
|
ms->nc = 24;
|
|
} else
|
|
if (!strmatchword(p, "default", pp)) {
|
|
int i, j, n;
|
|
int last[3] = { 0, 0, 0 }, dac[3] = { 0, 0, 0 };
|
|
|
|
/* parse a color fractint colors spec */
|
|
for (i = 0; i < 256; i++) {
|
|
if (!*p || *p == ',' || *p == ' ')
|
|
break;
|
|
n = 0;
|
|
if (*p == '<') {
|
|
if (i == 0)
|
|
return 0;
|
|
n = clamp_int(strtol_c(p + 1, &p, 10), 1, 255 - i);
|
|
if (*p != '>')
|
|
return 0;
|
|
p++;
|
|
memcpy(last, dac, sizeof dac);
|
|
i += n;
|
|
}
|
|
if (!fractal_get_color(p, dac)) {
|
|
return 0;
|
|
}
|
|
p += 3;
|
|
for (j = 1, ++n; j < n; j++) {
|
|
ms->colors[i - j] = QERGB((last[0] * j + dac[0] * (n - j)) / n,
|
|
(last[1] * j + dac[1] * (n - j)) / n,
|
|
(last[2] * j + dac[2] * (n - j)) / n);
|
|
}
|
|
ms->colors[i] = QERGB(dac[0], dac[1], dac[2]);
|
|
}
|
|
if (pp)
|
|
*pp = p;
|
|
ms->cb = 1;
|
|
ms->nc = i - ms->cb;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void fractal_set_parameters(EditState *s, FractalState *ms, const char *parms)
|
|
{
|
|
const char *p;
|
|
|
|
ms->width = ms->height = 0; /* force redraw */
|
|
|
|
for (p = parms;;) {
|
|
p += strspn(p, ";, \t\r\n");
|
|
if (*p == '\0')
|
|
break;
|
|
if (strstart(p, "type=", &p)) {
|
|
// XXX: should match type names
|
|
ms->type = clamp_int(strtol_c(p, &p, 0), 0, countof(fractal_type) - 1);
|
|
} else
|
|
if (strstart(p, "maxiter=", &p)) {
|
|
ms->maxiter = strtol_c(p, &p, 0);
|
|
} else
|
|
if (strstart(p, "colors=", &p)) {
|
|
if (!fractal_set_colors(ms, p, &p)) {
|
|
put_error(s, "Invalid colors: %s", p);
|
|
p += strcspn(p, ", ");
|
|
}
|
|
} else
|
|
if (strstart(p, "cb=", &p)) {
|
|
ms->cb = strtol_c(p, &p, 0);
|
|
} else
|
|
if (strstart(p, "nc=", &p)) {
|
|
ms->nc = strtol_c(p, &p, 0);
|
|
} else
|
|
if (strstart(p, "shift=", &p)) {
|
|
ms->shift = strtol_c(p, &p, 0);
|
|
} else
|
|
if (strstart(p, "rot=", &p)) {
|
|
fractal_set_rotation(ms, strtol_c(p, &p, 0));
|
|
} else
|
|
if (strstart(p, "zoom=", &p)) {
|
|
fractal_set_zoom(ms, strtol_c(p, &p, 0));
|
|
} else
|
|
if (strstart(p, "bailout=", &p)) {
|
|
ms->bailout = strtold_c(p, &p);
|
|
} else
|
|
if (strstart(p, "x=", &p)) {
|
|
ms->x = strtold_c(p, &p);
|
|
} else
|
|
if (strstart(p, "y=", &p)) {
|
|
ms->y = strtold_c(p, &p);
|
|
} else {
|
|
put_error(s, "Invalid parameter: %s", p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void do_fractal_draw(EditState *s, FractalState *ms)
|
|
{
|
|
#if USE_BITMAP_API
|
|
int width = ms->width, height = ms->height;
|
|
int maxiter = ms->maxiter + ms->zoom, nc = ms->nc;
|
|
int i, nx, ny, shift;
|
|
fnum_t x, y, dx, dy;
|
|
int (*func)(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) =
|
|
fractal_type[ms->type].func;
|
|
unsigned char *palette8 = NULL;
|
|
uint32_t *palette32 = NULL;
|
|
QEPicture pict;
|
|
|
|
if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 0)
|
|
return;
|
|
|
|
if (s->width == s->cols) {
|
|
/* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
|
|
dx = 32 * ms->scale / width / 10;
|
|
dy = dx * 12 / 10;
|
|
} else {
|
|
/* pixel based, assume 100% pixel aspect ratio */
|
|
dy = dx = 32 * ms->scale / width / 10;
|
|
}
|
|
if (ms->disp_bmp == NULL
|
|
|| ms->disp_bmp->width != width
|
|
|| ms->disp_bmp->height != height) {
|
|
bmp_free(ms->screen, &ms->disp_bmp);
|
|
/* create the displayed bitmap and put the image in it */
|
|
ms->screen = s->screen;
|
|
ms->disp_bmp = bmp_alloc(ms->screen, width, height, 0);
|
|
}
|
|
if (!ms->disp_bmp)
|
|
return;
|
|
|
|
bmp_lock(ms->screen, ms->disp_bmp, &pict, 0, 0, width, height);
|
|
|
|
/* Compute shifted palette */
|
|
shift = nc + ms->shift % nc; /* 0 < shift < 2 * nc */
|
|
if (pict.format == QEBITMAP_FORMAT_8BIT) {
|
|
palette8 = qe_malloc_array(unsigned char, maxiter + 1);
|
|
for (i = 0; i <= maxiter; i++) {
|
|
palette8[i] = (i >= maxiter) ? 0 : (ms->cb + (i + shift) % nc) & 255;
|
|
}
|
|
} else
|
|
if (pict.format == QEBITMAP_FORMAT_RGBA32) {
|
|
palette32 = qe_malloc_array(uint32_t, maxiter + 1);
|
|
for (i = 0; i <= maxiter; i++) {
|
|
palette32[i] = ms->colors[(i >= maxiter) ? 0 : (ms->cb + (i + shift) % nc) & 255];
|
|
}
|
|
}
|
|
|
|
/* Compute fractal bitmap */
|
|
for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
|
|
if (pict.format == QEBITMAP_FORMAT_8BIT) {
|
|
unsigned char *pb = pict.data[0] + ny * pict.linesize[0];
|
|
for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
|
|
fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
|
|
fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
|
|
pb[nx] = palette8[(*func)(xr, yr, ms->bailout, maxiter)];
|
|
}
|
|
} else
|
|
if (pict.format == QEBITMAP_FORMAT_RGBA32) {
|
|
uint32_t *pb = (uint32_t *)(void*)(pict.data[0] + ny * pict.linesize[0]);
|
|
for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
|
|
fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
|
|
fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
|
|
pb[nx] = palette32[(*func)(xr, yr, ms->bailout, maxiter)];
|
|
}
|
|
}
|
|
}
|
|
bmp_unlock(ms->screen, ms->disp_bmp);
|
|
qe_free(&palette8);
|
|
qe_free(&palette32);
|
|
edit_invalidate(s, 1);
|
|
#elif USE_DRAW_PICTURE
|
|
int width = ms->width, height = ms->height, zoom = ms->zoom;
|
|
int maxiter = ms->maxiter + zoom, cb = ms->cb, nc = ms->nc;
|
|
int i, nx, ny;
|
|
fnum_t xc = ms->x, yc = ms->y, scale = ms->scale;
|
|
fnum_t bailout = ms->bailout;
|
|
fnum_t x, y, dx, dy, xr, yr;
|
|
int (*func)(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) =
|
|
fractal_type[ms->type].func;
|
|
|
|
if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 0)
|
|
return;
|
|
|
|
if (s->width == s->cols) {
|
|
/* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
|
|
dx = 32 * scale / width / 10;
|
|
dy = dx * 12 / 10;
|
|
} else {
|
|
/* pixel based, assume 100% pixel aspect ratio */
|
|
dy = dx = 32 * scale / width / 10;
|
|
}
|
|
if (ms->ip == NULL || ms->ip->width != width || ms->ip->height != height) {
|
|
qe_free_picture(&ms->ip);
|
|
ms->ip = qe_create_picture(width, height, QEBITMAP_FORMAT_8BIT, 0);
|
|
}
|
|
if (!ms->ip)
|
|
return;
|
|
|
|
for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
|
|
unsigned char *pb = ms->ip->data[0] + ny * ms->ip->linesize[0];
|
|
for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
|
|
xr = xc + x * ms->m0 + y * ms->m1;
|
|
yr = yc + x * ms->m2 + y * ms->m3;
|
|
i = (*func)(xr, yr, bailout, maxiter);
|
|
pb[nx] = (i >= maxiter) ? 0 : cb + i % nc;
|
|
}
|
|
}
|
|
edit_invalidate(s, 1);
|
|
#else
|
|
int width = ms->width, height = ms->height / 2;
|
|
int maxiter = ms->maxiter + ms->zoom, nc = ms->nc;
|
|
int i, nx, ny, fg, bg, shift;
|
|
fnum_t x, y, dx, dy;
|
|
int (*func)(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) =
|
|
fractal_type[ms->type].func;
|
|
unsigned char *palette8 = NULL;
|
|
|
|
if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 0)
|
|
return;
|
|
|
|
dx = 3.2 * ms->scale / width;
|
|
if (s->width == s->cols) {
|
|
/* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
|
|
dy = dx * 2.4;
|
|
} else {
|
|
/* pixel based, assume 100% pixel aspect ratio */
|
|
dy = dx * width / s->width * s->height / height;
|
|
}
|
|
|
|
/* Compute shifted palette */
|
|
shift = nc + ms->shift % nc; /* 0 < shift < 2 * nc */
|
|
palette8 = qe_malloc_array(unsigned char, maxiter + 1);
|
|
for (i = 0; i <= maxiter; i++) {
|
|
palette8[i] = (i >= maxiter) ? 0 : (ms->cb + (i + shift) % nc) & 255;
|
|
}
|
|
|
|
s->b->flags &= ~BF_READONLY;
|
|
|
|
eb_delete_range(s->b, 0, s->b->total_size);
|
|
|
|
for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
|
|
for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
|
|
fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
|
|
fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
|
|
bg = palette8[(*func)(xr, yr, ms->bailout, maxiter)];
|
|
xr += dy / 2 * ms->m1;
|
|
yr += dy / 2 * ms->m3;
|
|
fg = palette8[(*func)(xr, yr, ms->bailout, maxiter)];
|
|
s->b->cur_style = QE_TERM_COMPOSITE | QE_TERM_MAKE_COLOR(fg, bg);
|
|
eb_insert_char32(s->b, s->b->total_size, fg == bg ? ' ' : 0x2584);
|
|
}
|
|
s->b->cur_style = QE_STYLE_DEFAULT;
|
|
eb_insert_char32(s->b, s->b->total_size, '\n');
|
|
}
|
|
s->b->flags |= BF_READONLY;
|
|
qe_free(&palette8);
|
|
#endif
|
|
}
|
|
|
|
#if USE_BITMAP_API
|
|
static void fractal_display(EditState *s) {
|
|
FractalState *ms = fractal_get_state(s, 0);
|
|
QEColor col = qe_styles[QE_STYLE_GUTTER].bg_color;
|
|
|
|
if (s->display_invalid) {
|
|
if (ms && ms->disp_bmp) {
|
|
int w = min_int(s->width, ms->disp_bmp->width);
|
|
int h = min_int(s->height, ms->disp_bmp->height / s->screen->dpy.yfactor);
|
|
int x = (s->width - w) / 2;
|
|
int y = (s->height - h) / 2;
|
|
|
|
bmp_draw(s->screen, ms->disp_bmp,
|
|
s->xleft + x, s->ytop + y, w, h, 0, 0, 0);
|
|
fill_window_slack(s, x, y, w, h, col);
|
|
} else {
|
|
fill_rectangle(s->screen, s->xleft, s->ytop, s->width, s->height, col);
|
|
}
|
|
s->display_invalid = 0;
|
|
}
|
|
if (s->qs->active_window == s) {
|
|
/* Update cursor */
|
|
int xc = s->xleft;
|
|
int yc = s->ytop;
|
|
int w = s->char_width;
|
|
int h = s->line_height;
|
|
if (s->screen->dpy.dpy_cursor_at) {
|
|
/* hardware cursor */
|
|
s->screen->dpy.dpy_cursor_at(s->screen, xc, yc, w, h);
|
|
} else {
|
|
xor_rectangle(s->screen, xc, yc, w, h, QERGB(0xFF, 0xFF, 0xFF));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if USE_DRAW_PICTURE
|
|
static void fractal_display(EditState *s) {
|
|
FractalState *ms = fractal_get_state(s, 0);
|
|
QEColor col = qe_styles[QE_STYLE_GUTTER].bg_color;
|
|
|
|
if (s->display_invalid) {
|
|
if (ms && ms->ip) {
|
|
int w = min_int(s->width, ms->ip->width);
|
|
int h = min_int(s->height, ms->ip->height / s->screen->dpy.yfactor);
|
|
int x0 = (s->width - w) / 2;
|
|
int y0 = (s->height - h) / 2;
|
|
uint32_t palette[256];
|
|
int c;
|
|
|
|
palette[0] = ms->colors[0];
|
|
for (c = 1; c < 256; c++) {
|
|
palette[c] = ms->colors[(c + ms->shift) & 255];
|
|
}
|
|
ms->ip->palette = palette;
|
|
ms->ip->palette_size = 256;
|
|
qe_draw_picture(s->screen, s->xleft + x0, s->ytop + y0, w, h,
|
|
ms->ip, 0, 0, w, h * s->screen->dpy.yfactor,
|
|
0, QERGB(128, 128, 128));
|
|
ms->ip->palette = NULL;
|
|
fill_window_slack(s, x0, y0, w, h, col);
|
|
} else {
|
|
fill_rectangle(s->screen, s->xleft, s->ytop, s->width, s->height, col);
|
|
}
|
|
s->display_invalid = 0;
|
|
}
|
|
if (s->qs->active_window == s) {
|
|
/* Update cursor */
|
|
int xc = s->xleft;
|
|
int yc = s->ytop;
|
|
int w = s->char_width;
|
|
int h = s->line_height;
|
|
if (s->screen->dpy.dpy_cursor_at) {
|
|
/* hardware cursor */
|
|
s->screen->dpy.dpy_cursor_at(s->screen, xc, yc, w, h);
|
|
} else {
|
|
xor_rectangle(s->screen, xc, yc, w, h, QERGB(0xFF, 0xFF, 0xFF));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void do_fractal_move(EditState *s, int deltax, int deltay) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
fnum_t dx = deltax * ms->scale / 40;
|
|
fnum_t dy = deltay * ms->scale / 40;
|
|
ms->x += dx * ms->m0 + dy * ms->m1;
|
|
ms->y += dx * ms->m2 + dy * ms->m3;
|
|
fractal_invalidate(ms);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_move_x(EditState *s, int n) {
|
|
do_fractal_move(s, n, 0);
|
|
}
|
|
|
|
static void do_fractal_move_y(EditState *s, int n) {
|
|
do_fractal_move(s, 0, n);
|
|
}
|
|
|
|
static void do_fractal_zoom(EditState *s, int n) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
fractal_set_zoom(ms, ms->zoom + n);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_rotate(EditState *s, int n) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
fractal_set_rotation(ms, n ? ms->rot + n : 0);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_shift_colors(EditState *s, int n) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
ms->shift += n;
|
|
#if USE_BITMAP_API
|
|
fractal_invalidate(ms);
|
|
#else
|
|
edit_invalidate(s, 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void do_fractal_set_colors(EditState *s, int type) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
ms->shift = 0;
|
|
if (type == 0) {
|
|
fractal_set_colors(ms, NULL, NULL);
|
|
} else
|
|
if (type == 1) {
|
|
fractal_set_colors(ms, "gray", NULL);
|
|
}
|
|
#if USE_BITMAP_API
|
|
fractal_invalidate(ms);
|
|
#else
|
|
edit_invalidate(s, 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void do_fractal_iter(EditState *s, int n) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
ms->maxiter += n;
|
|
fractal_invalidate(ms);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_bailout(EditState *s, int n) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
ms->bailout += n;
|
|
fractal_invalidate(ms);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_set_parameters(EditState *s, const char *params) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
fractal_set_parameters(s, ms, params);
|
|
}
|
|
}
|
|
|
|
static void do_fractal_set_type(EditState *s, int key) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
fractal_set_parameters(s, ms, fractal_default_parameters);
|
|
ms->type = key - '1';
|
|
if (ms->type) {
|
|
fractal_set_parameters(s, ms, "rot=0 zoom=0 x=0 y=0");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void do_fractal_help(EditState *s)
|
|
{
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
EditBuffer *b;
|
|
|
|
if (ms == NULL)
|
|
return;
|
|
|
|
b = new_help_buffer(s);
|
|
if (!b)
|
|
return;
|
|
|
|
eb_print_style(b, DESCRIBE_STYLE_HEAD, "Fractal description:\n\n");
|
|
|
|
b->tab_width = 16;
|
|
eb_print_field(b, "type", "%s\n", fractal_type[ms->type].name);
|
|
eb_print_field(b, "formula", "%s\n", fractal_type[ms->type].formula);
|
|
eb_print_field(b, "x", ""MFT"\n", ms->x);
|
|
eb_print_field(b, "y", ""MFT"\n", ms->y);
|
|
eb_print_field(b, "size", "%dx%d\n", ms->width, ms->height);
|
|
eb_print_field(b, "zoom", "%d\n", ms->zoom);
|
|
eb_print_field(b, "scale", "%.6g\n", (double)ms->scale);
|
|
eb_print_field(b, "rot", "%d\n", ms->rot);
|
|
eb_print_field(b, "bailout", ""MFT"\n", ms->bailout);
|
|
eb_print_field(b, "maxiter", "%d\n", ms->maxiter);
|
|
eb_print_field(b, "colors", "cb=%d nc=%d shift=%d\n", ms->cb, ms->nc, ms->shift);
|
|
|
|
eb_print_style(b, DESCRIBE_STYLE_HEAD, "\nFractal navigator:\n\n");
|
|
|
|
// XXX: should use print_bindings(b, "Fractal commands", 0, &fractal_mode);
|
|
|
|
eb_print_field(b, "left, right", "%s\n", "move center point horizontally");
|
|
eb_print_field(b, "up, down", "%s\n", "move center point vertically");
|
|
eb_print_field(b, "+, SP", "%s\n", "zoom in");
|
|
eb_print_field(b, "-, _", "%s\n", "zoom out");
|
|
eb_print_field(b, "/", "%s\n", "rotate right");
|
|
eb_print_field(b, "\\, .", "%s\n", "rotate left");
|
|
eb_print_field(b, "|", "%s\n", "reset rotation");
|
|
eb_print_field(b, "{, }", "%s\n", "change maxiter");
|
|
eb_print_field(b, "[, ]", "%s\n", "shift colors");
|
|
eb_print_field(b, "<, >", "%s\n", "change bailout");
|
|
eb_print_field(b, "=", "%s\n", "set fractal parameters");
|
|
eb_print_field(b, "g", "%s\n", "set gray colors");
|
|
eb_print_field(b, "c", "%s\n", "set default colors");
|
|
|
|
show_popup(s, b, "About Fractal");
|
|
}
|
|
|
|
static void fractal_display_hook(EditState *s) {
|
|
FractalState *ms = fractal_get_state(s, 0);
|
|
if (ms) {
|
|
#if USE_BITMAP_API || USE_DRAW_PICTURE
|
|
int width = s->width;
|
|
int height = s->height * s->screen->dpy.yfactor;
|
|
#else
|
|
int width = s->cols;
|
|
int height = s->rows * 2;
|
|
#endif
|
|
if (s->xleft == 0 && s->ytop == 0
|
|
&& (ms->height != height || ms->width != width)) {
|
|
/* XXX: should use a separate thread for this */
|
|
/* XXX: should use a different bitmap for each window */
|
|
ms->width = width;
|
|
ms->height = height;
|
|
do_fractal_draw(s, ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const CmdDef fractal_commands[] = {
|
|
CMD2( "fractal-left", "left",
|
|
"Move fractal origin left",
|
|
do_fractal_move_x, ESi, "q")
|
|
CMD2( "fractal-right", "right",
|
|
"Move fractal origin right",
|
|
do_fractal_move_x, ESi, "p")
|
|
CMD2( "fractal-up", "up",
|
|
"Move fractal origin up",
|
|
do_fractal_move_y, ESi, "q")
|
|
CMD2( "fractal-down", "down",
|
|
"Move fractal origin down",
|
|
do_fractal_move_y, ESi, "p")
|
|
CMD2( "fractal-zoom-in", "+, SPC",
|
|
"Increase fractal zoom level",
|
|
do_fractal_zoom, ESi, "p")
|
|
CMD2( "fractal-zoom-out", "-, _",
|
|
"Decrease fractal zoom level",
|
|
do_fractal_zoom, ESi, "q")
|
|
CMD2( "fractal-rotate-left", "\\, .",
|
|
"Rotate fractal figure counterclockwise",
|
|
do_fractal_rotate, ESi, "p")
|
|
CMD2( "fractal-rotate-right", "/",
|
|
"Rotate fractal figure clockwise",
|
|
do_fractal_rotate, ESi, "q")
|
|
CMD3( "fractal-rotate-none", "|",
|
|
"Reset fractal rotation",
|
|
do_fractal_rotate, ESi, "v", 0)
|
|
CMD3( "fractal-set-colors-default", "c",
|
|
"Reset fractal colors to default",
|
|
do_fractal_set_colors, ESi, "v", 0)
|
|
CMD3( "fractal-set-colors-gray", "g",
|
|
"Set fractal colors to gray scale",
|
|
do_fractal_set_colors, ESi, "v", 1)
|
|
CMD2( "fractal-shift-colors-left", "[",
|
|
"Shift fractal color palette left",
|
|
do_fractal_shift_colors, ESi, "q")
|
|
CMD2( "fractal-shift-colors-right", "]",
|
|
"Shift fractal color palette right",
|
|
do_fractal_shift_colors, ESi, "p")
|
|
CMD2( "fractal-iter-less", "{",
|
|
"Decrease the fractal iteration count",
|
|
do_fractal_iter, ESi, "q")
|
|
CMD2( "fractal-iter-more", "}",
|
|
"Increase the fractal iteration count",
|
|
do_fractal_iter, ESi, "p")
|
|
CMD2( "fractal-bailout-less", "<",
|
|
"Decrease the fractal bailout value",
|
|
do_fractal_bailout, ESi, "q")
|
|
CMD2( "fractal-bailout-more", ">",
|
|
"Increase the fractal bailout value",
|
|
do_fractal_bailout, ESi, "p")
|
|
CMD2( "fractal-set-type", "1, 2, 3, 4, 5, 6, 7, 8, 9",
|
|
"Select the fractal type (1-9)",
|
|
do_fractal_set_type, ESi, "k")
|
|
CMD2( "fractal-set-parameters", "=",
|
|
"Set the fractal parameters",
|
|
do_fractal_set_parameters, ESs,
|
|
"s{Fractal parameters: }[mparm]|mparm|")
|
|
CMD0( "fractal-help", "?, f1",
|
|
"Show the fractal information and help window",
|
|
do_fractal_help)
|
|
};
|
|
|
|
static int fractal_mode_probe(ModeDef *mode, ModeProbeData *p)
|
|
{
|
|
if (qe_get_buffer_mode_data(p->b, &fractal_mode, NULL))
|
|
return 100;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int fractal_mode_init(EditState *e, EditBuffer *b, int flags)
|
|
{
|
|
if (e && (flags & MODEF_NEWINSTANCE)) {
|
|
FractalState *ms;
|
|
|
|
if (!(ms = fractal_get_state(e, 0)))
|
|
return -1;
|
|
|
|
fractal_set_parameters(e, ms, fractal_default_parameters);
|
|
fractal_set_colors(ms, NULL, NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void fractal_mode_free(EditBuffer *b, void *state) {
|
|
#if USE_BITMAP_API
|
|
FractalState *ms = state;
|
|
|
|
bmp_free(ms->screen, &ms->disp_bmp);
|
|
#endif
|
|
#if USE_DRAW_PICTURE
|
|
FractalState *ms = state;
|
|
|
|
/* free bitmap and palette */
|
|
qe_free_picture(&ms->ip);
|
|
#endif
|
|
}
|
|
|
|
static void do_mandelbrot_test(EditState *s, int argval) {
|
|
QEmacsState *qs = s->qs;
|
|
EditBuffer *b;
|
|
|
|
if (!fractal_mode.name) {
|
|
/* populate and register shell mode and commands */
|
|
// XXX: remove this mess
|
|
memcpy(&fractal_mode, &text_mode, offsetof(ModeDef, first_key));
|
|
fractal_mode.name = "fractal";
|
|
fractal_mode.mode_probe = fractal_mode_probe;
|
|
fractal_mode.buffer_instance_size = sizeof(FractalState);
|
|
fractal_mode.mode_init = fractal_mode_init;
|
|
fractal_mode.mode_free = fractal_mode_free;
|
|
fractal_mode.display_hook = fractal_display_hook;
|
|
fractal_mode.default_wrap = WRAP_TRUNCATE;
|
|
#if USE_BITMAP_API || USE_DRAW_PICTURE
|
|
fractal_mode.display = fractal_display;
|
|
#endif
|
|
qe_register_mode(qs, &fractal_mode, MODEF_NOCMD | MODEF_VIEW);
|
|
qe_register_commands(qs, &fractal_mode, fractal_commands, countof(fractal_commands));
|
|
}
|
|
|
|
// XXX: should handle multiple Mandelbrot sets for comparison
|
|
b = qe_new_buffer(qs, "*Mandelbrot*", BC_CLEAR | BF_UTF8 | BF_STYLE4);
|
|
if (!b)
|
|
return;
|
|
|
|
b->default_mode = &fractal_mode;
|
|
eb_set_charset(b, &charset_ucs2be, EOL_UNIX);
|
|
do_delete_other_windows(s, 0);
|
|
switch_to_buffer(s, b);
|
|
if (argval != 1) {
|
|
FractalState *ms = fractal_get_state(s, 1);
|
|
if (ms) {
|
|
ms->type = clamp_int(argval - 1, 0, countof(fractal_type) - 1);
|
|
fractal_invalidate(ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const CmdDef fractal_global_commands[] = {
|
|
CMD2( "mandelbrot-test", "C-h m",
|
|
"Explore the Mandelbrot set in fractal-mode",
|
|
do_mandelbrot_test, ESi, "#" "p")
|
|
};
|
|
|
|
static int fractal_init(QEmacsState *qs)
|
|
{
|
|
qe_register_mode(qs, &fractint_mode, MODEF_SYNTAX);
|
|
qe_register_commands(qs, NULL, fractal_global_commands, countof(fractal_global_commands));
|
|
return 0;
|
|
}
|
|
|
|
qe_module_init(fractal_init);
|