Files
OpticsSimulation.Unity/Assets/Scenes/PlanoConvexLens.cs
T
2025-05-13 01:04:37 +03:00

163 lines
5.3 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class PlanoConvexLens : OpticalSurface2D
{
public float LensRadius = 1.0f;
public float CurveRadius = 1.0f;
public int SamplesCount = 10;
public float Height = 0.1f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private RaycastMath.RaycastResult2D[] _raycasts;
public override bool Raycast(Ray2D ray, ref RaycastMath.RaycastResult2D result) {
if(_raycasts == null) {
_raycasts = new RaycastMath.RaycastResult2D[4];
}
var localSpaceRay = ray;
localSpaceRay.origin = transform.InverseTransformPoint(localSpaceRay.origin);
localSpaceRay.direction = transform.InverseTransformVector(localSpaceRay.direction).normalized;
//cs = as + bs
//bs = cs - as
float h = Mathf.Sqrt(CurveRadius * CurveRadius - LensRadius * LensRadius);
var sphereCastRay = localSpaceRay;
sphereCastRay.origin += Vector2.up * (Mathf.Sign(CurveRadius) * h - Height);
int raycastsCount = 0;
bool curveIntersected = RaycastMath.RaycastCircle(sphereCastRay, Mathf.Abs( CurveRadius), ref _raycasts[raycastsCount]);
if (curveIntersected) {
_raycasts[raycastsCount].Point += Vector2.down * Mathf.Sign(CurveRadius) * (h);
if(CurveRadius >= 0) {
if (_raycasts[raycastsCount].Point.y < 0) {
curveIntersected = false;
} else {
_raycasts[raycastsCount].Point += Vector2.up * Height;
raycastsCount++;
}
} else {
if (_raycasts[raycastsCount].Point.y > 0) {
curveIntersected = false;
} else {
_raycasts[raycastsCount].Point += Vector2.up * Height;
_raycasts[raycastsCount].Normal = -_raycasts[raycastsCount].Normal;
raycastsCount++;
}
}
}
Vector2 leftHitPoint = Vector2.zero;
if(Height > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(-LensRadius, 0), new Vector2(-LensRadius, Height), ref leftHitPoint)) {
_raycasts[raycastsCount].Normal = Vector2.left;
_raycasts[raycastsCount].Point = leftHitPoint;
raycastsCount++;
}
Vector2 rightHitPoint = Vector2.zero;
if(Height > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(LensRadius, 0), new Vector2(LensRadius, Height), ref rightHitPoint)) {
_raycasts[raycastsCount].Normal = Vector2.right;
_raycasts[raycastsCount].Point = rightHitPoint;
raycastsCount++;
}
Vector2 bottomHitPoint = Vector2.zero;
if(LensRadius > 0.0f && RaycastMath.RayLineSegmentIntersection(localSpaceRay.origin, localSpaceRay.direction, new Vector2(-LensRadius, 0), new Vector2(LensRadius, 0), ref bottomHitPoint)) {
_raycasts[raycastsCount].Normal = Vector2.down;
_raycasts[raycastsCount].Point = bottomHitPoint;
raycastsCount++;
}
if(raycastsCount == 0) {
return false;
}
result = _raycasts.Take(raycastsCount).OrderBy(x => Vector2.Distance(x.Point, localSpaceRay.origin)).First();
result.Point = transform.TransformPoint(result.Point);
result.Normal = transform.InverseTransformVector(result.Normal).normalized;
return true;
}
private void OnValidate() {
if(CurveRadius > 0 && CurveRadius < LensRadius) {
CurveRadius = LensRadius;
}
if (CurveRadius < 0 && CurveRadius > -LensRadius) {
CurveRadius = -LensRadius;
}
var points = RadialCurve(LensRadius, CurveRadius, SamplesCount);
if(points.Length < 2) {
return;
}
Shape = new Line[points.Length - 1 + 3];
for(int i = 0; i < points.Length - 1; i++) {
Shape[i] = new Line() { P1 = points[i] + Vector2.up * Height, P2 = points[i + 1] + Vector2.up * Height };
}
Vector2 p1 = new Vector2(-LensRadius, Height);
Vector2 p2 = new Vector2(-LensRadius, 0);
Vector2 p3 = new Vector2(LensRadius, 0);
Vector2 p4 = new Vector2(LensRadius, Height);
Shape[Shape.Length - 3] = new Line() { P1 = p1, P2 = p2 };
Shape[Shape.Length - 2] = new Line() { P1 = p2, P2 = p3 };
Shape[Shape.Length - 1] = new Line() { P1 = p3, P2 = p4 };
}
public static Vector2[] RadialCurve(float lenseRadius, float curveRadius, int samplesCount) {
//r*r = x*x + h*h
//h*h = r*r - x*x
Vector2[] result = new Vector2[samplesCount];
float h = Mathf.Sqrt(curveRadius * curveRadius - lenseRadius * lenseRadius);
float dx = (-2.0f * lenseRadius) / (samplesCount - 1);
for (int i = 0; i < samplesCount; i++) {
float x2 = lenseRadius + dx * i;
float h2 = Mathf.Sqrt(curveRadius * curveRadius - x2 * x2);
result[i] = new Vector2(x2, Mathf.Sign(curveRadius) * (h2 - h));
}
return result;
}
}