网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
08月08日漏签0天
easyx吧 关注:10,938贴子:46,985
  • 看贴

  • 图片

  • 吧主推荐

  • 游戏

  • 3回复贴,共1页
<<返回easyx吧
>0< 加载中...

EasyX 实现箭头围绕小球 3D 旋转

  • 只看楼主
  • 收藏

  • 回复
  • 邹汇东
  • 二年级
    5
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
代码见楼下


  • 邹汇东
  • 二年级
    5
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
//////////////////////////////////////////////////
//
// 代码效果:箭头围绕小球进行 3D 旋转
// 作  者:huidong <mailhuid@163.com>
// 编译环境:Visual Studio 2022 + EasyX_20220901
// 完成时间:2024.6.11
//
#include <easyx.h>
#include <math.h>
#define PI 3.1415926
// 主球和轨迹圆的半径
int nR_ball = 100;
int nR_arrow = 130;
// 箭头长度(包括直线部分和三角形部分)
int nArrowLength = 120;
int nArrowHeadLength = 50; // 特指箭头的三角形部分
int nArrowHeigth = 40;
int nArrowHeadHeigth = 60;
// 绘图设备的渲染原点(仅用于调整绘制位置)
int nX0 = 320;
int nY0 = 240;
// 视野尽头 y 坐标
int nViewEndY = -1000;
// 判断一个弧度值是否位于某开区间内(同时自动匹配该区间的相邻周期区间)
bool IsInRange(double _X, double _Begin, double _End)
{
return (_X > _Begin && _X < _End)
|| (_X > _Begin - 2 * PI && _X < _End - 2 * PI)
|| (_X > _Begin + 2 * PI && _X < _End + 2 * PI);
}
// 将一个弧度转换到 [0, 2π) 内
double GetStandardRadian(double _X)
{
double X = _X;
while (!(X >= 0 && X < 2 * PI))
{
if (X < 0) X += 2 * PI;
else if (X >= 2 * PI) X -= 2 * PI;
}
return X;
}
// 获取箭头图案在某个点的厚度
// p_dRelativeRadian 该点相对于 A 点的弧度
int GetPatternThickness_Point(double p_dRelativeRadian, bool p_bIsLeftHanded)
{
// 整个箭头投影到其轨迹圆上的圆心角弧度
double dTotalRadian = (double)nArrowLength / nR_arrow;
// 箭头的三角形部分对应圆心角弧度
double dHeadRadian = (double)nArrowHeadLength / nR_arrow;
double dRelativeRadian = p_dRelativeRadian;
// 若箭头朝右,则变换相对弧度
if (!p_bIsLeftHanded)
{
dRelativeRadian = dTotalRadian - dRelativeRadian;
}
// 根据相对弧度给出该点的绘制厚度
// 箭身部分
if (IsInRange(dRelativeRadian, 0, dTotalRadian - dHeadRadian))
{
return nArrowHeigth;
}
// 箭头部分
else if (IsInRange(dRelativeRadian, dTotalRadian - dHeadRadian, dTotalRadian))
{
return (int)(GetStandardRadian(dTotalRadian - dRelativeRadian) * ((double)nArrowHeadHeigth / dHeadRadian));
}
// 异常,所给点不在箭头范围内
else
{
return 0;
}
}
// 绘制箭头上某个点的图像
// p_nX 该点 x 坐标
// 需要此参数是因为在绘制时如果使用 p_dCurrentRadian 计算绘制使用的 x 坐标
// 则需要在 sin 值上乘以轨迹圆半径,可能导致所得 x 坐标过于离散而不连续从而产生绘制缺口
void DrawArrow_Point(double p_dARadian, double p_dBRadian, double p_dCurrentRadian, int p_nX, bool p_bIsLeftHanded)
{
// 该点相对于 A 点的弧度
double dRelativeRadian = p_dCurrentRadian - p_dARadian;
// 该点的 y 坐标
double dY = nR_arrow * sin(p_dCurrentRadian);
// 根据 y 坐标计算该点绘制时的绘制大小比例和亮度比例
// 某一点的 y 坐标为 0 时,绘制大小比例为 1,在视野尽头时为 0
// 某一点位于轨迹圆的正背面时,亮度比例为 0,在正前面时为 1
double dThicknessRatio = (double)(dY - nViewEndY) / abs(nViewEndY);
double dLightnessRatio = (double)(dY + nR_arrow) / (2 * nR_arrow);
// 获取该点在所定义图案上的理论绘制厚度
int nThickness = GetPatternThickness_Point(dRelativeRadian, p_bIsLeftHanded);
if (dThicknessRatio >= 2)
{
Sleep(1);
}
// 根据绘制大小比例计算实际的绘制厚度
int nThickness_real = (int)(nThickness * dThicknessRatio);
// 根据亮度比例计算实际的绘制亮度
COLORREF color = GREEN;
COLORREF color_real;
float h, s, l, l_real;
RGBtoHSL(color, &h, &s, &l);
l_real = (float)(l * dLightnessRatio);
color_real = HSLtoRGB(h, s, l_real);
// 绘制
setlinecolor(color_real);
line(nX0 + p_nX, nY0 - nThickness_real / 2, nX0 + p_nX, nY0 + nThickness_real / 2);
}
// 渲染箭头围绕圆的画面
// p_dRadian 箭头中心弧度数
// p_bIsLeftHanded 绘制的箭头方向是否左旋
void Render(double p_dRadian, bool p_bIsLeftHanded)
{
// 半箭头长对应的弧度
double dHalfLengthRadian = ((double)nArrowLength / 2) / nR_arrow;
// 计算箭头端点弧度,A 端对应小弧度,B 端对应大弧度
double dARadian = p_dRadian - dHalfLengthRadian;
double dBRadian = p_dRadian + dHalfLengthRadian;
// 可视区域对应的弧度(主球对箭头轨迹的遮挡产生了视野盲区)
double dVisibleSpotRadian_0 = acos((double)nR_ball / nR_arrow);
double dVisibleSpotRadian_Begin = -dVisibleSpotRadian_0;
double dVisibleSpotRadian_End = PI + dVisibleSpotRadian_0;
// 绘制主圆
setfillcolor(RGB(211, 211, 211));
solidcircle(nX0, nY0, nR_ball);
// 对箭头轨迹圆沿 x 轴进行扫描
for (int x = -nR_arrow; x <= nR_arrow; x++)
{
double dIntersectionRadian[2]; // 扫描线与箭头轨迹圆交点对应的弧度
// 两个交点对应的弧度,第一个在 [0, π] 内,第二个在 [π, 2π) 内
dIntersectionRadian[0] = acos((double)x / nR_arrow);
dIntersectionRadian[1] = GetStandardRadian(-dIntersectionRadian[0] + 2 * PI);
// 记录两个弧度是否位于 AB 弧度内且不在视野盲区
bool bValid[2];
for (int i = 0; i < 2; i++)
{
bValid[i] = IsInRange(dIntersectionRadian[i], dARadian, dBRadian)
&& IsInRange(dIntersectionRadian[i], dVisibleSpotRadian_Begin, dVisibleSpotRadian_End);
}
// 判断绘制顺序
// 数组中的值按顺序用 0, 1 代表第几步绘制哪个点,-1 表示不绘制
int pPaintOrder[2] = { -1, -1 };
// 两个点都需要绘制
if (bValid[0] && bValid[1])
{
// 确保先绘制更远的点
if (sin(dIntersectionRadian[0]) < sin(dIntersectionRadian[1]))
{
pPaintOrder[0] = 0;
pPaintOrder[1] = 1;
}
else
{
pPaintOrder[0] = 1;
pPaintOrder[1] = 0;
}
}
else if (bValid[0])
{
pPaintOrder[0] = 0;
}
else if (bValid[1])
{
pPaintOrder[0] = 1;
}
// 按顺序绘制各个点
for (int i = 0; i < 2; i++)
{
int nIndex = pPaintOrder[i];
if (nIndex != -1)
{
DrawArrow_Point(dARadian, dBRadian, dIntersectionRadian[nIndex], x, p_bIsLeftHanded);
}
}
}
}
int main()
{
initgraph(640, 480);
setbkcolor(LIGHTBLUE);
BeginBatchDraw();
// 使箭头的弧度逐帧变化,然后绘制
for (int i = 0;; i++)
{
double radian = PI * (double)i / 16.0;
if (radian >= 2 * PI)
i = 0;
cleardevice();
Render(radian, true);
FlushBatchDraw();
Sleep(50);
}
EndBatchDraw();
getmessage(EX_KEY);
return 0;
}


2025-08-08 11:09:17
广告
不感兴趣
开通SVIP免广告
  • 邹汇东
  • 二年级
    5
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼


  • 贴吧用户_a27WPU2
  • 托儿所
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
NB哥们


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 3回复贴,共1页
<<返回easyx吧
分享到:
©2025 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示