前言
学的东西多了,要想办法用出来.C和C++是偏向底层的语言,直接与数据打交道.尝试做一些和数据方面相关的内容
引入
前面的内容都是矩阵图形类,现在讨论自由图形类设计
矩阵图形类和自由图形类的差别

左图为矩阵图形类对象,右图为自由图形类对象.矩阵图形类对象单独占据一个矩形框大小的位置,自由图形类对象是单独图形.在拖动时,左边对象带着矩形框,而右边不用.
自由图形类定义
自由图形类和前面的不规则图形类是差不多的,只不过当时举了一个字符的例子,算一个特例,现在让自由图形回到他最初的样子---像素图
//自由图形类(像素图)定义
struct Pixel_pic{
vector<Pixel_point> pps; //像素点集合
}
//像素点类型定义
struct Pixel_point{
short x_cord;
short y_cord;
short red;
short green;
short blue;
}
说明:这里的像素点定义和前面的字符定义有所区别,加上了颜色.原因是节省空间.试想有10000个字符,每个字符平均有50个屏幕点,那么节省空间有10000x50x6=300万字节,即3MB,看起来不大但无颜色的更简洁.
多张像素图的混合
顾名思义,几张图合成一张图,具有层叠效果,区别于矩阵图形类对象.例如上一张图可以看作是"/"和"\"的组合.
1>形式选择
前面矩阵图形类采用函数的写法,这里用类的写法.如果采用"数据类"定义的话,如下
//自由图形类(像素图)定义---不好用
struct Pixel_pic{
vector<Pixel_point> pps; //像素点集合
vector<Pixel_pic> pcs; //像素图由其他像素图组成
}
和前面的矩阵类定义比起来,看起来有些别扭,因为矩阵类对象用了散列表,而这里好像也不需要.所以考虑用工具类定义.
工具类用什么名称呢?前面有提到过类名要使用名词.建议采用generator或者builder,表示"生成器",用Pixel_pic_Builder.
//像素混合器定义
class Pixel_pic_Builder{
vector<Pixel_pic> pps;
... //内容暂定
}
2>混合算法
笔者尝试了两种方案,也可以作为一种思维的锻炼.
方案一
代码思路:
1)生成的最终对象,仍是Pixel_pic对象,即"原材料"和"产品"都是相同的数据类型
- 在一个矩形方框内混合,每加一张图,每加一个点,去遍历之前存在的点,如果没有,则添加,如果有,则修改.
===============================内容分割线↓=================================
问题:如何确定矩形方框的大小?有一个办法:修改像素图定义如下
//更新并略过---自由图形类(像素图)定义
struct Pixel_pic{
vector<Pixel_point> pps; //像素点集合
short length; //图的长度
short height; //图的高度
}
然而这个是无法确定的,因为考虑到直接叠起来的高度比较高,因此略过这一点.
对于高度的问题,可以固定一个,例如软件里的一个框,让像素图的高度来适应他.
===============================内容分割线↑=================================
//像素混合器定义---不成熟方案
class Pixel_pic_Builder{
public:
//materials原材料像素图集合;result结果像素图
Pixel_pic build(vector<Pixel_pic> & materials,Pixel_pic& result){
//遍历原材料像素图集合
for(msd=materials.begin();msd!=materials.end();msd++){
//遍历原材料像素图中的点
for(msdd=(*msd).pps.begin();msdd!=(*msd).pps.end();msdd++){
//遍历结果像素图中的点
for(rtd=result.pps.begin();rtd!=result.pps.end();rtd++){
//点已存在
if((*msdd).x_cord==(*rtd).x_cord)&& (*msdd).y_cord==(*rtd).y_cord){
//颜色覆盖
(*rtd).red=(*msdd).red;
(*rtd).green=(*msdd).green;
(*rtd).blue=(*msdd).blue;
}
//否则加上这个点
else
result.pps.add(Pixel_point{(*msdd).x_cord,(*msdd).y_cord,(*msdd).red,(*msdd).green,(*msdd).blue});
}
}
}
return result;
}
}
说明:遍历图的每一个点,同时遍历已生成的结果图里的点,查询是否有坐标重叠.如果坐标已存在,则用新点颜色覆盖;如果坐标未存在,则在结果中加上这个点.
这个写法非常复杂,而且时间复杂度达到n的3次方了---有一层是像素图动态数组,不算在内的话,复杂度是n的2次方.如果不是没有其他选择方案,不会被用于工程中.
方案二
代码思路:用一个辅助的矩阵类对象.
1>将像素图混合到矩阵类对象中,并记录图中所有点经过的位置.
2>重新遍历矩阵类对象,取出其中经过位置的点组成像素图.
1.定义一个辅助矩阵类
//辅助矩阵点定义
struct Assistant_point{
bool is_choosed;
short red;
short green;
short blue;
}
//辅助矩阵类定义
struct Assistant_matrix{
short length;
short height;
vector<vector<Assistant_point>> aps;
}
为了让程序清晰,不把所有像素图放一起,而是采用每一张都和矩阵类对象混合.引用上面的工具类来做这件事:
2.将像素图写入辅助矩阵类对象
//像素混合器定义
class Pixel_pic_Builder{
public:
Assistant_matrix & build_in(short x_ref,short y_ref,Assistant_matrix & ax,Pixel_pic & source){
//辅助矩阵类对象的初始行指针,定位到被放入像素图的基点位置
auto axd=ax.aps.begin()+x_ref;
//遍历像素图
for(pcd=source.pps.begin();pcd!=source.pps.end();pcd++){
axd=axd+(*pcd).x_cord; //行指针偏移
//*axd表示得到该行,行仍然是一个vector对象,所以调用begin()基点位置,并计算坐标
auto axdd=(*axd).begin()+y_ref+(*pcd).y_cord;
(*axdd).red=(*pcd).red;
(*axdd).green=(*pcd).green;
(*axdd).blue=(*pcd).blue;
(*axdd).is_choosed=true; //该点已被选中
}
//返回辅助矩阵类对象
return ax;
}
}
上面混合了一张图,按照这方法并可以把所有需要混合的图都写入辅助矩阵类对象.
3.将想要得到的Pixel_pic对象从辅助矩阵类对象中抽出来
本来可以用前面的build_in函数写在一起,为了看得清楚一些,分成了两个程序来写.
//像素混合器定义---有问题不能用
class Pixel_pic_Builder{
public:
Pixel_pic & build_out(Assistant_matrix & ax){
//生成一个空的对象表示结果
Pixel_pic result();
for (short i = 0; i <ax.height; i++)
for (short j = 0; j <ax.length; j++){
//如果显示被标记
if(ax[i][j].is_choosed){
result.pps.push_back(Pixel_point{i,j,ax[i][j].red,ax[i][j].green,ax[i][j].blue});
}
}
}
return result;
}
}
这里没有用二维数组的两次vector遍历,用了C语言一样的[][]来表示元素,意思是一样的.如果不成功,可以改成迭代器遍历.
更新
果然上面代码有点问题,修改如下:
Pixel_pic& build_out(Assistant_matrix& ax) {
//生成一个空的对象表示结果
Pixel_pic result;
short i = 0,j=0;
for ( auto axd=ax.aps.begin() ; axd!=ax.aps.end();axd++,i++)
for (auto axdd = (*axd).begin(); axdd != (*axd).end(); j++) {
//如果显示被标记
if ((*axdd).is_choosed) {
result.pps.push_back(Pixel_point{ i,j,(*axdd).red,(*axdd).green,(*axdd).blue });
}
}
return result;
}
现在可以得到一个以辅助矩阵类对象基点作为基点的像素混合图
问题
代码里有个问题,辅助矩阵类对象的长和高,像素图的长和高,没有表示.怎么解决?
1.不解决
假设辅助矩阵是在ps软件中的画布,给他固定一个尺寸,然后规定像素图的长和高比他小才能用
2.部分解决
给像素图设置两个属性,即修改定义如下:
//自由图形类(像素图)定义
struct Pixel_pic{
short height;
short length;
vector<Pixel_point> pps; //像素点集合
}
再写个算法,像素图超过尺寸后截取图像,规定尺寸小于矩阵对象才能混合.
这样做可能还会有问题.因为限制了图形尺寸功能差许多.
还可以想到的解决方法:把源像素图放到另一个场景中,根据情况从另一个场景写到当前画布来.
重申:毕竟是为了开拓思路而写的帖子,不是真正开发个ps软件,写工程级别代码遇到什么问题再说.
小结
自由图形类的混合算法,从中可以看出解决问题有多种方案,从中选择适当的使用.
每个类型设计中,属性定义很重要;不过也不要太纠结,想到什么都给他写上去也不会错.