随机 terazzo 生成器
前言文章基本思路来自:James Routley其中,判断点是否在多边形内的算法来自:PNPOLY – Point Inclusion in Polygon Test – WR Franklin (WRF)
本代码中的颜色来自于:RGB颜色值与十六进制颜色码转换工具,并使用RGB与十六进制与RGB565颜色转换工具转换为RGB565代码。(坦白说,我还挺喜欢这些颜色的,比莫奈色系要更鲜艳一点)
想要实现本文代码,你至少需要自己实现有如下功能的函数:绘制直线、绘制点、随机数。
Terazzo 是一种通过将小的彩色碎屑粘合在水泥中制成的材料。凝固后,对其进行切割和抛光,露出由木屑制成的图案。它在用为装饰性随机背景时有不错的表现。
本程序的原理:通过随机产生不重叠的圆,并在圆内内接一个多边形,即可确保生成的多边形是凸多边形且互不交叉。
图为原理演示图。由于画圆函数无法正确处理负数坐标,出现了重叠的情况。以及它的最终效果看起来是这样的:
下面是代码:
terazzo.h#ifndef TERAZZO_H
#define TERAZZO_H
#include "./BSP/LCD/lcd.h"
#include "./SYSTEM/sys/sys.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#define Pink 0xfe19//粉色
#define LavenderBlush 0xff9e//脸红的淡紫色
#define Thistle 0xddfb//蓟
#define MediumSlateBlue 0x7b5d//适中的板岩暗蓝灰色
#define Lavender 0xe73f//熏衣草花的淡紫色
#define CornflowerBlue 0x64bd//矢车菊的蓝色
#define LightSteelBlue 0xb63b//淡钢蓝
#define LightCyan 0xe7ff//淡青色
#define Auqamarin 0x7ff5//绿玉\碧绿色
#define Khaki 0xf731//卡其布
#define Moccasin 0xff36//鹿皮鞋
#define LightSalmon 0xfd0f//浅鲜肉(鲑鱼)色
#define LightCoral 0xf410//淡珊瑚色
typedef struct {
uint8_t r;
uint16_t x;
uint16_t y;
}Circle;
extern uint16_t terazzo_COLORS;
/*清空画布*/
void init_terazzo(void);
/* 在指定大小的画布内(尽可能地)画指定数量的多边形 */
void update_terazzo(uint16_t width,uint16_t height,uint16_t num);
#endifterazzo.c#include "./BSP/terazzo/terazzo.h"
Circle c={0}; //圆集合
uint16_t cnum=0; //圆个数
uint16_t terazzo_COLORS={Pink,LavenderBlush,Thistle,MediumSlateBlue,Lavender,CornflowerBlue,LightSteelBlue,LightCyan,Auqamarin,Khaki,Moccasin,LightSalmon,LightCoral};
uint16_t p_width=320,p_height=480; //画布宽度
void init_terazzo(void){
//c={0};
cnum=0;
lcd_clear(g_back_color);
}
int terazzo_Random(int n)
{
srand(SysTick->VAL+n); /* 半主机模式下使用time函数会报错,在这里用系统定时器的值替代 */
return rand() % 1000;
}
int check_circle(Circle *circle){
for(int i=0;i<cnum;i++){
int x=circle->x-c.x;
int y=circle->y-c.y;
if((x*x+y*y)<pow(circle->r+c.r,2)){
//printf("0");
return 0;
}
}
//printf("check_success\n");
return 1;
}
/*
返回一个不与其它圆重叠的圆,如果返回的圆半径为0,说明找不到这种圆
*/
Circle create_circle(){
Circle cir;
int r=0;
do{
cir.r=terazzo_Random(r+=10)%45+10;
cir.x=terazzo_Random(r+=10)%p_width;
cir.y=terazzo_Random(r+=10)%p_height;
}while(!check_circle(&cir)&&(r<3000));
if(!check_circle(&cir)){
//printf("creat_error\n");
cir.r=0;
}
//printf("\n%d %d\n",cir.x,cir.y);
//printf("creat_success\n");
return cir;
}
/*
填充指定圆里的多边形,采用逐行扫描填充算法,圆的边界坐标用勾股定理获得。
(Bresenham算法没学会)
按照从左到右、从上到下的方法扫描
注意,本函数的边界判断有很大漏洞。
*/
void terazzo_scan_fill_color(Circle cir,uint16_t color){
for(int i=0;i<=cir.r;i++){
if(i<0)i=0;
if(i>=p_width)i=p_width-1;
int h =sqrt(cir.r*cir.r-pow(cir.r-i,2)); //计算扫描长度
uint8_t last_point = 0;
uint8_t b = 0;
for(int j=cir.y-h;j<cir.y+h;j++){ //左边
int ty = j;
if(ty<0)ty=0;
if(ty>=p_height)ty=p_height-1; //判断是否越界
int tx=cir.x-cir.r+i;
if(tx<0)tx=0;
if(tx>=p_width)tx=p_width-1;
if(lcd_read_point(tx,ty)==color){ //扫描到彩色
if(!last_point){ //上一个点不是彩色,说明这个点是边界
b=!b; //切换填色
last_point=1;
}
}else{
last_point=0;
}
if(b){
lcd_draw_point(cir.x-cir.r+i,j,color);
}
}
b = 0;
last_point = 0;
for(int j=cir.y-h;j<cir.y+h;j++){ //右边
int ty = j;
if(ty<0)ty=0;
if(ty>=p_height)ty=p_height-1; //判断是否越界
int tx=cir.x+cir.r-i;
if(tx<0)tx=0;
if(tx>=p_width)tx=p_width-1;
if(lcd_read_point(tx,ty)==color){ //扫描到彩色
if(!last_point){//上一个点不是彩色,说明这个点是边界
b=!b; //切换填色
last_point=1;
}
}else{
last_point=0;
}
if(b){
lcd_draw_point(cir.x+cir.r-i,j,color);
}
}
printf("fill,%d\n",h);
}
}
/*此算法由W. Randolph Franklin提出*/
/*参考链接:https://wrfranklin.org/Research/Short_Notes/pnpoly.html*/
uint8_t terazzo_pnpoly(int nvert, uint16_t *vertx, uint16_t *verty, uint16_t testx, uint16_t testy)
{
uint16_t i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty>testy) != (verty>testy)) &&
(testx < (vertx-vertx) * (testy-verty) / (verty-verty) + vertx) )
c = !c;
}
return c;
}
/*
采用判断点是否在多边形的内部,从而上色。
nvert:顶点数
vertx/y:存有顶点坐标的数组
*/
void terazzo_vector_fill_color(Circle cir,uint16_t color,uint8_t nvert, uint16_t *vertx, uint16_t *verty){
for(int i=0;i<=cir.r*cir.r;i++){
int h =sqrt(cir.r*cir.r-pow(cir.r-i,2)); //计算扫描长度
for(int j=cir.y-h;j<=cir.y+h;j++){
int ty = j;
if(ty<0)ty=0;
if(ty>=p_height)ty=p_height-1; //判断是否越界
int tx=cir.x+cir.r-i;
if(tx<0)tx=0;
if(tx>=p_width)tx=p_width-1;
if(OLED_pnpoly(nvert,(short *)vertx,(short *)verty,tx,j)){
//printf("");
lcd_draw_point(tx,j,color);
}
}
}
}
/*
在一个给定的圆里选取随机的点,并按指定的颜色连线
内部连线方式:先随机产生3~6个弧度值,并按大小排序。
然后按照排序顺序生成点坐标并连线。
这样可以确保生成的连线不会交叉。
*/
void link_point(Circle *cir,uint16_t color){
if(cir->r==0){
//printf("draw_error\n");
return;
}
int zeta_num = terazzo_Random(cir->x+cir->y) % 5 + 3;
float zeta;
for(int i=0;i<zeta_num;i++){
zeta = terazzo_Random(i+cir->x+cir->y)%628/100.0f;//生成弧度值
}
/* 选择排序,小的在前 */
for(int i=0;i<zeta_num;i++){
int z=zeta_num-1;
for(int j=zeta_num-1;j>=i;j--){
if(zeta>zeta){
z=j;
}
}
float t=zeta;
zeta=zeta;
zeta=t;
}
int16_t pointx,pointy;
/* 生成点坐标 */
for(int i=0;i<zeta_num;i++){
pointx=(cir->x-cir->r*cos(zeta));
pointy=(cir->y-cir->r*sin(zeta));
if(pointx>=p_width){
pointx=p_width-1;
}
if(pointy>=p_height){
pointy=p_height-1;
}
if(pointx<0){
pointx=0;
}
if(pointy<0){
pointy=0;
}
}
/* 连线 */
for(int i=0;i<zeta_num;i++){
lcd_draw_line(pointx,pointy,pointx[(i+1)%zeta_num],pointy[(i+1)%zeta_num],color);
}
terazzo_vector_fill_color(*cir,color,zeta_num,(uint16_t *)pointx,(uint16_t *)pointy);
//lcd_draw_circle(cir->x,cir->y,cir->r,RED);
c=*cir;
//printf("draw:%d\n",cnum);
}
/*
在指定大小的画布内(尽可能地)画指定数量的多边形
`*/
void update_terazzo(uint16_t width,uint16_t height,uint16_t num){
p_width=width;
p_height=height;
Circle cir;
for(int i=0;i<num;i++){
cir=create_circle();
link_point(&cir,terazzo_COLORS);
}
printf("%d\n",cnum);
}勾股定理算圆心距离,与半径和比较判断两圆位置关系。
Circle create_circle();
int check_circle(Circle *circle){
for(int i=0;i<cnum;i++){
int x=circle->x-c.x;
int y=circle->y-c.y;
if((x*x+y*y)<pow(circle->r+c.r,2)){
return 0;
}
}
return 1;
}值得注意的是对生成的弧度值进行排序:这是为了防止在连线的时候生成形如沙漏的自交叉的多边形。
如果你不想生成过于细长的形状,可以在函数第16行改为【上一次产生的弧度值+随机值+合适的常数】来规定两个点之间的最小距离。注意别超过2Π。
void update_terazzo(uint16_t width,uint16_t height,uint16_t num);
/* 在指定大小的画布内(尽可能地)画指定数量的多边形`*/void update_terazzo(uint16_t width,uint16_t height,uint16_t num){ p_width=width; p_height=height; Circle cir; for(int i=0;i
页:
[1]