CMExamples/PlayerTraceChart2D.cpp
2025-04-23 03:22:49 +08:00

337 lines
7.6 KiB
C++

#include<hgl/util/csv/CSVParse.h>
#include<hgl/type/ArrayList.h>
#include<hgl/type/Map.h>
#include<iostream>
#include<hgl/2d/BitmapLoad.h>
#include<hgl/2d/BitmapSave.h>
#include<hgl/2d/DrawGeometry.h>
#include"BitmapFont.h"
#include<hgl/color/Color.h>
#include<hgl/filesystem/Filename.h>
using namespace hgl;
using namespace hgl::bitmap;
uint POSITION_SCALE_RATE=100; //位置缩放比例,unreal中单位为cm
uint64 BATTLE_FIELD_ID=0; //战场ID
constexpr const uint CHAR_WIDTH=8;
constexpr const uint CHAR_HEIGHT=16;
constexpr const uint CHAR_HALF_WIDTH=4;
constexpr const uint CHAR_HALF_HEIGHT=8;
constexpr const uint ICON_SIZE=8;
BitmapRGB8 *BackgroundBitmap=nullptr;
DrawGeometryRGB8 *draw_bmp=nullptr;
using TraceList=ArrayList<Vector2i>;
ObjectMap<uint,TraceList> PlayerTrace; //玩家轨迹
Vector3u8 *PlayerColor=nullptr;
class TraceParse:public util::CSVParseCallback<char>
{
uint record_count=0;
uint parse_count=0;
uint valid_count=0;
Vector2i pos;
uint64 bf_id;
uint player_id;
public:
bool ParsePosition(const char *str,const int len)
{
const char *sp=str;
const char *cp;
cp=hgl::strchr(sp,' ');
if(!cp)return(false);
if(!hgl::stoi(sp,pos.x))
return(false);
++cp;
if(!hgl::stoi(cp,pos.y))
return(false);
pos/=POSITION_SCALE_RATE;
if(pos.x<0||pos.y<0)return(false);
if(pos.x>=BackgroundBitmap->GetWidth()
||pos.y>=BackgroundBitmap->GetHeight())return(false);
return(true);
}
bool OnLine(util::CSVFieldSplite<char> &split) override
{
++record_count;
const char *p;
int len;
p=split.next_field(&len);
if(!p)
return(false);
if(!ParsePosition(p,len))
return(false);
p=split.next_field(&len);
if(!hgl::stou(p,bf_id))
return(false);
if(bf_id!=BATTLE_FIELD_ID)
return(false);
p=split.next_field(&len);
if(!hgl::stou(p,player_id))
return(false);
++parse_count;
{
TraceList *tl;
if(!PlayerTrace.Get(player_id,tl))
{
tl=new TraceList;
PlayerTrace.Add(player_id,tl);
}
tl->Add(pos);
}
return(true);
}
};//class CSVTest;
bool LoadCSVRecord(const OSString &filename)
{
TraceParse tp;
return util::ParseCSVFile(filename,&tp);
}
void DrawIcon(const uint index,const uint x,uint y,const Vector3u8 &color)
{
const uint8 *sp=Get8x8Char(index);
if(!sp)return;
draw_bmp->SetDrawColor(color);
draw_bmp->DrawMonoBitmap(x,y,sp,8,8);
}
void DrawChar(const char ch,const uint x,const uint y)
{
const uint8 *sp=Get8x16Char(ch);
if(!sp)return;
draw_bmp->DrawMonoBitmap(x,y,sp,CHAR_WIDTH,CHAR_HEIGHT);
}
void DrawString(const uint x,const uint y,const AnsiString &str,const Vector3u8 &color)
{
const char *sp=str.c_str();
const uint len=str.Length();
uint pos=x;
draw_bmp->CloseBlend();
draw_bmp->SetDrawColor(color);
for(uint i=0;i<len;i++)
{
if(*sp!=' ')
DrawChar(*sp,pos,y);
pos+=CHAR_WIDTH;
++sp;
}
}
void StatPlayerTrace()
{
const uint pc=PlayerTrace.GetCount();
const auto *pt=PlayerTrace.GetDataList();
uint left=10;
uint top=10;
AnsiString str;
for(uint i=0;i<pc;i++)
{
std::cout<<"Player "<<(*pt)->key<<" Trace Count "<<(*pt)->value->GetCount()<<std::endl;
//draw player icon and id
{
DrawIcon(i+1,left,top+4,PlayerColor[i]);
str=AnsiString::numberOf((*pt)->key);
DrawString(left+20,top,str,PlayerColor[i]);
top+=CHAR_HEIGHT+2;
{
TraceList *tl=(*pt)->value;
const Vector2i *last_pos=nullptr;
const Vector2i *pos=tl->GetData();
const uint count=tl->GetCount();
for(uint j=0;j<count;j++)
{
draw_bmp->SetDrawColor(PlayerColor[i]);
if(j==0)
{
draw_bmp->DrawSolidCircle(pos->x,pos->y,8);
}
else
{
draw_bmp->DrawLine(last_pos->x,last_pos->y,pos->x,pos->y);
str=AnsiString::numberOf(j);
DrawString(pos->x-str.Length()*CHAR_HALF_WIDTH,pos->y-CHAR_HALF_HEIGHT,str,PlayerColor[i]);
}
last_pos=pos;
++pos;
}
}
}
++pt;
}
}
int os_main(int argc,os_char **argv)
{
std::cout<<"PlayerTraceChart2D"<<std::endl;
if(argc<5)
{
std::cout<<"Example: map.tga 100 data.csv XXXXXX"<<std::endl;
std::cout<<" map.tga The map image file"<<std::endl;
std::cout<<" 100 position div rate"<<std::endl;
std::cout<<" data.csv Player trace data file"<<std::endl;
std::cout<<" XXXXXX BattleField ID"<<std::endl;
return(0);
}
{
BackgroundBitmap=bitmap::LoadBitmapRGB8FromTGA(argv[1]);
if(!BackgroundBitmap)
{
std::cerr<<"Load background bitmap file failed!"<<std::endl;
return(-1);
}
std::cout<<"Load background bitmap file OK!"<<std::endl;
std::cout<<"Background bitmap size: "<<BackgroundBitmap->GetWidth()<<"x"<<BackgroundBitmap->GetHeight()<<std::endl;
}
{
if(!stou(argv[2],POSITION_SCALE_RATE))
{
std::cerr<<"Parse position scale rate failed!"<<std::endl;
return(-2);
}
std::cout<<"Position scale rate is "<<POSITION_SCALE_RATE<<std::endl;
}
{
if(!stou(argv[4],BATTLE_FIELD_ID))
{
std::cerr<<"Parse battle field id failed!"<<std::endl;
return(-3);
}
std::cout<<"Battle field id is "<<BATTLE_FIELD_ID<<std::endl;
}
{
if(!LoadBitmapFont())
{
std::cerr<<"Load bitmap font failed!"<<std::endl;
return(-4);
}
}
const OSString csv_filename=argv[3];
{
if(!LoadCSVRecord(csv_filename))
{
std::cerr<<"Load CSV record file failed!"<<std::endl;
return(-4);
}
std::cout<<"Load CSV record file OK!"<<std::endl;
{
uint total=0;
const uint pc=PlayerTrace.GetCount();
std::cout<<"Player Count: "<<pc<<std::endl;
if(pc>0)
{
PlayerColor=new Vector3u8[pc];
const auto *pt=PlayerTrace.GetDataList();
Color3f color;
for(uint i=0;i<pc;i++)
{
std::cout<<"Player "<<(*pt)->key<<" Trace Count "<<(*pt)->value->GetCount()<<std::endl;
GetBGR((COLOR)(i+(int)COLOR::Blue),PlayerColor[i]);
total+=(*pt)->value->GetCount();
++pt;
}
}
if(total<=0)return(-5);
}
}
draw_bmp=new DrawGeometryRGB8(BackgroundBitmap);
StatPlayerTrace();
{
const OSString tga_filename=filesystem::ReplaceExtName(csv_filename,OSString(OS_TEXT(".tga")));
os_out<<OS_TEXT("output: ")<<tga_filename.c_str()<<std::endl;
if(!SaveBitmapToTGA(tga_filename,BackgroundBitmap))
std::cerr<<"Create tga failed!"<<std::endl;
}
delete[] PlayerColor;
delete draw_bmp;
ClearBitmapFont();
return(0);
}