UE4C++饼状图绘制(EChart)

UE4C++饼状图绘制(EChart),第1张

UE4C++饼状图绘制(EChart)

  • 前言
    在UE4中实现饼状图,折线图等一系列图可能得采用材质,或者凑一个UMG的方式实现我们想要的图,在这我给大家分享如何去实现一个饼状图。
    实现目标:在UMG中添加饼状图

    知识要求:基本UEC++,了解图形学的原理。

(学过图形学原理的这段可以跳过)
模型绘制过程中,采用的是传统的点线面的绘制,即从绘制一个像素点,到绘制一条线,到绘制一条面的过程,一般的引擎渲染都是以三角形作为最基础的面的绘制,也有的会采用四边形来作为最基础的图形绘制,这里我们采用的是三角形来讲解原理。而在代码中的体现则是:用一个数组存储顶点信息(位置、UV值、颜色等),再用一个数组存储下标信息,即从下标数组中去三个下标绘制成三角形,循环遍历整个数组即可绘制多个三角形,最好获得由三角形组成的图像或者模型。

首先:扇形是属于圆的一部分,我们如果能绘制圆,择也能绘制扇形,圆我们可以想作由无数个以圆心为中心点,四周无数个三角形构成的图案,而扇形则是有起始角度范围的圆,见下图:

我们将圆分成360份来绘制,代码如下(在此就不具体讲解怎么做的了,代码中都有注释):
编译时候将代码放在自己需要的文件路径,再将SPECIALCHART_API 改成 大写文件夹名字_API,再添加模块即可编译成功:

PrivateDependencyModuleNames.AddRange(
	new string[]
	{
		"CoreUObject",
		"Engine",
		"Slate",
		"SlateCore",
		"UMG"
		// ... add private dependencies that you statically link with here ...	
	}
	);
  • .h文件
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "PieWidget.generated.h"

/**
 * 饼状图
 */
UCLASS()
class SPECIALCHART_API UPieWidget : public UUserWidget
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintCallable)
		//蓝图中设置基础角度值
		void SetValues(TArray<int32>InAngles);
	UFUNCTION(BlueprintCallable)
		//蓝图中设置基础角度的颜色值
		void SetColors(TArray<FLinearColor>InColors);

protected:
	UPieWidget(const FObjectInitializer& ObjectInitializer);

	//绘制
	virtual int32 NativePaint(const FPaintArgs& Args,
		const FGeometry& AllottedGeometry,
		const FSlateRect& MyCullingRect, //绘制的矩形
		FSlateWindowElementList& OutDrawElements,//返回的信息
		int32 LayerId, 
		const FWidgetStyle& InWidgetStyle, //UMG样式
		bool bParentEnabled) const;
private:
	void DrawFan(FSlateWindowElementList&OutDrawElements,//Slate窗口元素列表
		const FGeometry&AllottedGemoetry,//几何信息
		int32 Layer,//层级
		const FVector2D& CenterPosition,//圆心坐标位置
		float Radius,int32 BeginAngle,//开始角度
		int32 EndAngle,//结束角度
		FColor Color)const;//绘制扇形的颜色

	FColor GetColorByIndex(int32 InIndex)const;//通过数组下标获取颜色值

	TArray<int32>Angles;//传入的不同扇形角度
	TArray<FLinearColor>Colors;//传入的不同扇形的颜色值
};
  • .cpp文件
// Fill out your copyright notice in the Description page of Project Settings.


#include "PieWidget.h"

UPieWidget::UPieWidget(const FObjectInitializer& ObjectInitializer)
	:Super(ObjectInitializer)
{
}
//角度数据计算
void UPieWidget::SetValues(TArray<int32> InAngles)
{
	if(InAngles.Num()<1)
		return;
	Angles.Empty();
	Angles.Add(0);
	float Total = 0;
	for (int32 Index=0;Index<InAngles.Num();Index++)
	{
		Total += InAngles[Index];
	}
	float CurrentTotal = 0;
	for (int32 Index = 0; Index < InAngles.Num(); Index++)
	{
		CurrentTotal += InAngles[Index];
		int32 Angle = (CurrentTotal / Total) * 360;
		Angles.Add(Angle);
	}
}

void UPieWidget::SetColors(TArray<FLinearColor> InColors)
{
	Colors = InColors;
}
//绘制多个扇形组成一个圆
int32 UPieWidget::NativePaint(const FPaintArgs& Args,
                              const FGeometry& AllottedGeometry,
                              const FSlateRect& MyCullingRect,
                              FSlateWindowElementList& OutDrawElements, int32 LayerId,
                              const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	for (int32 Index=0;Index<Angles.Num()-1;Index++)
	{
		int32 BeginAngle = Angles[Index];
		int32 EndAngle = Angles[Index+1];
			DrawFan(OutDrawElements,
				AllottedGeometry,
				LayerId,
				FVector2D(300.f, 300.f),
				250.f,
				BeginAngle,
				EndAngle,
				GetColorByIndex(Index)
			);
	}
	return LayerId++;
}
//扇形绘制
void UPieWidget::DrawFan(FSlateWindowElementList& OutDrawElements,
	const FGeometry& AllottedGemoetry, 
	int32 Layer,
	const FVector2D& CenterPosition, 
	float Radius, 
	int32 BeginAngle, int32 EndAngle,
	FColor Color) const
{
	if(EndAngle < BeginAngle||Radius<0.f)
		return;
	//顶点数组
	TArray<FSlateVertex>SlateVertexArray;
	//索引数组
	TArray<SlateIndex>SlateIndexArray;
	for (int32 CurrentAngle=BeginAngle; CurrentAngle <EndAngle; CurrentAngle++)
	{
		//当前顶点信息
		FSlateVertex CurrentSlateVertex;
		//下一个顶点信息
		FSlateVertex NextSlateVertex;
		//中心坐标顶点信息
		FSlateVertex CenterSlateVertex;
		//当前顶点位置
		FVector2D CurrenSlateVertexPosition = FVector2D(
			CenterPosition.X+Radius* FMath::Cos(FMath::DegreesToRadians(CurrentAngle)),
			CenterPosition.Y + Radius * FMath::Sin(FMath::DegreesToRadians(CurrentAngle))
		);
		//下一个顶点的位置信息
		FVector2D NextSlateVertexPosition = FVector2D(
			CenterPosition.X + Radius * FMath::Cos(FMath::DegreesToRadians(CurrentAngle+1)),
			CenterPosition.Y + Radius * FMath::Sin(FMath::DegreesToRadians(CurrentAngle+1))
		);
		//转换成渲染空间
		CurrentSlateVertex.Position = AllottedGemoetry.ToPaintGeometry().GetAccumulatedRenderTransform().TransformPoint(CurrenSlateVertexPosition);
		NextSlateVertex.Position =AllottedGemoetry.ToPaintGeometry().GetAccumulatedRenderTransform().TransformPoint(NextSlateVertexPosition);
		CenterSlateVertex.Position = AllottedGemoetry.ToPaintGeometry().GetAccumulatedRenderTransform().TransformPoint(CenterPosition);

		//顶点颜色值
		CurrentSlateVertex.Color = Color;
		NextSlateVertex.Color = Color;
		CenterSlateVertex.Color = Color;
		//顶点下标存储
		int32 IndexOfCurrentSlateVertex= SlateVertexArray.Add(CurrentSlateVertex);
		int32 IndexOfNextSlateVertex = SlateVertexArray.Add(NextSlateVertex);
		int32 IndexOfCenterSlateVertex = SlateVertexArray.Add(CenterSlateVertex);
		//虚幻引擎是右手法则,用左手添加绘制出来的三角形可能看不见
		SlateIndexArray.Add(IndexOfCurrentSlateVertex);
		SlateIndexArray.Add(IndexOfNextSlateVertex);
		SlateIndexArray.Add(IndexOfCenterSlateVertex);
	}
	//顶点的UV
	for(FSlateVertex&TempSlateVertex:SlateVertexArray)
	{
		TempSlateVertex.TexCoords[0] = 0.f;
		TempSlateVertex.TexCoords[1] = 0.f;
		TempSlateVertex.TexCoords[2] = 0.f;
		TempSlateVertex.TexCoords[3] = 0.f;
	}
	//获取Brush(根据的虚幻引擎其他地方调用采用的同样方式获取的)
	const FSlateBrush* SlateBrush = FCoreStyle::Get().GetBrush("ColorSpectrum.Spectrum");
	//获取资源句柄
	FSlateResourceHandle SlateResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*SlateBrush);
	//绘制自定义顶点
	FSlateDrawElement::MakeCustomVerts(
		OutDrawElements,
		Layer,
		SlateResourceHandle,
		SlateVertexArray,
		SlateIndexArray,
		nullptr,
		0, 0
	);
}
//通过下标获取颜色数组
FColor UPieWidget::GetColorByIndex(int32 InIndex) const
{
	if (Colors.Num() < 1)
		return FColor::White;
	//取余防止越界
	return Colors[InIndex % Colors.Num()].ToFColor(true);
}

继承此类创建蓝图

蓝图中五组数据添加:



即可看见我们需要的饼状图

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/713314.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存