判断三角面片与空间中球体是否相交
文章目录
- 一、问题描述
- 二、解题思路
在做项目时遇到了一个数学问题,即,如何判断给定一个三角面片与空间中某个球体有相交部分?这个问题看似简单,实际处理起来需要一些方法和手段。
一、问题描述
已知空间中球体的球心位置center,半径为r,三角形三个顶点分别为v1,v2,v3。判断该三角形与球体是否有交点?
二、解题思路
通过写写画画,我们可以大致将球体和三角形的位置分为以下三种情况:
(一)三角形三个顶点至少有一个在球体内部
这种情况相对来说比较简单,只需要判断三角形三个顶点到球心的最小距离<半径 r 即可。
(二)三个顶点都在球体外部,但至少有一条线段与球体相交
计算三边所在直线到球心的距离,最短距离 < r 即可。
但是同时需要确保 ∠OAC\angle OAC∠OAC 与 ∠OCA\angle OCA∠OCA 均< ∠ODA\angle ODA∠ODA,否则会出现以下错误的情况:
根本原因是,我们求的是线段与球体相交,而不是一条直线。
(三)三个顶点、三条边都在球体外部,但平面与球体相交
同理,计算球心到平面的距离 < r 即可。
一样的,需要避免以下的情况,原因一样,我们求的是三角形与球体是否有交点,而不是平面:
判断方式:作球心 O 到平面 ABC 的投影 P,看 P 是否在三角形内部:
-
方法一: 面积法。算出三角形 ABP、BCP、CAP 的面积和,与 ABC 面积进行比较。如果相同,则 P 在内部
-
方法二: 矢量法。若 P 在三角形内部,则:
- 对 BC 而言,P、A 在同一侧
- 对 CA 而言,P、B 在同一侧
- 对 AB 而言,P、C 在同一侧
可以用矢量的叉乘判断两点是否在线段的同一侧。
即,对于 BC 而言,BP→×BC→\overrightarrow{BP}\times\overrightarrow{BC}BP×BC 与 BP→×BA→\overrightarrow{BP}\times\overrightarrow{BA}BP×BA 同向,则 P、A 在同一侧。
实现代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Segment
{public Vector3 start; // 起始位置public Vector3 end; // 终点位置/// <summary>/// 线段向量/// </summary>public Vector3 vector => end - start;/// <summary>/// 单位方向/// </summary>public Vector3 direction => vector.normalized;/// <summary>/// 线段长度/// </summary>public float length => vector.magnitude;public Segment(Vector3 start, Vector3 end) {this.start = start;this.end = end;}/// <summary>/// 计算点到线段所在直线的距离/// </summary>/// <param name="point">点</param>/// <returns></returns>public float LineDistance(Vector3 point) {float theta = Vector3.Angle(point - start, vector);return (point - start).magnitude * Mathf.Sin(Mathf.Deg2Rad * theta);}/// <summary>/// 线段是否穿过球体/// </summary>/// <param name="center">球心</param>/// <param name="radius">半径</param>/// <returns></returns>public bool CrossSphere(Vector3 center, float radius) {// 如果线段两端点其中一个在球体内部,直接返回 trueif (Vector3.Distance(center, start) < radius || Vector3.Distance(center, end) < radius) return true;// 球心到线段距离大于半径,则返回 falsefloat d = LineDistance(center);if (d >= radius) return false;// 计算球心与线段的两个夹角,以判断线段是否穿过球体float theta = Mathf.Asin(d / radius) * Mathf.Rad2Deg;float a1 = Vector3.Angle(center - start, vector);float a2 = Vector3.Angle(center - end, -vector);return a1 <= theta && a2 <= theta;}/// <summary>/// 判断两点是否处于线段同一侧/// </summary>/// <param name="p1">点 1</param>/// <param name="p2">点 2</param>/// <returns></returns>public bool SameSide(Vector3 p1, Vector3 p2) {Vector3 v1 = Vector3.Cross(p1 - start, vector);Vector3 v2 = Vector3.Cross(p2 - start, vector);return Vector3.Dot(v1, v2) >= 0;}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 三角形
/// </summary>
public class Triangle
{public Vector3 v1; // 顶点 1public Vector3 v2; // 顶点 2public Vector3 v3; // 顶点 3/// <summary>/// 线段 12/// </summary>public Segment l1 => new Segment(v1, v2);/// <summary>/// 线段 23/// </summary>public Segment l2 => new Segment(v2, v3);/// <summary>/// 线段 31/// </summary>public Segment l3 => new Segment(v3, v1);/// <summary>/// 所在平面/// </summary>public Plane plane => new Plane(v1, v2, v3);public Triangle(Vector3 v1, Vector3 v2, Vector3 v3) {SetInfo(v1, v2, v3);}/// <summary>/// 设置三角形位置/// </summary>/// <param name="v1">顶点 1</param>/// <param name="v2">顶点 2</param>/// <param name="v3">顶点 3</param>public void SetInfo(Vector3 v1, Vector3 v2, Vector3 v3) {this.v1 = v1;this.v2 = v2;this.v3 = v3;}/// <summary>/// 顶点到点 point 的最小距离/// </summary>/// <param name="point">点 point</param>/// <returns></returns>public float MinPointDistance(Vector3 point) {float d1 = Vector3.Distance(v1, point);float d2 = Vector3.Distance(v2, point);float d3 = Vector3.Distance(v3, point);return Mathf.Min(d1, d2, d3);}/// <summary>/// 投影转化为平面三角形(大小不变)/// </summary>/// <param name="center">投影中心</param>/// <returns>平面三角形</returns>public Triangle2D CastTriangle2D(Vector3 center) {Vector3 origin = plane.ClosestPointOnPlane(center);Vector3 vector1 = v1 - origin;Vector3 vector2 = v2 - origin;Vector3 vector3 = v3 - origin;float a12 = Vector3.SignedAngle(vector1, vector2, plane.normal) * Mathf.Deg2Rad;float a13 = Vector3.SignedAngle(vector1, vector3, plane.normal) * Mathf.Deg2Rad;Vector2 p1 = new Vector2(vector1.magnitude, 0);Vector2 p2 = new Vector2(vector2.magnitude * Mathf.Cos(a12), vector2.magnitude * Mathf.Sin(a12));Vector2 p3 = new Vector2(vector3.magnitude * Mathf.Cos(a13), vector3.magnitude * Mathf.Sin(a13));return new Triangle2D(p1, p2, p3);}/// <summary>/// 判断三角形是否穿过球体/// </summary>/// <param name="center">球心</param>/// <param name="radius">半径</param>/// <returns></returns>public bool CrossSphere(Vector3 center, float radius) {// 如果最近的点在球体内部则返回 tureif (MinPointDistance(center) < radius) return true;// 如果有线段穿过了球体,则返回 trueif (l1.CrossSphere(center, radius) || l2.CrossSphere(center, radius) || l3.CrossSphere(center, radius)) return true;// 否则投影球心到三角平面上,看其是否在三角形内float d = Mathf.Abs(plane.GetDistanceToPoint(center));if (d < radius) { // 球心到平面的距离小于半径才可能相交Triangle2D t2D = CastTriangle2D(center); // 进行投影return t2D.Contains(Vector2.zero); // 判断}return false;}
}
using System;
using UnityEngine;/// <summary>
/// 平面线段
/// </summary>
public class Segment2D
{public Vector2 start; // 起始位置public Vector2 end; // 终点位置/// <summary>/// 线段向量/// </summary>public Vector2 vector => end - start;/// <summary>/// 单位方向/// </summary>public Vector2 direction => vector.normalized;public float length => vector.magnitude;/// <summary>/// 线段长度/// </summary>public Segment2D(Vector2 start, Vector2 end) {this.start = start;this.end = end;}/// <summary>/// 判断两点是否处于线段同一侧/// </summary>/// <param name="p1">点 1</param>/// <param name="p2">点 2</param>/// <returns></returns>public bool SameSide(Vector2 p1, Vector2 p2) {Vector3 v1 = Vector3.Cross(p1 - start, vector);Vector3 v2 = Vector3.Cross(p2 - start, vector);return Vector3.Dot(v1, v2) >= 0;}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Triangle2D
{public Vector2 v1; // 顶点 1public Vector2 v2; // 顶点 2public Vector2 v3; // 顶点 3/// <summary>/// 线段 12/// </summary>public Segment2D l1 => new Segment2D(v1, v2);/// <summary>/// 线段 23/// </summary>public Segment2D l2 => new Segment2D(v2, v3);/// <summary>/// 线段 31/// </summary>public Segment2D l3 => new Segment2D(v3, v1);public Triangle2D(Vector2 v1, Vector2 v2, Vector2 v3) {SetInfo(v1, v2, v3);}/// <summary>/// 设置三角形位置/// </summary>/// <param name="v1">顶点 1</param>/// <param name="v2">顶点 2</param>/// <param name="v3">顶点 3</param>public void SetInfo(Vector2 v1, Vector2 v2, Vector2 v3) {this.v1 = v1;this.v2 = v2;this.v3 = v3;}/// <summary>/// 判断点 point 是否在三角形内部/// </summary>/// <param name="point">点</param>/// <returns></returns>public bool Contains(Vector2 point) {return l1.SameSide(point, v3) && l2.SameSide(point, v1) && l3.SameSide(point, v2);}
}
相关文章:
判断三角面片与空间中球体是否相交
文章目录一、问题描述二、解题思路 在做项目时遇到了一个数学问题,即,如何判断给定一个三角面片与空间中某个球体有相交部分?这个问题看似简单,实际处理起来需要一些方法和手段。一、问题描述 已知空间中球体的球心位置center&a…...
继承下的缺省参数值和访问说明符
前言 本文将介绍 C 继承体系下,函数缺省参数的绑定和函数访问说明符的绑定。这些奇怪的问题实际上不应在我们的代码中出现,但它们能帮助我们理解 C 的动态绑定和静态绑定,也能帮助我们更好的通过面试。 缺省参数值 先来看一段代码…...
Spring核心模块—— BeanFactoryPostProcessorBeanPostProcessor(后处理器)
后置处理器前言Spring的后处理器BeanFactoryPostProcessor(工厂后处理器)执行节点作用基本信息经典场景子接口——BeanDefinitiRegistryPostProcessor基本介绍用途具体原理例子——注册BeanDefinition使用Spring的BeanFactoryPostProcessor扩展点完成自定…...
产品新人如何培养产品思维?
什么是产品思维?其实很难定义,不同人有不同的定义。有的人定义为以用户为中心打磨一个完美体验的产品;有的定义为从需求调研到需求上线各个步骤需要思考的点,等等。本文想讨论的产品思维是:怎么去发现问题,…...
「兔了个兔」CSS如此之美,看我如何实现可爱兔兔LOADING页面(万字详解附源码)
💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后…...
【Java】阻塞队列 BlcokingQueue 原理、与等待唤醒机制condition/await/singal的关系、多线程安全总结
在实习过程中使用阻塞队列对while sleep 轮询机制进行了改造,提升了发送接收的效率,这里做一点点总结。 自从Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列,BlcokingQueue继承了Queue接口,是线程安全…...
【水下图像增强】Enhancing Underwater Imagery using Generative Adversarial Networks
原始题目Enhancing Underwater Imagery using Generative Adversarial Networks中文名称使用 GAN 增强水下图像发表时间2018年1月11日平台ICRA 2018来源University of Minnesota, Minneapolis MN文章链接https://arxiv.org/abs/1801.04011开源代码官方:https://gith…...
Maven专题总结—详细版
第一章 为什么使用Maven 获取jar包 使用Maven之前,自行在网络中下载jar包,效率较低。如【谷歌、百度、CSDN…】使用Maven之后,统一在一个地址下载资源jar包【阿里云镜像服务器等…】 添加jar包 使用Maven之前,将jar复制到项目工程…...
华为OD机试真题Java实现【字符串加密】真题+解题思路+代码(20222023)
字符串加密 题目 给你一串未加密的字符串str, 通过对字符串的每一个字母进行改变来实现加密, 加密方式是在每一个字母str[i]偏移特定数组元素a[i]的量, 数组a前三位已经赋值:a[0]=1,a[1]=2,a[2]=4。 当i>=3时,数组元素a[i]=a[i-1]+a[i-2]+a[i-3], 例如:原文 abcde …...
「Python 基础」函数与高阶函数
文章目录1. 函数调用函数定义函数函数的参数递归函数2. 高阶函数map/reducefiltersorted3. 函数式编程返回函数匿名函数装饰器偏函数1. 函数 函数是一种重复代码的抽象方式,Python 内建支持的一种封装; 调用函数 调用一个函数,需要知道函数…...
DIV内容滚动,文字符滚动标签marquee兼容稳定不卡
marquee(文字滚动)标签 marquee简介 <marquee>标签,是成对出现的标签,首标签<marquee>和尾标签</marquee>之间的内容就是滚动内容。 <marquee>标签的属性主要有behavior、bgcolor、direction、width、height、hspace、vspace、loop、scrollamount、scr…...
SpringBoot_第五章(Web和原理分析)
目录 1:静态资源 1.1:静态资源访问 1.2:静态资源源码解析-到WebMvcAutoConfiguration 2:Rest请求绑定(设置put和delete) 2.1:代码实例 2.2:源码分析到-WebMvcAutoConfiguratio…...
4-2 Linux进程和内存概念
文章目录前言进程状态进程优先级内存模型进程内存关系前言 进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源。一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝。 进程状态…...
【微信小程序】计算器案例
🏆今日学习目标:第二十一期——计算器案例 ✨个人主页:颜颜yan_的个人主页 ⏰预计时间:30分钟 🎉专栏系列:我的第一个微信小程序 计算器前言实现效果实现步骤wxmlwxssjs数字按钮事件处理函数计算按钮处理事…...
408 计算机基础复试笔记 —— 更新中
计算机组成原理 计算机系统概述 问题一、冯诺依曼机基本思想 存储程序:程序和数据都存储在同一个内存中,计算机可以根据指令集执行存储在内存中的程序。这使得程序具有高度灵活性和可重用性。指令流水线:将指令分成若干阶段,每…...
找出最大数-课后程序(Python程序开发案例教程-黑马程序员编著-第二章-课后作业)
实例6:找出最大数 “脑力大乱斗”休闲益智游戏的关卡中,有一个题目是找出最大数。本实例要求编写程序,实现从输入的任意三个数中找出最大数的功能。 实例分析 对于3个数比较大小,我们可以首先先对两个数的大小进行比较ÿ…...
Java——N叉树的层序遍历
题目链接 leetcode在线oj题——N叉树的层序遍历 题目描述 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例&…...
【Kubernetes】第十八篇 - k8s 服务发现简介
一,前言 上一篇,介绍了阿里云 ECS 服务器重启后的环境修复; 本篇,介绍 k8s 服务发现; 二,服务发现简介 当 A服务依赖了 B服务,而 B服务的IP和端口未知(或相对不固定)&…...
Codeforces Round 856 (Div. 2) 最好ak的div2
最近几场的div2 E都是一个思路啊,代码大差不差的,感觉随便ak啊。 A. Prefix and Suffix Array 题意 给你前n−1n-1n−1个字符串前缀和后n−1n-1n−1个字符串后缀,判断原字符串是否是回文串 思路 相同长度的判断是否是对称的即可。 代码 B C…...
最新JVM技术: GraalVM,让你一文了解它的方方面面
1. 什么是GraalVM? GraalVM是一种开源的虚拟机平台,由Oracle公司开发。它支持多种编程语言,包括Java、JavaScript、Python、Ruby、R、C++等,旨在提高应用程序的性能和扩展性。 GraalVM通过提供即时编译器(Just-in-Time Compiler,JIT)和Ahead-of-Time(AOT)编译器来提…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
