// sprite.h by 李兴球,风火轮编程基地
#ifndef SPRITE_H
#define SPRITE_H
#include "screen.h"
#include <SDL2/SDL.h>
#include "shape.h"
#include "stamp.h"
#include "polygon_offset.h"
#include <string>
#include <unordered_map>
#include <vector>
#include "writetxt.h"
#include "dynamicproperty.h"
class Sprite {
public:
std::unordered_map<std::string,DynamicProperty> property;
void _load_pic_as_shape( std::string &shapeimage);
Sprite(std::vector<std::string> shapes={"res/rocket.png"},bool visible=true,std::string tag="",float x=0.0f,float y=0.0f,float heading=0.0);
Sprite(std::initializer_list<std::string> shapes, bool visible = true, std::string tag = "", float x = 0.0f, float y = 0.0f, float heading = 0.0f);
Sprite(std::string shape, bool visible = true, std::string tag = "", float x = 0.0f, float y = 0.0f, float heading = 0.0f);
//以下两个是一样的
Sprite& setlayer(int layer); //设置角色所在的层
Sprite& set_layer(int layer); //设置角色所在的层
~Sprite();
Sprite& forward(float distance); //朝角色的方向移动一定的距离
Sprite& fd(float distance); //作为forward的别名之用, 朝角色的方向移动一定的距离
Sprite& backward(float distance); // 朝角色的反方向移动一定的距离
Sprite& back(float distance); // 作为backward的别名,朝角色的反方向移动一定的距离
Sprite& bk(float distance); // 作为backward的别名,朝角色的反方向移动一定的距离
Sprite& right(float angle); //让角色顺时钟旋转angle度
Sprite& left(float angle); //让角色逆时钟旋转angle度
Sprite& rt(float angle); //让角色顺时钟旋转angle度
Sprite& lt(float angle); //让角色逆时钟旋转angle度
Sprite& setheading(float angle); // 设定角色方向, 0=东, 90=北, 180=西, 270=南
Sprite& seth(float angle); // 设定角色方向,0=东, 90=北, 180=西, 270=南
float heading(); //返回角色的角度方向值
Sprite& heading(float x,float y); //让角色朝向某个坐标
Sprite& heading(Sprite& other); //让角色朝向另一个角色
Sprite& heading(Point &point); //让角色朝向某个点
bool isdown(); //画笔是否落下
Sprite& penup(); //抬笔
Sprite& pu(); //抬笔
Sprite& up(); //抬笔
Sprite& pendown(); //落笔
Sprite& pd(); //落笔
Sprite& down(); //落笔
float pensize(); //返回画笔线宽
Sprite& pensize(float width); //设定画笔线宽
float width(); //返回画笔线宽
Sprite& width(float width); //设定画笔线宽
std::string pencolor(); //以16进制RGB字符串,返回画笔颜色
Sprite& pencolor(SDL_Color color); //设定画笔颜色
Sprite& pencolor(Color color); //设定画笔颜色
Sprite& pencolor(Uint8 r, Uint8 g, Uint8 b,Uint8 a=255); ////设定画笔颜色 ,r,g,b,a
Sprite& pencolor(std::string pc); ////设定画笔颜色 ,字符串形式的颜色如red或#FF0000
Sprite& pencolor(float hue); //设定画笔颜色 ,hue被当成颜色的色相,把这个单精度浮点数转换成RGB形式
Sprite& pencolor(double hue); //设定画笔颜色 ,hue被当成颜色的色相,把这个双精度浮点数转换成RGB形式
Sprite& pencolor(int hue); //设定画笔颜色 ,hue被当成颜色的色相,把这个整数转换成RGB形式
Sprite& penhue(int hue); //设定画笔颜色 ,hue被当成颜色的色相,把这个整数转换成RGB形式
Sprite& penbhd(float bhd); //设定画笔的饱和度,0到100.0
Sprite& pensat(float bhd); //设定画笔的饱和度,0到100.0
Sprite& coloradd(float step); //画笔颜色的色相增加
float penhue(); //返回画笔的色相
float penbhd(); //返回画笔的饱和度
float pensat(); //返回画笔的饱和度
float pentone(); //返回画笔的明度
Sprite& pentone(float tone); //明度0到100.0的
float penvalue(); //返回画笔的明度
Sprite& penvalue(float tone); //明度0到100.0的
Sprite& penshade(float sd); //设定画笔的shade
float penshade() const; // 获取当前画笔的 shade 值
Sprite& penhsv(float hue,float sat=100.0,float value=100.0); //设定画笔的色相,饱和度和明度
std::string fillcolor(); //返回填充颜色
Sprite& fillcolor(Color fc); //设定填充颜色
Sprite& fillcolor(SDL_Color fc); //设定填充颜色
Sprite& fillcolor(Uint8 r, Uint8 g, Uint8 b,Uint8 a=255); //设定填充颜色
Sprite& fillcolor(std::string fc); //设定填充颜色 ,字符串形式的颜色如red,#FF0000
Sprite& fillcolor(float hue); ////设定填充颜色 ,当成色相,把这个浮点数转换成RGB形式
Sprite& fillcolor(double hue); ////设定填充颜色 ,当成色相,把这个浮点数转换成RGB形式
Sprite& fillcolor(int hue); ////设定填充颜色 ,当成色相,把这个浮点数转换成RGB形式
Sprite& dot(float diameter); //打圆点,参数是直径,默认为画笔颜色
Sprite& dot(float diameter,std::string color,int alpha=255); //打一个直径为diameter的圆点
Sprite& dot(float diameter,int hue); //打一个直径为diameter的圆点,hue表示色相
Sprite& dot(float diameter,SDL_Color color); //打一个直径为diameter的圆点
//以下_dot方法不公开
void _dot(float diameter,SDL_Color color); //打一个直径为diameter的圆点
Sprite& setpixel(Color px);//在角色坐标设置一个像素值,就是调用打直径为1的dot命令,颜色就是Color类型
Sprite& setpixel(SDL_Color px);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,颜色就是SDL_Color类型
Sprite& setpixel(Uint8 r, Uint8 g, Uint8 b,Uint8 a=255);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,颜色就RGB三元色整数值
Sprite& setpixel(std::string px);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,px是颜色字符串或者16进制字符串
Sprite& setpixel(float px);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,px表示色相
Sprite& setpixel(double px);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,px表示色相
Sprite& setpixel(int px);//在角色坐标设置一个像素值,,就是调用打直径为1的dot命令,px表示色相
Sprite& setpixel();//在角色坐标设置一个像素值,就是调用打直径为1的dot命令,颜色就是画笔颜色
Color getpixel(); //在角色坐标位置,调用屏幕的getpixel得到某个点的像素值
Sprite& color(int cc); //同时设定画笔颜色和填充颜色
Sprite& color(float cc); //同时设定画笔颜色和填充颜色
Sprite& color(double cc); //同时设定画笔颜色和填充颜色
Sprite& color(int r,int g,int b,int a=255); //同时设定画笔颜色和填充颜色
Sprite& color(std::string cc); //同时设定画笔颜色和填充颜色
Sprite& color(std::string pc, std::string fc); //设定画笔颜色和填充颜色
Sprite& color(SDL_Color cc); //同时设定画笔颜色和填充颜色
Sprite& color(Color cc); //同时设定画笔颜色和填充颜色
Sprite& color(SDL_Color pc, SDL_Color fc); //分别设定画笔颜色和填充颜色
std::pair<SDL_Color,SDL_Color> color(); //返回画笔颜色和填充颜色
Sprite& coloralpha(Uint8 pa,Uint8 fa=255); //设置画笔颜色和填充颜色的透明通道
Sprite& penalpha(Uint8 pa,Uint8 fa=255); //设置画笔颜色和填充颜色的透明通道
Sprite& home(); //回家,让角色朝向90度和回到原点
Sprite& setposition(float x,float y); //设置角色坐标
Sprite& setpos(float x,float y); //设置角色坐标
Sprite& gotoxy(float x, float y); //设置角色坐标
Sprite& goxy(float x, float y); //设置角色坐标
Sprite& setxy(float x,float y); //设置角色坐标
Sprite& go(float x,float y); //设置角色坐标
Sprite& go(std::pair<float,float> point );//设置角色坐标
Sprite& go(std::pair<int,int>); //设置角色坐标
Sprite& go(Sprite &other); //到达其它角色的位置
Sprite& go(Point &point); //到达某个点
Sprite& move(float dx,float dy); //水平与垂直位置相对移动 ,dx是水平相对位移,dy是垂直相对位移
float xcor(); //获取角色的x坐标
float ycor(); //获取角色的y坐标
std::pair<float,float> position(); //获取角色的x,y坐标
std::pair<float,float> pos(); //获取角色的x,y坐标
Sprite& setx(float x); //设定x坐标
Sprite& sety(float y); //设定y坐标
Sprite& gotox(float x); //设定x坐标
Sprite& gotoy(float y); //设定y坐标
Sprite& gox(float x); //设定x坐标
Sprite& goy(float y); //设定y坐标
Sprite& addx(float dx); //x坐标增加dx
Sprite& addy(float dy); //y坐标增加dy
float distance(float x,float y); //返回角色到x,y的距离
float distance(std::pair<float,float>); //返回角色到 pair<float,float>的距离
float distance(Sprite &other); //返回到其它角色的距离
float distance(Point &point); //返回到其它点的距离
// 速度控制
Sprite& speed(int speed); // 设置速度 0~10
int speed() const { return m_speed; } //返回速度值
bool filling(); //返回正在填充
Sprite& begin_fill(); //开始填充
std::vector< Point > end_fill(int mode=0); //结束填充,模式为0为默认采用的扫描线填充算法
std::vector< Point > _end_fill_scanline(); //结束填充 ,扫描线算法的结束填充
std::vector< Point > _end_fill_floodfill(); //结束填充 ,洪水填充算法的结束填充
Sprite& _drawPolygon(std::vector<Point> polygon);
Sprite& circle(double radius, double extent=360.0, int steps=0);//画圆圈,radius为半径,正数表示逆时钟,负数表示顺时钟画,extent表示画的度数,为负数时会倒着画。steps表示画的步长,比如为3时是画立着的正三角形。
Sprite& arc(double radius, double extent=60.0, int steps=0); //画圆弧,参数同circle方法,只不过extent值默认为60.0
Sprite& ellipse(double semiMajor, double semiMinor, double extent=360); //画椭圆
Sprite& oval(double semiMajor, double semiMinor, double extent=360);//画椭圆
Screen *get_screen(); //返回屏幕指针
Screen *getscreen(); //返回屏幕指针
Sprite& removeshape(const std::string& name);
Sprite& removeshape(const int index);
Sprite& _removeshape(const std::string& name); //移除某个造型
Sprite& addshape(const std::string& name); // 添加造型
Sprite& addshape(const std::string& name, Shape *shape);// 添加造型
Sprite& _addshape(const std::string& name, Shape *shape);// 添加造型
Sprite& next_costume(); // 切换到下一个造型
Sprite& pre_costume(); // 切换到上一个造型
Sprite& nextcostume(); // 切换到下一个造型
Sprite& precostume(); // 切换到上一个造型
Sprite& next_shape(); // 切换到下一个造型
Sprite& pre_shape(); // 切换到上一个造型
Sprite& nextshape(); // 切换到下一个造型
Sprite& preshape(); // 切换到上一个造型
Sprite& shape(int index); // 通过索引切换造型
Sprite& shape(const std::string& name); // 通过名称切换造型
int shapeindex(); //返回当前造型的索引号 cur_shape_index
std::string shape() const; // 获取当前造型名称
int shapes(); //返回角色当前的造型数量
Sprite& show(); //显示角色(精灵)
Sprite& hide(); //隐藏角色(精灵)
Sprite& st(); //显示角色(精灵)
Sprite& ht(); //隐藏角色(精灵)
bool isvisible(); //是否可见
bool ishide(); //是否不可见
int get_width(); //得到当前缩放后的造型宽度
int get_height(); //得到当前缩放后的造型高度
SDL_Rect bbox(); //返回AABB绑定盒
bool contain(int cx,int cy); //某个点是否在角色绑定盒内
bool rect_collide(Sprite *other); //缩放旋转后的矩形碰撞检测
void kill(); // 杀死角色,销毁角色并从屏幕中移除,在堆上生成的角色才能用kill,否则会崩溃(需要用new Sprite来新建)
bool isdestroyed(); //返回角色是否销毁
void destroy(); // 销毁角色,只清理资源,不 delete,
Sprite& scale(double sc); //缩放角色
Sprite& shapesize(double xscale,double yscale); //让角色变形
std::pair<double,double> shapesize() const; //返回角色的变形参数,即xscale和yscale
//以下asbackground方法不公开
Sprite& asbackground(bool add=true); //角色作为背景用(主要为动态背景之用,在普通角色之前渲染)
Sprite& asbg(bool add);
float towards(Sprite& other); //朝向某个角色,返回角度 ,仿Python turtle的towards命令
float towards(const Point& p); //朝向某个点,返回角度
float towards(float x, float y); //朝向x,y坐标,返回角度
int stamp(); // 盖盖章,返回 id
Sprite& clearstamp(int id); // 删除指定 id 图章
Sprite& clearstamps(int n = 0); // 删除 n 个:n>0 前n个,n<0 后n个,n=0 全删
std::vector<Stamp> stampitems(); //返回图章们的所有编号
int rotate_mode(); //得到角色的旋转模式,旋转模式的值小于等于0,表示角色可以360度旋转(仿Scratch图形化编程),为1表示左右旋转,值为2表示上下翻转,值为大于等于3时为不翻转
int rotatemode(); //得到角色的旋转模式
Sprite& rotate_mode(int mode); //设定旋转模式
Sprite& rotatemode(int mode); //设定旋转模式
Sprite& bounce_on_edge(); //碰到边缘就反弹
Sprite& set_tag(std::string tag); //设置角色的标签,标签是用于分组的
std::string get_tag(); //得到角色的标签
void set_flag(std::string flag); //设置角色的标志,标志是用于作标记和,和标签有点像
std::string get_flag(); //得到角色的标志
Sprite& done(); //进入事件循环,仿python海龟的done,调用的是屏幕的mainloop()
Sprite& bgpic(const std::string imgpath); //设定屏幕的背景图片,调用的是屏幕的同名方法
Sprite& title(std::string s); //设定屏幕的标题,调用的是屏幕的同名方法
Sprite& setup(int width,int height); //设定屏幕的宽高,调用的是屏幕的同名方法
Sprite& bgcolor(int color); //设定屏幕的背景颜色,调用的是屏幕的同名方法
Sprite& bgcolor(SDL_Color color); //设定屏幕的背景颜色,调用的是屏幕的同名方法
Sprite& bgcolor(Uint8 r,Uint8 g,Uint8 b,Uint8 a=0); //设定屏幕的背景颜色,调用的是屏幕的同名方法
Sprite& bgcolor(std::string c); //设定屏幕的背景颜色,调用的是屏幕的同名方法
Sprite& delay(unsigned int ms); //设定屏幕的绘画延时,调用的是屏幕的同名方法
Sprite& tracer(bool auto_update); //设定屏幕的追踪器,调用的是屏幕的同名方法 ,追踪角色在进行相关动作后是否立即刷新屏幕 ,默认为真。如果设定为false,则不会显示图像,直到进行update。
Sprite& clear(); //调用的是屏幕的同名clear方法。
Sprite& setalpha(int a); //设定角色透明度
Sprite& addalpha(int step); //增加角色透明度
int getalpha(); //获取角色透明度m_alpha
Sprite& write( char ch, std::string align = "center",
std::vector<std::string> font = {"Arial", "18", "normal"},
double angle = 0.0); //角色的写字符命令,align是对齐方式,font是字体样式,angle是角度
// Sprite& write( std::string text, std::string align = "center",
// std::vector<std::string> font = {"Arial", "18", "normal"},
// double angle = 0.0); //角色的写字符串命令,align是对齐方式,font是字体样式,angle是角度
Sprite& write(DynamicProperty text, std::string align="center",
std::vector<std::string> font={"Arial", "18", "normal"}, double angle=0.0);
//
// Sprite& write(DynamicProperty text, std::string align="center",
// std::tuple<std::string,int,std::string> font={"Arial", 18, "normal"}, double angle=0.0);
//以下_write方法不公开
Sprite& _write( std::string text, std::string align = "center",
std::vector<std::string> font = {"Arial", "18", "normal"},
double angle = 0.0);
//std::pair<int,int> txt2png(std::string text,std::vector<std::string> font_style, std::string filePath);
std::pair<int,int> txt2png(DynamicProperty text,std::vector<std::string> font_style, std::string filePath);
std::pair<int,int> txt2png(char ch);
std::pair<int,int> txt2png(std::string text);
std::pair<int,int> txt2png(std::string text, std::string filePath);
std::pair<int,int> _txt2png(std::string text,std::vector<std::string> font_style, std::string filePath);
Sprite& wait(float seconds); //角色的等待命令,调用屏幕的wait
Sprite& update(); //角色的刷新命令,调用屏幕的update命令
//以下两个displaybbox方法不公开
bool displaybbox(); //返回是否显示绑定盒 m_displaybbox的值
Sprite& displaybbox(bool yes); //设置是否显示碰撞矩形
//画贝塞尔曲线声明
Sprite& bezierQuad(Point startPos, Point endPos, Point controlPos,int steps=12); //使用带有控制点的二次贝塞尔曲线绘制直线
Sprite& bezierCubic(Point startPos, Point endPos, Point startControlPos, Point endControlPos,int steps=20); //使用具有2个控制点的三次贝塞尔曲线绘制直线
Sprite& bezier(Point startPos, Point endPos, Point startControlPos, Point endControlPos,int steps=20); //同上使用具有2个控制点的三次贝塞尔曲线绘制直线
Sprite& bspline(const std::vector<Point>& controlPoints, int steps=20); //B样条曲线命令
Sprite& cubicspline(const std::vector<Point>& points, int steps = 10); // 三次样条命令,会让角色通过所有给定点
Sprite& fill(SDL_Color color,float offsetx=0.0,float offsety=0.0 ); //从角色位置开始洪水填充的命令
Sprite& fill(int hue,float offsetx=0.0,float offsety=0.0); //从角色位置开始洪水填充的命令,hue是颜色的色相
Sprite& fill(Color color,float offsetx=0.0,float offsety=0.0); //从角色位置开始进行洪水填充
Sprite& fill(std::string color,float offsetx=0.0,float offsety=0.0); //在角色位置开始进行洪水填充
Sprite& fill(int r,int g,int b,int a=255,float offsetx=0.0,float offsety=0.0); //洪水填充命令
Sprite& rotate_center(int offset_x,int offset_y); //设定旋转中心(相对于角色中心点)
std::pair<float,float> rotate_center() const ; //返回旋转中心
//以下的draw和draw_bbox方法不公开
void draw(); // 绘制精灵,
void draw_bbox(); //绘制精灵的绑定盒
int layer(); //得到角色所在的层
bool _sprite_type(); //返回角色类型,true表示cm普通角色, false表示bg背景角色
int born_timestamp();
std::vector<Point> get_points_list();
//画多边形存储顶点到 _poly向量里
Sprite& begin_poly();
Sprite& end_poly();
std::vector< std::pair<float,float> > get_poly();
private:
SDL_Texture* getCurrentTexture(); //得到角色当前造型的纹理
// 定义缩放因子:保留 3 位小数精度,这是给offset polygon用的
const double _SCALE;
bool m_displaybbox; //是否显示绑定盒子 ,默认不显示
//造型系统
std::unordered_map<std::string, Shape*> shapesdict; // 造型字典(名称→造型指针)
std::vector<std::string> shapeslist; // 造型列表(按添加顺序存储名称)
int cur_shape_index; // 当前造型在shapeslist列表中的索引(初始-1)
bool m_isvisible; //是否显示
// 同scratch里的旋转模式,2025-9-8增加的功能
// 值小于等于0表示360度旋转,此时在渲染造型时用SDL_FLIP_NONE,旋转角度是多少就是多少
// 值为1表示左右旋转,此时渲染造型是用SDL_FLIP_HORIZONTAL,旋转角度为0
// 值为2表示上下旋转,此时渲染造型是用SDL_FLIP_VERTICAL,旋转角度为0
// 值为大于等于3时为不旋转, 在渲染造型时用SDL_FLIP_NONE,旋转角度是0
int m_rotate_mode; //旋转模式
bool fill_start; //开始填充标记
std::vector< Point > points_list; //点列表
//如果是世界坐标系,则角色的x,y坐标都会看成是世界坐标,在最后渲染的时候要转换成屏幕坐标系渲染。
//如果不是世界坐标系,即本来就是屏幕坐标系,则在最后渲染的时候不需要转换成屏幕坐标系,因为本来这些坐标就是屏幕坐标
std::string m_tag; //标签用于分组
float x, y;
float m_heading; // 角度,0度朝右(东)
Sprite& _heading(float x,float y); //朝向某个坐标
bool m_pen_down;
float m_pen_size;
SDL_Color m_pen_color; // 画笔颜色
float m_pen_hue ; //HSV中的色度,0.0到360
float m_pen_saturation ; //HSV中的饱和度,本来是0.0一1.0,为方便编程设为0.0到100.0
float m_pen_value; //HSV中的 明度 本来是0.0一1.0,为方便编程设为0.0到100.0
float m_pen_shade; // 0.0 ~ 100.0
void updatePenColorFromHSV() ;
SDL_Color m_fill_color;
Screen* screen;
void _pencolor(Uint8 r, Uint8 g, Uint8 b,Uint8 a);
void _fillcolor(Uint8 r, Uint8 g, Uint8 b,Uint8 a);
void draw_round_cap_simple(float,float,float,float,float,bool);
void draw_line(float x1, float y1, float x2, float y2);
void update_if_needed(); // 根据 auto_update 决定是否刷新
float toRadians(float degrees);
// 添加辅助函数声明,用于dot函数调用生成圆的顶点
void generateCircleVertices(SDL_Vertex* vertices, int segments, float radius, const SDL_Color& color);
bool m_isDestroyed; //是否已经自毁
//缩放系数
double xscale,yscale;
int m_alpha; //角色的透明度0到255的值,本质是设定纹理的alpha通道,所以0表示透明
// 速度控制(新增)
int m_speed; // 0~10: 0=最快, 1=最慢, 10=较快 // 内部状态
bool m_moving; // 是否正在移动中(用于动画)
float m_target_x, m_target_y;
float m_target_heading;
float m_remaining_distance;
float m_turn_angle_left;
// 缓存:每级速度对应的步长和角速度
static const float move_step[11]; // speed 1~10,索引 1~10
static const float turn_step[11]; // 每次转向增量
float _distance(float x,float y); //角色到x,y的距离最终调用的方法
//朝向命令最终调用的命令
float _towards(float x, float y); // 计算从 (this->x, this->y) 指向 (x, y) 的角度
//图章管理
static int m_next_stamp_id; // 全局图章 ID 计数器
std::vector<Stamp> m_stamps; // 存储所有图章
// 获取 StampRenderer(从 Screen 获取 renderer)
StampRenderer* getStampRenderer();
// 缓存当前要write的文字的索引(用于复用)
int currentTextIndex ;
std::pair<float,float> m_rotate_center;
//三次样条曲线辅助函数:求解三对角矩阵(用于计算三次样条的斜率)
std::vector<float> solveTridiagonal(const std::vector<float>& a,
const std::vector<float>& b,
const std::vector<float>& c,
const std::vector<float>& d);
// 辅助函数:计算单个三次样条段上的点
Point cubicSplinePoint(float t,
float x0, float y0, float m0,
float x1, float y1, float m1);
SDL_Rect AABBRect; //角色缩放旋转后的AABB矩形 ,用于AABB绑定盒碰撞检测,它的值是屏幕坐标系下的!
SDL_Rect scaledDstRect; //角色在旋转前,SDL_RenderCopyEx命令使用的矩形
int m_layer; //角色所在的层,当角色渲染时根据这个进行排序,数值越大,则越渲染在后面,即越靠前
bool m_sprite_type; //true表示标识是普通角色 ,false表示是bg角色
int m_born_timestamp; //角色诞生的时间戳
std::string m_flag; //角色的标志,用于某些标识之用
std::vector< std::pair<float,float> > _poly;
bool _creatingPoly;
};
#endif // SPRITE_H