使用MFC的CDC类绘制三维坐标系及球面函数

2023-05-16

系列链接

  • 使用MFC的CDC类绘制二维坐标系及正余弦函数 / 源码

  • 使用MFC的CDC类绘制三维坐标系及球面函数 / 源码

概述

本文使用MFC的CDC类绘制三维坐标系及球面函数。首先计算推导出三维坐标在二维平面显示的坐标变换方程(使用斜二测视图),使用球面的参数方程,然后定义图形缩放比例规模、坐标轴位移,变换坐标系和规模等,最后绘制坐标轴及球面函数。

如果对绘制二维坐标系还不太熟悉可以先看上面系列链接的:使用MFC的CDC类绘制二维坐标系及正余弦函数,本文对二维绘制及绘制函数部分不再赘述。因为二维坐标系的博文已经分模块讲解地比较清楚了,而与三维坐标系的基本思路相同,所以本文大部分直接使用注释讲解。

三维转二维的推导

Transform3Dto2D

上图可知,只要使用Transform3Dto2D()函数,即可方便的把三维坐标转化为二维坐标(斜二测视图)。

球面参数方程

在三维空间直角坐标系中,以原点为球心、半径为 r 的球面的方程为 x^2 + y^2 + z^2 = r^2,其参数方程为

SphericalParameterEquation

新建项目

Visual Studio- 新建项目 - MFC应用程序 - 命名为GraphicsExercise3D - 确定 - 下一步 - 应用程序类型选择单个文档 - 完成

GraphicsExercise3DView.h

GraphicsExercise3DView.h添加以下内容

// 操作
public:
	void SetScale(int scale);
	void SetTransformOrigin(float transformOriginX, float transformOriginY);
	void SetPlotSphere(float radius, float stepPhi, float stepTheta);
  	void SetSlantRadian(float slant);

	float TransformScale(float num);
	float TransformOriginX(float x);
	float TransformOriginY(float y);
	float TransformOriginScaleX(float x);
	float TransformOriginScaleY(float y);
	void Transform3Dto2D(float &x, float &y, float z);

private:
    int scale;
    float radius, stepPhi, stepTheta, slant, transformOriginX, transformOriginY;

GraphicsExercise3DView.cpp

引入数学函数库

#include <math.h>

定义π

#ifndef PI
#define PI 3.14159
#endif // !PI

在构造函数初始化

CGraphicsExercise3DView::CGraphicsExercise3DView()
{
	// TODO: 在此处添加构造代码

	// 设置斜二测视图倾斜角度(弧度制)
	SetSlantRadian(PI / 4);

	// 设置规模比例
	SetScale(70);

	// 设置坐标系在x、y方向的位移(不改变规模情况下,即移动像素)
	SetTransformOrigin(300, 350);

	// 设置球面半径radius、取样步长stepPhi、stepTheta
	SetPlotSphere(2.0, 0.01, 0.1);
}

设置初始化参数的Set函数

// 设置规模
void CGraphicsExercise3DView::SetScale(int scale)
{
	this->scale = scale;
}

// 设置坐标系原点在x、y方向的位移(不改变规模情况下,即移动像素)
void CGraphicsExercise3DView::SetTransformOrigin(float transformOriginX, float transformOriginY)
{
	this->transformOriginX = transformOriginX;
	this->transformOriginY = transformOriginY;
}

// 设置球面半径radius、取样步长stepPhi、stepTheta
void CGraphicsExercise3DView::SetPlotSphere(float radius, float stepPhi, float stepTheta)
{
	this->radius = radius;
	this->stepPhi = stepPhi;
	this->stepTheta = stepTheta;
}

// 设置斜二测视图的倾斜角(单位弧度)
void CGraphicsExercise3DView::SetSlantRadian(float slant)
{
	this->slant = slant;
}

坐标及规模变换

// 变换规模
float CGraphicsExercise3DView::TransformScale(float num)
{
	return num * scale;
}

// 坐标系X轴方向位移
float CGraphicsExercise3DView::TransformOriginX(float x)
{
	return x + transformOriginX / scale;
}

// 坐标系y轴方向位移
float CGraphicsExercise3DView::TransformOriginY(float y)
{
	return y - transformOriginY / scale;
}

// 变换坐标系X和规模
float CGraphicsExercise3DView::TransformOriginScaleX(float x)
{
	return TransformScale(TransformOriginX(x));
}

// 变换坐标系Y和规模
float CGraphicsExercise3DView::TransformOriginScaleY(float y)
{
	return -TransformScale(TransformOriginY(y));
}

三维坐标转化为二维坐标

// 使用斜二测视图,把三维坐标点转化为二维平面上的点
void CGraphicsExercise3DView::Transform3Dto2D(float &x, float &y, float z)
{
	x = x - (z * cos(slant)) / 2;
	y = y - (z * sin(slant)) / 2;
}

绘制坐标轴及函数图形

// CGraphicsExercise2View 绘制

void CGraphicsExercise3DView::OnDraw(CDC* pDC)
{
	CGraphicsExercise3DDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码

	float x, y, z;

	// -------------------- 绘制坐标系 -------------------------

	// 坐标x轴
	pDC->MoveTo(TransformOriginScaleX(0), TransformOriginScaleY(0));
	pDC->LineTo(TransformOriginScaleX(radius + 2), TransformOriginScaleY(0));

	// 坐标y轴
	pDC->MoveTo(TransformOriginScaleX(0), TransformOriginScaleY(0));
	pDC->LineTo(TransformOriginScaleX(0), TransformOriginScaleY(radius + 2));

	// 坐标z轴
	x = 0, y = 0;
	Transform3Dto2D(x, y, radius + 5);
	pDC->MoveTo(TransformOriginScaleX(0), TransformOriginScaleY(0));
	pDC->LineTo(TransformOriginScaleX(x), TransformOriginScaleY(y));

	// 坐标x轴的箭头
	pDC->MoveTo(TransformOriginScaleX(radius + 1.8), TransformOriginScaleY(0.2));
	pDC->LineTo(TransformOriginScaleX(radius + 2), TransformOriginScaleY(0));
	pDC->LineTo(TransformOriginScaleX(radius + 1.8), TransformOriginScaleY(-0.2));

	// 坐标y轴的箭头
	pDC->MoveTo(TransformOriginScaleX(-0.2), TransformOriginScaleY(radius + 1.8));
	pDC->LineTo(TransformOriginScaleX(0), TransformOriginScaleY(radius + 2));
	pDC->LineTo(TransformOriginScaleX(0.2), TransformOriginScaleY(radius + 1.8));

	// 坐标z轴的箭头
	x = 0, y = 0.2;
	Transform3Dto2D(x, y, radius + 5 - 0.2);
	pDC->MoveTo(TransformOriginScaleX(x), TransformOriginScaleY(y));
	x = 0, y = 0;
	Transform3Dto2D(x, y, radius + 5);
	pDC->LineTo(TransformOriginScaleX(x), TransformOriginScaleY(y));
	x = 0.2, y = 0;
	Transform3Dto2D(x, y, radius + 5 - 0.2);
	pDC->LineTo(TransformOriginScaleX(x), TransformOriginScaleY(y));

	// -------------------- 绘制刻度线 -------------------------

	// 绘制x轴刻度线
	for (float scaleX = 0.2; scaleX < radius + 1; scaleX += 0.2)
	{
		pDC->MoveTo((int)TransformOriginScaleX(scaleX), (int)TransformOriginScaleY(0));
		pDC->LineTo((int)TransformOriginScaleX(scaleX), (int)TransformOriginScaleY(0.1));
	}

	// 绘制y轴刻度线
	for (float scaleY = 0.2; scaleY <= radius + 1; scaleY += 0.2)
	{
		pDC->MoveTo((int)TransformOriginScaleX(0), (int)TransformOriginScaleY(scaleY));
		pDC->LineTo((int)TransformOriginScaleX(0.1), (int)TransformOriginScaleY(scaleY));
	}

	// 绘制z轴刻度线
	for (float x = 0, y = 0, scaleZ = 0.2; scaleZ <= radius + 4; scaleZ += 0.2, x = 0, y = 0)
	{
		Transform3Dto2D(x, y, scaleZ);
		pDC->MoveTo((int)TransformOriginScaleX(x), (int)TransformOriginScaleY(y));
		pDC->LineTo((int)TransformOriginScaleX(x + 0.1), (int)TransformOriginScaleY(y));
	}


	// -------------------- 绘制文字 -------------------------

	// 绘制x轴的x
	pDC->TextOutW(TransformOriginScaleX(radius + 1.6), TransformOriginScaleY(-0.2), CString("x"));
	// 绘制y轴的y
	pDC->TextOutW(TransformOriginScaleX(-0.2), TransformOriginScaleY(radius + 1.6), CString("y"));
	// 绘制z轴的z
	x = 0.2, y = 0;
	Transform3Dto2D(x, y, radius + 5 - 0.4);
	pDC->TextOutW(TransformOriginScaleX(x), TransformOriginScaleY(y), CString("z"));

	CString s;
	// 绘制x轴刻度文字
	for (float ScaleTextX = 0.4; ScaleTextX < radius + 1; ScaleTextX += 0.4)
	{
		s.Format(_T("%.1f"), ScaleTextX);
		pDC->TextOutW(TransformOriginScaleX(ScaleTextX - 0.1), TransformOriginScaleY(-0.1), s);
	}

	// 绘制y轴刻度文字
	for (float ScaleTextY = 0.4; ScaleTextY <= radius + 1; ScaleTextY += 0.4)
	{
		s.Format(_T("%.1f"), ScaleTextY);
		pDC->TextOutW(TransformOriginScaleX(-0.4), TransformOriginScaleY(ScaleTextY + 0.1), s);
	}

	// 绘制z轴刻度文字
	for (float ScaleTextZ = 0.6; ScaleTextZ <= radius + 4; ScaleTextZ += 0.6)
	{
		s.Format(_T("%.1f"), ScaleTextZ);
		x = 0, y = 0;
		Transform3Dto2D(x, y, ScaleTextZ);
		pDC->TextOutW(TransformOriginScaleX(x + 0.15), TransformOriginScaleY(y + 0.12), s);
	}

	// 绘制函数图的Title
	x = 0, y = 0;
	Transform3Dto2D(x, y, radius + 5);
	pDC->TextOutW(TransformOriginScaleX(x + 3), TransformOriginScaleY(y), CString("x^2 + y^2 + z^2 = r^2"));

	// -------------------- 绘制函数 -------------------------

	// 球面
	float phi, theta;
	for (phi = 0; phi < 2 * PI; phi += stepPhi)
	{
		for (theta = 0; theta < PI; theta += stepTheta)
		{
			x = radius * sin(phi) * cos(theta);
			y = radius * sin(phi) * sin(theta);
			z = radius * cos(phi);

			Transform3Dto2D(x, y, z);

			srand(z);
			pDC->SetPixel(TransformOriginScaleX(x), TransformOriginScaleY(y), RGB(rand() % 255, rand() % 255, rand() % 255));
		}
	}


	 三棱锥(测试用)
	//x = 1, y = 0, z = 0;
	//Transform3Dto2D(x, y, z);
	//pDC->MoveTo((int)TransformOriginScaleX(x), (int)TransformOriginScaleY(y));

	//x = 0, y = 1, z = 0;
	//Transform3Dto2D(x, y, z);
	//pDC->LineTo((int)TransformOriginScaleX(x), (int)TransformOriginScaleY(y));

	//x = 0, y = 0, z = 1;
	//Transform3Dto2D(x, y, z);
	//pDC->LineTo((int)TransformOriginScaleX(x), (int)TransformOriginScaleY(y));

	//x = 1, y = 0, z = 0;
	//Transform3Dto2D(x, y, z);
	//pDC->LineTo((int)TransformOriginScaleX(x), (int)TransformOriginScaleY(y));

}

效果图

GraphicsExercise3DCapture

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用MFC的CDC类绘制三维坐标系及球面函数 的相关文章

  • 自定义注解,打造自己的框架-下篇

    2019 12 04 22 53 52 文章目录 结构声明注解声明注解处理器处理注解逻辑给使用者提供调用方法使用 该系列介绍自定义注解 xff0c 完成如下功能 64 BindView 代替 findViewById 64 ClickRes
  • 嵌入式STM32深入之RTOS编程

    RTOS编程 一 前言二 RTOS的概念 xff08 一 xff09 用人来类比单片机程序和RTOS1 1 我无法一心多用1 2 我可以一心多用 xff08 二 xff09 架构的概念 xff08 三 xff09 常见的嵌入式操作系统 xf
  • 坐标系变换

    slam 基础之机器人学中的坐标转换学习总结 rot z 90 ppipp1109的博客 CSDN博客 https www youtube com watch v 61 4Y1 y9DI Hw amp t 61 538s 1 平移和旋转 3
  • 如何安装inf文件

    方法1 运行RunDll32 advpack dll LaunchINFSection YOUINF inf DefaultInstall 方法2 修复右键安装 操作步骤如下 xff1a 打开我的电脑 xff0c 工具 菜单中的 文件夹选项
  • 无人机采集图像的相关知识

    1 xff0e 飞行任务规划 一般使用飞行任务规划软件进行飞行任务的设计 xff0c 软件可以自动计算相机覆盖和图像重叠情况 比如ArduPilot http ardupilot com 和UgCS http www ugcs com 是两
  • 无人机编程donekit及通讯(三)——仿真

    1 启动SITL 启动STL cd courseRoot apm ardupilot sim vehicle py v ArduCopter console map 飞机起飞降落 mode GUIDED arm throttle takeo
  • DroneKit(四)——无人机协同

    coding utf8 from dronekit import connect VehicleMode LocationGlobalRelative APIException import time import exceptions i
  • 看这篇就够了——opencv与libopencv与cv_bridge的安装与使用

    一 基本关系 opencv OpenCV的全称是Open Source Computer Vision Library xff0c 是一个跨平台的计算机视觉处理开源软件库 xff0c 是由Intel公司俄罗斯团队发起并参与和维护 xff0c
  • Linux下更改oracle客户端字符集和服务端字符集

    from http blog csdn net chid article details 6166506 Linux 下更改 oracle 客户端字符集和服务端字符集 1 Linux 下更改 oracle 客户端字符集 xff0c 即设置环
  • C语言书籍推荐

    C 语言书籍推荐 宗旨 xff1a 技术的学习是有限的 xff0c 分享的精神是无限的 一 基础 1 C语言入门很简单 零起点学通C语言 xff08 多媒体范例教学 xff09 C语言从入门到精通 2 C程序设计语言 第2版 新版 C程序设
  • Jetson配置realsense D435i SDK以及realsense-ros

    一 命令安装 SDK sudo apt install librealsense2 realsense viewer 测试 realsense ros sudo apt get install ros ROS DISTRO realsens
  • 工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)

    博主的合并代码 git 64 github com huashu996 VINS FUSION ESDFmap git 一 D435i深度相机配置 1 1 SDK 43 ROS 参考我之前的博客 xff0c 步骤和所遇见的问题已经写的很详细
  • 从零入门激光SLAM(八)——ROS常用消息

    大家好呀 xff0c 我是一个SLAM方向的在读博士 xff0c 深知SLAM学习过程一路走来的坎坷 xff0c 也十分感谢各位大佬的优质文章和源码 随着知识的越来越多 xff0c 越来越细 xff0c 我准备整理一个自己的激光SLAM学习
  • 嵌入式软件开发,快五年,没有成就感,快迷失自己了

    嵌入式软件开发 xff0c 快五年 xff0c 没有成就感 xff0c 快迷失自己了 看到了这个的问题 xff0c 其实这个问题何尝不是很多软件开发人员的困惑呢 至少我自己如此 在任何公司 xff0c 个人永远就是一枚螺丝钉的角色 当公司的
  • 从高校中走出的 Apache 顶级项目 —— IoTDB 核心成员黄向东

    本期访谈嘉宾 xff1a 黄向东 Apache 顶级开放源代码项目 IoTDB xff08 物联网数据库 xff09 核心成员 2021 年开源先锋 清华大学软件学院助理研究员 Q xff1a 简单介绍一下 Apache IoTDB IoT
  • pixhawk入门 -- 编译环境问题解决过程

    pixhawk 编译的基本操作步骤 xff1a 1 xff09 下载固件仓 git clone https github com PX4 Firmware git 2 xff09 更新固件代码 cd Firmware git submodu
  • 树莓派的供电问题

    树莓派99 的故障来自于5V电源 树莓派出现任何故障 xff08 死机 键盘和鼠标不工作 显示器变暗或失真 终端程序出现乱码 xff0c 无法开机 xff0c 运行的程序死机 xff0c usb设备无法识别或不工作 网络故障等 xff09
  • 树莓派的操作系统介绍

    1 Raspbian 单纯的Arm版的Linux系统 xff0c 基于Debian 是当前实用最广泛的操作系统 2 Pidora 单纯的Arm版的Linux系统 xff0c 基于Fedora 是拥有另一种风格的树莓派操作系统 3 Arch
  • gd32e103加入freertos的步骤

    gd32E103的芯片比较强大 xff0c m4的内核 xff0c 120mhz主频 xff0c 跑freertos是没有问题的 作为一个轻量级的操作系统 xff0c FreeRTOS提供的功能包括 xff1a 任务管理 时间管理 信号量
  • oracle数据库开启的时候 是先开监听还是先开主服务,关数据库的时候呢???...

    启动的时候无所谓先后 xff0c 关闭的话 1 首先是关闭监听 xff08 让远程客户端无法再连进来 xff09 xff1b 2 发出一个系统检查点 xff0c 让数据文件和控制文件的系统修改号统一 xff1b xff08 alter sy

随机推荐

  • Docker本地images删除问题

    背景 测试环境和生产环境 部署了不同的registry服务 xff0c 通过cli 操作过生产环境或测试环境push xff0f pull image功能 本地虚拟机 xff0c docker image 残留了很多image xff0c
  • Aruco的使用+opencv+opencv_contrib+cmake

    Aruco的使用 By GuangyeHu 1 下载 xff1a https sourceforge net projects aruco files 解压到相应的文件夹 2 安装Cmake 本次实现使用的是cmake 3 15 1 win
  • Nginx 配置多个Vue项目

    背景 xff1a 公司要求将之前用不同端口号区分的三个独立的系统统一配置到统一域名下 VUE项目配置 示例 xff1a 有两个项目 第一个 xff1a 打包到 96 test1 96 目录中 xff1b 第二个 xff1a 打包到 96 t
  • AD随堂笔记

    算起来 xff0c 这次算是第四次打板子了 xff0c 还是出现了不可饶恕的错误 串口三的TXD RXD接反了 xff0c 还有一根线是短路的 造成的原因 xff0c 就是没有进行规则检查 因为这个板子是在原来基础上面改的 xff0c 大意
  • 学习Linux必备书籍推荐

    xfeff xfeff 鸟哥的私房菜 xff08 第3版 xff09 xff0c 鸟哥著 xff1a 这本书内容丰富全面 xff0c 基本概念的讲解非常细致 xff0c 深入浅出 xff0c 各种功能和命令的介绍都配以大量的实例操作和详尽的
  • 无人机目标检测 Darknet-ROS 学习(一)

    无人机目标检测 Darknet ROS 学习 xff08 一 xff09 简介 在无人机上实现目标检测 xff0c 使用的设备是阿木实验室P200 xff0c 飞控是PX4 xff0c 板载计算机是TX2 xff0c 目的是在无人机有限的计
  • Android - Service

    前台20s后台200s不执行玩就报ANR异常 一 概念 没有界面在后台长期运行在主线程中的一个组件 ServiceThread可以配置执行在不同的进程中 CPU调度的最小单位 任何有Context的地方都可以控制Service当Activi
  • 2022年打工人转行实录!你后悔转行了吗?

    2022年 xff0c 有许多年轻人在寻求 转行 根据 2022Q1中高端人才就业趋势大数据报告 xff0c 今年一季度有55 87 的职场人有跳槽计划 xff0c 而这之中 xff0c 有65 34 的职场人选择跨行业跳槽 在各种社交平台
  • 2.PendSV的触发

    PendSV典型使用场合是在上下文切换时 xff08 在不同任务之间切换 xff09 我们先简单的写几段代码实现PendSV的中断触发 xff0c 当然也会涉及到CM3内核汇编指令 xff0c 自从开始挑战的那天起 xff0c 你不如地狱谁
  • C语言: 字符串结束符 ‘ \0 ’

    1 本质 39 0 39 是字符串结束标志 xff0c 不计入串长 xff0c 但要占内存空间 39 0 39 是一个ASCII控制字符 xff0c 是转义字符 意思是告诉编译器 xff0c 这不是字符0 xff0c 而是空字符 空字符 0
  • Docker中容器的备份、恢复和迁移

    转自 xff1a http www linuxidc com Linux 2015 08 121184 htm 1 备份容器 首先 xff0c 为了备份Docker中的容器 xff0c 我们会想看看我们想要备份的容器列表 要达成该目的 xf
  • Ubuntu 安装 clang++

    clang 安装
  • 对vector使用指针

    include lt stdio h gt include lt iostream gt include lt vector gt using namespace std int main vector lt int gt a b c fo
  • 单片机初学者电路常识

    电路常识性概念 xff08 1 xff09 输入 输出阻抗 1 输入阻抗 输入阻抗是指一个电路输入端的等效阻抗 在输入端上加上一个电压源U xff0c 测量输入端的电流I xff0c 则输入阻抗Rin 61 U I 你可以把输入端想象成一个
  • 使用 FreeRTOS 时使用 GPIO 监控 CPU 负载的正确方法?

    总目录链接 61 61 gt gt AutoSAR入门和实战系列总目录 总目录链接 61 61 gt gt AutoSAR BSW高阶配置系列总目录 文章目录 我想切换一些 GPIO 以监控 CPU 活动和 FreeRTOS 上下文 更具体
  • iTerm2 + OhMyZsh + agnoster + Powerline + solarized = 漂亮的Mac终端

    唠叨一下 自从装了黑苹果后一直用着 Mac 自带的终端 xff08 Terminal xff09 xff0c 相比 Windows 的终端根本无法同台竞技 xff01 毕竟 Mac 是基于 Unix 嘛 对开发友好太多了 就是下面这个家伙了
  • CloudCompare功能概要

    File open xff1a 打开save xff1a 保存Global Shift settings xff1a 设置最大绝对坐标 xff0c 最大实体对角线Primitive Factory xff1a 对点云进行原始加工 xff0c
  • 机会总是留给有准备的人 —— 从裁缝到码农

    序言 2014年8月 xff0c 一个男生拿着广工服装工程专业的录取通知书来到东风路 xff0c 望着学校大门 xff0c 想着 xff1a 这就是我的大学 xff1f 2014年11月 xff0c 一个男生一个女生望着广工大学城的大门 x
  • Launcher 启动 UE4 引擎出现 An Unreal process has crashed 的问题

    Launcher 启动 UE4 引擎出现 An Unreal process has crashed 的问题 问题描述 在 Launcher 启动 UE4 引擎 xff0c 引擎奔溃 xff0c 出现奔溃报告 xff1a An Unreal
  • 使用MFC的CDC类绘制三维坐标系及球面函数

    系列链接 使用MFC的CDC类绘制二维坐标系及正余弦函数 源码 使用MFC的CDC类绘制三维坐标系及球面函数 源码 概述 本文使用MFC的CDC类绘制三维坐标系及球面函数 首先计算推导出三维坐标在二维平面显示的坐标变换方程 xff08 使用