返回列表 发帖

控制台函数绘图工具fplot.exe

FPLOT.EXE更新至1.7版,新架构更稳定。
下载链接: https://pan.baidu.com/s/1Mai3MaUQrVLD5Nh_JM2KvQ?pwd=8kvy

摘要:
=============================================================================
控制台数学函数绘图工具,支持几乎所有的数学函数表达式绘制,支持标准直角坐标、
极坐标、参数方程、参数极方程的绘制。

采用剪枝算法,具备极高绘制精度、速度,同时自动过滤间断点、无穷点。自动上色、
自动编号、自动筛选有效定义域。这是一个完全媲美GNU PLOT的工具,纯命令行操作,
更少的开关,简约而不简单。
=============================================================================

用法:
-----------------------------------------------------------------------------
fplot  [options] {arguments}...
-----------------------------------------------------------------------------
       /zoom     [x-zoom] [y-zoom]           //设置水平缩放、垂直缩放率
       /move     [x-move] [y-move]           //设置水平偏移、垂直偏移值
       /plo(t|p) [indvar] [expression]       //待绘制自变量、因变量字串
-----------------------------------------------------------------------------

注意: 绘直角坐标用/plot,绘极坐标用/plop。

示例:
-----------------------------------------------------------------------------
fplot /zoom 30  30  /plot x sin(x)           //放大30倍,自变量为x,绘制sin(x)
fplot /zoom 300 300 /plop x sin(x)           //放大300倍,按照极坐标方式绘制
fplot /zoom 0   0                            //清除图像
fplot /zoom 0   1                            //隐藏光标
fplot /zoom 1   0                            //显示光标
-----------------------------------------------------------------------------


备注:
-----------------------------------------------------------------------------
常数类
        pi    3.1415926535897932
        e     2.7182818284590452       

通用类
        rand  随机数
        round 四舍五入
        int   取整
        ceil  向上舍入
        floor 向下舍入
        abs   绝对值
        sqrt  开方
        lg    常用对数,以10为底
        ln    自然对数
        exp   e的次幂
        gamma 伽玛函数
        torad 度转弧度
        +     加
        -     减
        *     乘
        /     除
        %     取余数
        ^     次方
        !     阶乘

三角函数类
        sin、cos、tan   
        arcsin、arccos、arctan

双曲函数类
        sinh、cosh、tanh
        arcsinh、arccosh、arctanh
-----------------------------------------------------------------------------

英译:
-----------------------------------------------------------------------------
CONSOLE FUNCTION DRAWING TOOL, COPYRIGHT@2017~2019 BY HAPPY
-----------------------------------------------------------------------------
fplot  [options] {arguments}...
-----------------------------------------------------------------------------
       /zoom     [x-zoom] [y-zoom]
       /move     [x-move] [y-move]
       /plo(t|p) [indvar] [expression]
-----------------------------------------------------------------------------
2017-02-11 VERSION 1.7"


原创代码:(仅支持g++编译器)
/*
CONSOLE FUNCTION DRAWING TOOL, COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
FPLOT.EXE
LINK GDIPLUS.LIB GDI32.LIB
*/
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <windows.h>
#include <gdiplus\gdiplus.h>
//使用GDI+
using namespace Gdiplus;
//申明函数
extern "C" HWND WINAPI GetConsoleWindow();
//定义帮助说明
#define HELP_INFORMATION "\
-----------------------------------------------------------------\n\
CONSOLE FUNCTION DRAWING TOOL, COPYRIGHT@2017~2019 BY HAPPY\n\
-----------------------------------------------------------------\n\
fplot  [options] {arguments}...\n\
-----------------------------------------------------------------\n\
       /zoom     [x-zoom] [y-zoom]\n\
       /move     [x-move] [y-move]\n\
       /plo(t|p) [indvar] [expression]\n\
-----------------------------------------------------------------\n\
2017-02-12 VERSION 1.7"
/***************定义全局变量*************/
//定义开关词目
#define            SENSITIVE_NUM   7
static const char* SENSITIVE_WORDS[]={"/ZOOM", "/MOVE", "/PLOT", "/PLOP", "/HELP", "/H", "/?"};
static const char  SENSITIVE_ARGVS[]={      2,       2,       2,       2,       0,    0,    0};
//数学函数词目
static const char* KEY_WORDS[]={"E", "PI", "SQRT", "LG", "LN", "SIN", "COS", "TAN", "ARCSIN", "ARCCOS", "ARCTAN", "TORAD", "ABS", "ROUND", "FLOOR", "CEIL", "EXP", "SINH", "COSH", "TANH", "ARCSINH", "ARCCOSH", "ARCTANH", "INT", "GAMMA", "X", NULL};
//堆栈尺寸
#define  STACK_SIZE      1024
//运算符栈
static char   STACK1[STACK_SIZE]={0};
//逆波兰栈
static char   STACK2[STACK_SIZE]={0};
//浮点数栈
static float  STACK3[STACK_SIZE]={0};
//定义绘线属性
static float  xZOOM=50, yZOOM=50, xMOVE=0, yMOVE=0, hCOLOR=0, hPREC=1000, hMARK=12, xOFFSET=0, yOFFSET=0;
//主窗区域
RECT          winRECT={0};
LPRECT        lrRECT;
//字符串存储容器
static WCHAR  Tainer[1024]={0};  
static CHAR   FTainer[32] ={0};  
//定义颜色结构体
typedef struct{
BYTE R;
BYTE G;
BYTE B;
COLORREF VALUE;
}STRGB;
//十六色系数组
static const STRGB PARGB[15]={
{255,  0,  0,RGB(255,  0,  0)}, //红色
{ 33,198,239,RGB( 33,198,239)}, //未蓝
{255,255,  0,RGB(255,255,  0)}, //黄色
{238,130,238,RGB(238,130,238)}, //紫兰
{165, 42, 42,RGB(165, 42, 42)}, //棕色
{144,238,144,RGB(144,238,144)}, //浅绿
{  0,  0,255,RGB(  0,  0,255)}, //蓝色
{255,  0,255,RGB(255,  0,255)}, //洋紫
{169,169,169,RGB(169,169,169)}, //深灰
{173,216,230,RGB(173,216,230)}, //淡蓝
{248, 29, 56,RGB(248, 29, 56)}, //亮红
{  0,255,  0,RGB(  0,255,  0)}, //绿色
{224,255,255,RGB(224,255,255)}, //淡青
{  0,255,255,RGB(  0,255,255)}, //青色
{255,255,255,RGB(255,255,255)}  //白色
};
//创建绘图字体
FontFamily* fontFAMILY;
//设置字体属性
Font* tFONT, *nFONT;
//创建字体画刷
SolidBrush* tBRUSH;
/***************逆波兰核心***************/
float RevPolishCore(const char* expression, float varV)
{
char  *op=(char*)expression, *S1=STACK1, *S2=STACK2, **key, *cp, *kp;
float *S3=STACK3, di, ni;
int    brackets=0;
STACK3[0]=0;
//生成逆波兰
while(*op!='\0'){
switch(*op){
case ' ' :
case '\t':
case '\r':
case '\n':
//过滤空字符
op++;
continue;
case '(':
brackets++;
*(++S1)=*op;
if(*(op+1)=='-' || *(op+1)=='+'){
*(S2++)='0', *(S2++)=' ';
}
break;
case ')':
//验证括号是否闭合
if(brackets ==0){
fputs("The brackets or ')' are not need", stderr);
exit(1);
}
brackets--;
while(*S1!='(')
{
*(S2++)=*(S1--);
}
//舍弃'('
S1--;
break;
case '+':
case '-':
while(S1!=STACK1 && *S1!='(')
{
*(S2++)=*(S1--);
}
*(++S1)=*op;
break;
case '^':
//指数符
while( ('A'<=(*S1) && (*S1)<='Z') )
{
*(S2++)=*(S1--);
}
*(++S1)=*op;
break;
case '!':
//阶乘符
*(S2++)=*op;
break;
case '%':
case '*':
case '/':
while(('A'<=(*S1) && (*S1)<='Z') ||*S1=='%' ||*S1=='*' ||*S1=='/' ||*S1=='^'){
*(S2++)=*(S1--);
}
*(++S1)=*op;
break;
default :
if(
('a'<=*op && *op<='z') ||
('A'<=*op && *op<='Z')
){
//识别数学函数关键词
key=(char**)KEY_WORDS;
while(*key !=NULL){
cp=op, kp=*key;
//比对关键词字母
while((*cp==*kp||*cp==*kp+32) && *kp!='\0'){
cp++, kp++;
}
//验证关键词结尾
if( ((*cp<'A')||('Z'<*cp && *cp<'a')||(*cp>'z')) && (*kp=='\0') ){
op=cp;
break;
}
key++;
}
//构建伪双目
*(S2++)='.';
*(S2++)=' ';
//伪双目入栈
while( ('A'<=(*S1) && (*S1)<='Z') ){
*(S2++)=*(S1--);
}
if(*key !=NULL){
*(++S1)=key-(char**)KEY_WORDS+65;
}else{
while(
('a'<=*op && *op<='z') ||
('A'<=*op && *op<='Z')
){op++;}
*(++S1)='Z';
}
continue;
}else if(('0'<=*op && *op<='9') || (*op=='.')){
//浮点数入栈
while(('0'<=*op && *op<='9') || (*op=='.')){
*(S2++)=*(op++);
}
if(
('a'<=*op && *op<='z') ||
('A'<=*op && *op<='Z')
){
//缺少必要的运算符
fputs("Missing required operator\n", stderr);
exit(1);
}
op--;
*(S2++)=' ';
}else{
//无法识别的运算符
fputs("Unrecognized operator\n", stderr);
exit(1);
}
break;
}
op++;
}
//验证括号是否闭合
if(brackets !=0){
fputs("The brackets '(' are not closed", stderr);
exit(1);
}
//收尾逆波兰
while(S1 !=STACK1){*(S2++)=*(S1--);}
*S2=' ';
//计算逆波兰
op=STACK2;
while(*op!=' '){
switch(*op){
case 'A':
*S3=2.7182818284590452;
break;
case 'B':
*S3=3.1415926535897932;
break;
case 'C':
*(S3-1)=sqrtf(*S3);
S3--;
break;
case 'D':
*(S3-1)=log10f(*S3);
S3--;
break;
case 'E':
*(S3-1)=logf(*S3);
S3--;
break;
case 'F':
*(S3-1)=sinf(*S3);
S3--;
break;
case 'G':
*(S3-1)=cosf(*S3);
S3--;
break;
case 'H':
*(S3-1)=tanf(*S3);
S3--;
break;
case 'I':
*(S3-1)=asinf(*S3);
S3--;
break;
case 'J':
*(S3-1)=acosf(*S3);
S3--;
break;
case 'K':
*(S3-1)=atanf(*S3);
S3--;
break;
case 'L':
*(S3-1)=(*S3)*3.1415926535897932/180.0;
S3--;
break;
case 'M':
*(S3-1)=fabsf(*S3);
S3--;
break;
case 'N':
*(S3-1)=roundf(*S3);
S3--;
break;
case 'O':
*(S3-1)=floorf(*S3);
S3--;
break;
case 'P':
*(S3-1)=ceilf(*S3);
S3--;
break;
case 'Q':
*(S3-1)=expf(*S3);
S3--;
break;
case 'R':
*(S3-1)=sinhf(*S3);
S3--;
break;
case 'S':
*(S3-1)=coshf(*S3);
S3--;
break;
case 'T':
*(S3-1)=tanhf(*S3);
S3--;
break;
case 'U':
*(S3-1)=asinhf(*S3);
S3--;
break;
case 'V':
*(S3-1)=acoshf(*S3);
S3--;
break;
case 'W':
*(S3-1)=atanhf(*S3);
S3--;
break;
case 'X':
*(S3-1)=(int)(*S3);
S3--;
break;
case 'Y':
*(S3-1)=tgammaf(*S3);
S3--;
break;
case 'Z':
*S3=varV;
break;
case '+':
*(S3-1)+=*S3;
S3--;
break;
case '-':
*(S3-1)-=*S3;
S3--;
break;
case '*':
*(S3-1)*=*S3;
S3--;
break;
case '%':
case '/':
if(*op=='%'){
//取余数
*(S3-1)=(int)*(S3-1) % (int)*S3;
}else{
*(S3-1)/=*S3;
}
S3--;
break;
case '^':
*(S3-1)=powf(*(S3-1), *S3);
S3--;
break;
case '!':
*S3=tgammaf(*S3+1);
break;
default :
//字符串转浮点
di=0, ni=1;
while('0'<=*op && *op<='9'){
di=10*di+(*op)-'0';
op++;
}
if(*op=='.'){
op++;
while('0'<=*op && *op<='9'){
di=10*di+(*op)-'0';
op++, ni*=10;
}
}
*(++S3)=di/ni;
break;
}
op++;
}
//返回计算结果
return *S3;
}
/***************功能函数群***************/
//光标函数
BOOL DispyCursor(int size,bool mode)
{
CONSOLE_CURSOR_INFO cinfo ={(DWORD)size, mode};
return SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo);
}
//转码函数
WCHAR* L(const CHAR* str)
{
if(!str){return NULL;}
int wLen=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, NULL, 0);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, Tainer, wLen);
Tainer[wLen]='\0';
return Tainer;
}
//辨关键词
int itifyWORDS(const char* strARGV)
{
int i, SN;
for(SN=0; SN<SENSITIVE_NUM; SN++){
char *op=(char*)strARGV, *kp=(char*)SENSITIVE_WORDS[SN];
while(*kp!='\0'){
if( (('a'<= *op && *op<='z')?*op-32:*op) != (('a'<= *kp && *kp<='z')?*kp-32:*kp) ){break;}
op++;kp++;
}
if( (*kp=='\0') && (*op==' '||*op=='\t'||*op=='\r'||*op=='\n'||*op=='\0') ){return SN;}
}
return -1;
}
//显示数字
void drawNUMS(Graphics *graph, float fnum, float px, float py)
{
sprintf(FTainer, "%.5g", fnum);
graph->DrawString(L(FTainer), -1, tFONT, PointF(px,py), tBRUSH);
}
//绘制刻度
void drawAXY(Graphics *graph)
{
//设置坐标字体
Font fontp(fontFAMILY, 12, FontStyleRegular, UnitPixel);
tFONT=&fontp;
//设置曲线光滑
graph->SetSmoothingMode(SmoothingModeHighQuality);
//创建白色画笔
Pen *pen, pez(Color(160, 255, 255, 255), 1), pep(Color(160, 255, 255, 255), 1);
//设置虚线画笔
pez.SetDashStyle(DashStyleDash);
for(int i=0; i<=lrRECT->right*2; i+=50){
//绘制坐标
if(i==0){
//绘制坐标原点
drawNUMS(graph, 0, xOFFSET, yOFFSET);
}else{
//绘制坐标刻度
drawNUMS(graph,   i/xZOOM, xOFFSET+i,   yOFFSET);
drawNUMS(graph, 0-i/xZOOM, xOFFSET-i,   yOFFSET);
drawNUMS(graph, 0-i/yZOOM,   xOFFSET, yOFFSET+i);
drawNUMS(graph,   i/yZOOM,   xOFFSET, yOFFSET-i);
}
//绘制网格
if(i==0){
pen=&pep;
}else{
pen=&pez;
}
graph->DrawLine(pen, PointF(0,       yOFFSET+i), PointF(lrRECT->right, yOFFSET+i));
graph->DrawLine(pen, PointF(0,       yOFFSET-i), PointF(lrRECT->right, yOFFSET-i));
graph->DrawLine(pen, PointF(xOFFSET+i,       0), PointF(xOFFSET+i,lrRECT->bottom));
graph->DrawLine(pen, PointF(xOFFSET-i,       0), PointF(xOFFSET-i,lrRECT->bottom));
}
//清理对象
DeleteObject(pen);
DeleteObject(&pez);
DeleteObject(&pep);
DeleteObject(&fontp);
}
//绘制曲线
void drawFUN(Graphics *graph, BOOL penMODE, const char* varression, const char* expression)
{
//设置绘图曲线光滑
graph->SetSmoothingMode(SmoothingModeHighQuality);
//创建彩色画笔
STRGB* penRGB=(STRGB*)&PARGB[(int)hCOLOR];
Pen pep(Color(255,penRGB->R,penRGB->G,penRGB->B), 2), *pen=&pep;
//设置注释字体
Font fontp(fontFAMILY, 13, FontStyleBold, UnitPixel);
tFONT=&fontp;
//绘制函数注释
graph->DrawLine(pen, PointF(2, hMARK+6), PointF(20, hMARK+6));
graph->DrawString(L(expression), -1, tFONT, PointF(20,hMARK), tBRUSH);
hMARK+=16;
//绘制函数曲线
float t, x1, y1, x2, y2, t_start=(-lrRECT->right/2.0-xMOVE)/xZOOM, t_end=(lrRECT->right/2.0-xMOVE)/xZOOM, add=lrRECT->right/xZOOM/hPREC, y_start=2*(-lrRECT->bottom/2.0-yMOVE)/yZOOM, y_end=2*(lrRECT->bottom/2.0-yMOVE)/yZOOM;
if(penMODE){
//直角坐标
x1=RevPolishCore(varression, t_start), y1=RevPolishCore(expression, t_start);
for(t=t_start; t<=t_end; t+=add){
x2=RevPolishCore(varression, t+add), y2=RevPolishCore(expression, t+add);
if((t!=t_start)  && (y_start<y1 && y1<y_end) && (y_start<y2 && y2<y_end)){
graph->DrawLine(pen, PointF(xZOOM*x1+xOFFSET, -yZOOM*y1+yOFFSET), PointF(xZOOM*x2+xOFFSET, -yZOOM*y2+yOFFSET));
}
x1=x2, y1=y2;
}
}else{
//极轴坐标
x1=RevPolishCore(varression, t_start), y1=RevPolishCore(expression, t_start);
for(t=t_start; t<=t_end*5; t+=add){
x2=RevPolishCore(varression, t+add), y2=RevPolishCore(expression, t+add);
if((t!=t_start) && (y_start<y1 && y1<y_end) && (y_start<y2 && y2<y_end)){
graph->DrawLine(pen, PointF(xZOOM*y1*cos(x1)+xOFFSET, -yZOOM*y1*sin(x1)+yOFFSET), PointF(xZOOM*y2*cos(x2)+xOFFSET, -yZOOM*y2*sin(x2)+yOFFSET));
}
x1=x2, y1=y2;
}
}
//清理对象
DeleteObject(pen);
DeleteObject(&pep);
DeleteObject(&fontp);
}
//开关解析
void OptRE(int argc, char** argv)
{
//计算有效参数数目
int oargc=argc-1, anum;
if(oargc==0){
//无参数,则抛出使用说明
fputs(HELP_INFORMATION, stderr);
exit(1);
}
//获取CMD窗口句柄
HWND hCMD=GetConsoleWindow();
HDC  hDC =GetDC(hCMD);
//获取CMD窗口大小
lrRECT=&winRECT;
GetClientRect(hCMD, lrRECT);
//获取像素坐标基值
xOFFSET=lrRECT->right/2, yOFFSET=lrRECT->bottom/2;
//创建加速内存画布
HWND    hWND2=NULL;
HDC     hDC2=CreateCompatibleDC(hDC);
HBITMAP hBitmap2=CreateCompatibleBitmap(hDC, lrRECT->right, lrRECT->bottom);
SelectObject(hDC2, hBitmap2);
//创建绘图工具
Graphics graphicsp(hDC), *graph=&graphicsp;
//创建绘图字体
FontFamily fontf(L"SimSun");
fontFAMILY=&fontf;
//创建字体画刷
SolidBrush brushp(Color(255, 255, 255, 255));
tBRUSH=&brushp;
//初始化线条颜色
int srCOLOR=0;
//参数接收针
char** oargv=argv;
//参数累加器
int i=0;
//坐标绘制态
BOOL dMARK=FALSE;
//临时变量
float tp1, tp2;
//开启主循环
while((++i)<=oargc){
int SN=itifyWORDS(argv[i]);
if(SN!=-1){
if(i+SENSITIVE_ARGVS[SN]>oargc){
//缺少必要参数
fprintf(stderr, "The option '%s' needs %d parameters\n", argv[i], SENSITIVE_ARGVS[SN]);
exit(1);
}
for(anum=1; anum<=SENSITIVE_ARGVS[SN]; anum++){
if(oargv[i+anum][0]==SENSITIVE_WORDS[0][0]){
//缺少必要参数
fprintf(stderr, "Only %d parameters, the option '%s' needs %d parameters\n", anum-1, argv[i], SENSITIVE_ARGVS[SN]);
exit(1);
}
}
//执行开关命令
switch(SN)
{
case 0:
//ZOOM
tp1=fabsf(atof(argv[i+1])), tp2=fabsf(atof(argv[i+2]));
//解析ZOOM开关的隐藏功能
if(tp1*tp2==0){
if(tp2!=0){
DispyCursor((DWORD)25, FALSE);
}else if(tp1!=0){
DispyCursor((DWORD)25, TRUE );
}else{
InvalidateRect(hCMD,  NULL, FALSE);
dMARK=FALSE;
}
break;
}
InvalidateRect(hCMD,  NULL, FALSE);
dMARK=FALSE, srCOLOR=0, hMARK=12;
xZOOM=tp1, yZOOM=tp2;
break;
case 1:
//MOVE
if(dMARK){
InvalidateRect(hCMD,  NULL, FALSE);
dMARK=FALSE, srCOLOR=0, hMARK=12;
}
xMOVE=atof(argv[i+1]), yMOVE=atof(argv[i+2]);
xOFFSET+=xMOVE, yOFFSET-=yMOVE;
break;
case 2:
case 3:
//PLOP||PLOT
if(!dMARK){
//调用坐标绘制函数
drawAXY(graph);
dMARK=TRUE;
}
hCOLOR=(srCOLOR++)%15;
drawFUN(graph, (SN==2)?TRUE:FALSE, argv[i+1], argv[i+2]);
break;
default:
//HELP
fputs(HELP_INFORMATION, stderr);
exit(1);
}
i+=SENSITIVE_ARGVS[SN];
//无法识别的参数
}else{
fprintf(stderr, "An error occurred near '%s'\n", argv[i]);
exit(1);
}
}
//从内存画布复制到CMD画布
//BitBlt(hDC,0,0,lrRECT->right,lrRECT->bottom, hDC2,0,0,SRCCOPY);
//清理绘图工具
DeleteObject(graph);
DeleteObject(tBRUSH);
DeleteObject(tFONT);
DeleteObject(fontFAMILY);
//释放内存画布
ReleaseDC(NULL, hDC2);
DeleteDC(hDC2);
DeleteObject(hBitmap2);
//释放CMD 画布
ReleaseDC(hCMD, hDC);
DeleteDC(hDC);
}
/*************MAIN主函数入口*************/
int main(int argc, char** argv)
{
//初始化GdiPlus
ULONG_PTR gdipludToken;
GdiplusStartupInput gdiplusInput;
GdiplusStartup(&gdipludToken,&gdiplusInput,NULL);
//解析开关
OptRE(argc, argv);
//关闭Gdiplus
GdiplusShutdown(gdipludToken);
return 0;
}COPY
4

评分人数

回复 2# CrLf
多谢大师,代码很业余,只实现了部分简单功能,还有很多要写却没完成。

TOP

回复 4# codegay
那些函数本来就长得很美,我只是让它在控制台上展现出来,因为cmd的黑背景很适合绘图。

TOP

返回列表