本章是小熊猫C++发行版中集成的部分C/C++库的教程和使用介绍。
小熊猫C++在发行版所带的编译器中集成了若干教学中常用的库以方便学生使用,包括:
EGE(Easy Graphics Engine),是windows下的简易绘图库,是一个类似BGI(graphics.h)的面向C/C++语言新手的图形库,它的目标也是为了替代TC的BGI库而存在。
它的使用方法与TC中的graphics.h相当接近,对新手来说,简单,友好,容易上手,免费开源,而且接口意义直观,即使是完全没有接触过图形编程的,也能迅速学会基本的绘图。
如果直接使用单文件方式编写ege程序(不创建项目),直接在程序中包含ege.h,编译即可。小熊猫C++的自动链接功能会自动在编译时链接相关的库。
如需修改编译时的链接参数,可通过“工具”菜单→“选项”打开“选项”对话框,在“编译器”→“自动链接”页中找到ege.h,修改相关的链接参数。
如果要在项目中使用ege库,请使用新建项目向导中的“多媒体”→“EGE绘图”模板创建项目,即可正常编译。
小熊猫C++ 2.22或更早的版本,请使用新建项目向导中的“多媒体”→“Graphics.h”模板创建项目。
如需修改编译时的链接参数,可以通过“项目”菜单→“项目属性”打开“项目选项”对话框,修改自定义选项。
为了方便调试,缺省编译得到的可执行文件在运行时会显示控制台窗口。如果不想显示它,可以:
raylib是一个以简单易用为目标的跨平台游戏绘图库,它的特点包括:
下面这个程序绘制了一个旋转的立方体(其实是摄像机在围绕立方体旋转):
#include <raylib.h>
#include <math.h>
int main(void)
{
// 初始化
const int screenWidth = 640;
const int screenHeight = 480;
//启用反锯齿
SetConfigFlags(FLAG_MSAA_4X_HINT);
//初始化窗口
InitWindow(screenWidth, screenHeight, "Sample");
// 初始化摄像机
Camera3D camera = { 0 };
camera.position = (Vector3){ 40.0f, 20.0f, 0.0f }; //相机所在位置{x,y,z}
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //相机朝向位置{x,y,z}
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; //相机正上方朝向矢量
camera.fovy = 70.0f; //相机视野宽度
camera.projection = CAMERA_PERSPECTIVE; //采用透视投影
//设置动画帧率(刷新率,fps)为30帧/秒
SetTargetFPS(30);
//--------------------------------------------------------------------------------------
int angle=0; //多边形旋转角度
// 主游戏循环
while (!WindowShouldClose()) //关闭窗口或者按ESC键时返回true
{
double time = GetTime();
// 每次循环更新一帧
// 摄像机围绕y轴转动
double cameraTime = time*0.3;
camera.position.x = (float)cos(cameraTime)*40.0f;
camera.position.z = (float)sin(cameraTime)*40.0f;
BeginDrawing();
ClearBackground(WHITE);
//以摄像机视角绘制3d内容
BeginMode3D(camera);
DrawCube(Vector3{0,0,0},10,10,10,VIOLET);
DrawCubeWires(Vector3{0,0,0},10,10,10,BLACK);
EndMode3D();
EndDrawing();
}
//关闭窗口
CloseWindow();
return 0;
}
对于初学者来说,只要了解了OpenGL的3维坐标系,以及摄像机投影的各项基本参数,很快就可以用raylib编写动画程序啦。
rdrawing是raylib的扩展库raylib-drawing中的一个子库,提供了诸如绘制指定线宽的线段、填充圆形、填充任意形状多边形等raylib缺少的2d绘图功能。
#include <raylib.h>
#include <rdrawing.h>
#include <time.h>
#define SHRINK_FRAMES 2
Image genBodyImage();
int main() {
InitWindow(800,600,"Doraemon");
SetTraceLogLevel(LOG_WARNING);
SetTargetFPS(30);
Image img=genBodyImage();
Texture texture = LoadTextureFromImage(img);
while(!WindowShouldClose()) {
BeginDrawing();
ClearBackground(WHITE);
DrawTexture(texture,0,0,WHITE);
EndDrawing();
}
//Clean up
UnloadTexture(texture);
UnloadImage(img);
CloseWindow();
}
Image genBodyImage(){
Image img=GenImageColor(800,600,WHITE);
// 画头
Color fillColor = (Color){7,190,234,255};
Color color=BLACK;
ImageFillRoundRectEx(&img,265, 94, 270, 260, 124, 124,fillColor);
ImageDrawRoundRectEx(&img,265, 94, 270, 260, 124, 124,1,color);
fillColor = WHITE; // 脸
ImageFillEllipseEx(&img, 400, 256, 115, 95, fillColor);
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = BLACK;
ImageFillCircleEx(&img,384,184,6, fillColor); // 右眼球
ImageFillCircleEx(&img,416,184,6, fillColor); // 左眼球
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
ImageDrawLineEx(&img,400,223,400,296,1,color); // 人中
ImageDrawArcEx(&img,400, 192, 108, 108, PI * 5 / 4, PI * 7 / 4,1,color); // 嘴
ImageDrawLineEx(&img,358, 227, 310, 209,1,color); // 胡子
ImageDrawLineEx(&img,442, 227, 490, 209,1,color);
ImageDrawLineEx(&img,359, 235, 308, 235,1,color);
ImageDrawLineEx(&img,441, 235, 492, 235,1,color);
ImageDrawLineEx(&img,358, 243, 310, 261,1,color);
ImageDrawLineEx(&img,442, 243, 490, 261,1,color);
// 画身体
ImageDrawLineEx(&img, 319, 332, 262, 372,1,color); // 手臂(上)
ImageDrawLineEx(&img, 481, 332, 538, 372,1,color);
ImageDrawLineEx(&img, 304, 396, 284, 410,1,color); // 手臂(下)
ImageDrawLineEx(&img, 496, 396, 516, 410,1,color);
ImageDrawLineEx(&img, 304, 385, 304, 478,1,color); // 腿外侧
ImageDrawLineEx(&img, 496, 385, 496, 478,1,color);
ImageDrawArcEx(&img, 400, 479, 15,11,0,PI,1,color); // 腿内侧
fillColor=WHITE; // 手
ImageFillCircleEx(&img, 260,399,27, fillColor);
ImageDrawCircleEx(&img, 260,399,27, 1, color);
ImageFillCircleEx(&img, 540,399,27, fillColor);
ImageDrawCircleEx(&img, 540,399,27, 1, color);
ImageFillRoundRectEx(&img,288,478,110,27,12,12,fillColor); // 脚
ImageDrawRoundRectEx(&img,288,478,110,27,12,12,1,color);
ImageFillRoundRectEx(&img,402,478,110,27,12,12,fillColor);
ImageDrawRoundRectEx(&img,402,478,110,27,12,12,1,color);
fillColor=(Color){7,190,234,255}; // 身体填充蓝色
ImageFloodFill(&img,400,400,BLACK,fillColor);
fillColor=WHITE; // 肚皮
ImageFillCircleEx(&img,400,381,75,fillColor);
ImageFillRectangleEx(&img,340,304,120,20,fillColor); // 用白色矩形擦掉多余的肚皮
ImageDrawSectorEx(&img,400,381,58, 58,PI,2*PI,1,color); // 口袋
// 画铃铛
fillColor=(Color){169, 38, 0,255}; // 绳子
ImageFillRoundRectEx(&img,300,323,200,19,12,12,fillColor);
fillColor=(Color){245, 237, 38,255}; // 铃铛外形
ImageFillCircleEx(&img,400,349,19,fillColor);
fillColor=BLACK; // 铃铛上的洞
ImageFillEllipseEx(&img,400,354,4,4,fillColor);
ImageDrawLineEx(&img,400,357,400,368,3,color);
ImageDrawLineEx(&img,384,340,416,340,1,color); // 铃铛上的纹路
ImageDrawLineEx(&img,384,344,418,344,1,color);
return img;
}
Image genShrinkImage0() {
Image img = GenImageColor(800,600, BLANK);
Color fillColor=WHITE;
Color color=BLACK;
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = BLACK;
ImageFillCircleEx(&img,384,184,6, fillColor); // 右眼球
ImageFillCircleEx(&img,416,184,6, fillColor); // 左眼球
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
return img;
}
Image genShrinkImage1() {
Image img = GenImageColor(800,600, BLANK);
Color fillColor=WHITE;
Color color=BLACK;
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawRoundRectEx(&img,337, 150, 63, 37, 28, 28, 1, color);
ImageDrawRoundRectEx(&img,400, 150, 63, 37, 28, 28, 1, color);
ImageFillRectangleEx(&img,337,168,63,19,fillColor);
ImageFillRectangleEx(&img,400,168,63,19,fillColor);
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = BLACK;
ImageFillCircleEx(&img,384,184,6, fillColor); // 右眼球
ImageFillCircleEx(&img,416,184,6, fillColor); // 左眼球
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
return img;
}
Image genShrinkImage2() {
Image img=GenImageColor(800,600,BLANK);
Color fillColor=WHITE;
Color color=BLACK;
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawLineEx(&img,337,168,399,168,1,color);
ImageDrawLineEx(&img,400,168,462,168,1,color);
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
return img;
}
Image genShrinkImage3(){
Image img=GenImageColor(800,600,BLANK);
Color fillColor=WHITE;
Color color=BLACK;
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawRoundRectEx(&img,337, 150, 63, 37, 28, 28, 1, color);
ImageDrawRoundRectEx(&img,400, 150, 63, 37, 28, 28, 1, color);
ImageFillRectangleEx(&img,337,150,63,19,fillColor);
ImageFillRectangleEx(&img,400,150,63,19,fillColor);
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
return img;
}
Image genShrinkImage4() {
Image img=GenImageColor(800,600,BLANK);
Color fillColor=WHITE;
Color color=BLACK;
ImageFillRoundRectEx(&img,337, 131, 63, 74, 28, 28, fillColor); // 右眼
ImageFillRoundRectEx(&img,400, 131, 63, 74, 28, 28, fillColor); // 左眼
ImageDrawRoundRectEx(&img,337, 131, 63, 74, 28, 28,1, color);
ImageDrawRoundRectEx(&img,400, 131, 63, 74, 28, 28,1, color);
fillColor = (Color){201, 62, 0, 255}; // 鼻子
ImageFillCircleEx(&img, 400, 208, 15, fillColor);
return img;
}
函数名 | 功能 |
---|---|
ImageDrawPointEx | 使用指定的大小,在Image对象中画一个点 |
ImageDrawArcEx | 使用指定的线宽和颜色,在Image对象中画一段椭圆弧 |
ImageDrawCubicBezierEx | 使用指定的线宽和颜色,绘制三次贝塞尔曲线 |
ImageDrawLineEx | 使用指定的线宽和颜色,在Image对象中画一条线段 |
ImageDrawPolylineEx | 使用指定的线宽和颜色,在Image对象中画一条多义线(polyline) |
ImageDrawRectangleEx | 使用指定的线宽和颜色,在Image对象中画一个矩形(仅轮廓线) |
ImageDrawRoundRectEx | 使用指定的线宽和颜色,在Image对象中画一个圆角矩形(仅轮廓线) |
ImageDrawCircleEx | 使用指定的线宽和颜色,在Image对象中画一个圆(仅轮廓线) |
ImageDrawEllipseEx | 使用指定的线宽和颜色,在Image对象中画一个椭圆(仅轮廓线) |
ImageDrawPolygonEx | 使用指定的线宽和颜色,在Image对象中画一个多边形(仅轮廓线) |
ImageDrawSectorEx | 使用指定的线宽和颜色,在Image对象中画一个扇形(仅轮廓线) |
ImageFillCircleEx | 使用指定的颜色,绘制一个填充的圆 |
ImageFillEllipseEx | 使用指定的颜色,绘制一个填充的椭圆 |
ImageFillTriangleEx | 使用指定的颜色,绘制一个填充的三角形 |
ImageFillRectangleEx | 使用指定的颜色,绘制一个填充的矩形 |
ImageFillRoundRectEx | 使用指定的颜色,绘制一个填充的圆角矩形 |
ImageFillPolygonEx | 使用指定的颜色,绘制一个填充的多边形 |
ImageFillSectorEx | 使用指定的颜色,绘制一个填充的扇形 |
ImageFloodFill | 泛洪填充(当遇到颜色为border color的像素时停止) |
特色:
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}
std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
Format a string using positional arguments (run)
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
#include <fmt/chrono.h>
int main() {
using namespace std::literals::chrono_literals;
fmt::print("Default format: {} {}\n", 42s, 100ms);
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
输出:
Default format: 42s 100ms
strftime-like format: 03:15:30
#include <vector>
#include <fmt/ranges.h>
int main() {
std::vector<int> v = {1, 2, 3};
fmt::print("{}\n", v);
}
输出:
[1, 2, 3]
下面的代码在C++20中产生编译器错误,因为对字符串参数来说d是非法的格式符
std::string s = fmt::format("{:d}", "I am not a number");
#include <fmt/os.h>
int main() {
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
}
运行速度是fprintf的5到9倍。