北京网站建设+知乎,导航网站html模板,电脑软件培训班计算机培训班,微信 公众号平台文章目录一、问题描述二、解题思路 在做项目时遇到了一个数学问题#xff0c;即#xff0c;如何判断给定一个三角面片与空间中某个球体有相交部分#xff1f;这个问题看似简单#xff0c;实际处理起来需要一些方法和手段。一、问题描述 已知空间中球体的球心位置center即如何判断给定一个三角面片与空间中某个球体有相交部分这个问题看似简单实际处理起来需要一些方法和手段。一、问题描述 已知空间中球体的球心位置center半径为r三角形三个顶点分别为v1v2v3。判断该三角形与球体是否有交点 二、解题思路
通过写写画画我们可以大致将球体和三角形的位置分为以下三种情况
一三角形三个顶点至少有一个在球体内部 这种情况相对来说比较简单只需要判断三角形三个顶点到球心的最小距离半径 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/// 线段向量/// /summarypublic Vector3 vector end - start;/// summary/// 单位方向/// /summarypublic Vector3 direction vector.normalized;/// summary/// 线段长度/// /summarypublic float length vector.magnitude;public Segment(Vector3 start, Vector3 end) {this.start start;this.end end;}/// summary/// 计算点到线段所在直线的距离/// /summary/// param namepoint点/param/// returns/returnspublic float LineDistance(Vector3 point) {float theta Vector3.Angle(point - start, vector);return (point - start).magnitude * Mathf.Sin(Mathf.Deg2Rad * theta);}/// summary/// 线段是否穿过球体/// /summary/// param namecenter球心/param/// param nameradius半径/param/// returns/returnspublic 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 namep1点 1/param/// param namep2点 2/param/// returns/returnspublic 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/// /summarypublic Segment l1 new Segment(v1, v2);/// summary/// 线段 23/// /summarypublic Segment l2 new Segment(v2, v3);/// summary/// 线段 31/// /summarypublic Segment l3 new Segment(v3, v1);/// summary/// 所在平面/// /summarypublic Plane plane new Plane(v1, v2, v3);public Triangle(Vector3 v1, Vector3 v2, Vector3 v3) {SetInfo(v1, v2, v3);}/// summary/// 设置三角形位置/// /summary/// param namev1顶点 1/param/// param namev2顶点 2/param/// param namev3顶点 3/parampublic void SetInfo(Vector3 v1, Vector3 v2, Vector3 v3) {this.v1 v1;this.v2 v2;this.v3 v3;}/// summary/// 顶点到点 point 的最小距离/// /summary/// param namepoint点 point/param/// returns/returnspublic 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 namecenter投影中心/param/// returns平面三角形/returnspublic 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 namecenter球心/param/// param nameradius半径/param/// returns/returnspublic 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/// 线段向量/// /summarypublic Vector2 vector end - start;/// summary/// 单位方向/// /summarypublic Vector2 direction vector.normalized;public float length vector.magnitude;/// summary/// 线段长度/// /summarypublic Segment2D(Vector2 start, Vector2 end) {this.start start;this.end end;}/// summary/// 判断两点是否处于线段同一侧/// /summary/// param namep1点 1/param/// param namep2点 2/param/// returns/returnspublic 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/// /summarypublic Segment2D l1 new Segment2D(v1, v2);/// summary/// 线段 23/// /summarypublic Segment2D l2 new Segment2D(v2, v3);/// summary/// 线段 31/// /summarypublic Segment2D l3 new Segment2D(v3, v1);public Triangle2D(Vector2 v1, Vector2 v2, Vector2 v3) {SetInfo(v1, v2, v3);}/// summary/// 设置三角形位置/// /summary/// param namev1顶点 1/param/// param namev2顶点 2/param/// param namev3顶点 3/parampublic void SetInfo(Vector2 v1, Vector2 v2, Vector2 v3) {this.v1 v1;this.v2 v2;this.v3 v3;}/// summary/// 判断点 point 是否在三角形内部/// /summary/// param namepoint点/param/// returns/returnspublic bool Contains(Vector2 point) {return l1.SameSide(point, v3) l2.SameSide(point, v1) l3.SameSide(point, v2);}
}