Files
OpticsSimulation.Unity/Assets/Polynom.cs
T

282 lines
8.0 KiB
C#
Raw Normal View History

2025-05-13 04:46:54 +03:00
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Polynom : MonoBehaviour {
public float[] Coeffs = new float[] { 0, 0, 0.01f, 0, 0.01f };
public PlotSampler Sampler;
public TraceRay PlotTraceRay;
public int lonSamples = 256;
public int latSamples = 50;
void Start () {
}
void Update () {
}
float Sagitta(float r) {
float y = 0;
int i = 0;
for (i = 4; i >= (int) 2; i--) {
y = y * r + Coeffs[i];
}
y *= Mathf.Pow(r, i + 1);
return y;
}
float Derivative(float r){
float y = 0;
int i = 0;
for (i = 4; i >= 2; i--) {
y = y * r + i * Coeffs[i];
}
y *= Mathf.Pow(r, i);
return y;
}
public float Fx(float x){
return Mathf.Pow(x, 4) * Coeffs[4] + Mathf.Pow(x, 2) * Coeffs[2];
}
Vector3 Normal(Vector3 point){
float r = Mathf.Sqrt(point.x * point.x + point.z * point.z);
if(r == 0) {
return Vector3.up;
} else {
var d = Derivative(r);
return new Vector3(point.x * d / r, -1, point.z * d / r).normalized * -1.0f;
}
}
float RayPlaneDistance(Ray plane, Ray ray)
{
return (Vector3.Dot(plane.origin, plane.direction) - Vector3.Dot(plane.direction, ray.origin)) / (Vector3.Dot(ray.direction, plane.direction));
}
bool Intersect(Ray ray, float startX, float maxX, ref Vector3 point)
{
Ray p = new Ray();
// initial intersection with z=0 plane
{
float s = ray.direction.y;
if (s == 0)
return false;
float a = -ray.origin.y / s;
if (a < 0)
return false;
p.origin = (ray.origin + ray.direction * a).normalized * startX;
}
// p.origin = new Vector3(startX, 0, 0);
// p.direction = Normal(p.origin).normalized;
int n = 64; // avoid infinite loop
while (n-- != 0) {
float new_sag = Sagitta(new Vector2(p.origin.x, p.origin.z).magnitude);
float old_sag = p.origin.y;
// project previous intersection point on curve
p.origin = new Vector3(p.origin.x, new_sag, p.origin.z);
// stop if close enough
if (Mathf.Abs(old_sag - new_sag) < 1e-6)
break;
// get curve tangeante plane at intersection point
p.direction = Normal(p.origin);
// intersect again with new tangeante plane
float a = RayPlaneDistance(p, ray);
// if (a < 0)
// return false;
p.origin = ray.origin + ray.direction * a;
}
if(n <=0) {
return false;
}
if(Vector3.Dot(-ray.direction, Normal(p.origin)) < 0.0f) {
return false;
}
if((new Vector2(p.origin.x, p.origin.z)).sqrMagnitude > (maxX * maxX)) {
return false;
}
point = p.origin;
return true;
}
bool Intersect(Ray ray, ref Vector3 point)
{
var extremums = FindExtremums(4.0f);
extremums.Add(4);
List<float> startPoints = new List<float>();
for (int i = 0; i < extremums.Count - 1; ++i) {
startPoints.Add((extremums[i] + extremums[i + 1]) * 0.5f);
}
List<Vector3> points = new List<Vector3>();
foreach (var e in startPoints) {
if (Intersect(ray, e, 4.0f, ref point)) {
points.Add(point);
}
}
if(points.Count > 0) {
point = points.OrderBy(v => (v - ray.origin).magnitude).First();
return true;
} else {
return false;
}
}
List<float> FindExtremums(float range)
{
List<float> result = new List<float>();
List<float> points = new List<float>();
for (int i = 0; i < 10; ++i) {
points.Add(Derivative(i / 10.0f * range));
}
for (int i = 0; i < 10 - 1; ++i) {
if(Mathf.Abs(points[i]) < 0.000001f) {
result.Add(points[i]);
} else if (Mathf.Abs(points[i + 1]) < 0.000001f) {
result.Add(points[i + 1]);
i++;
} else {
if(Mathf.Sign(points[i]) != Mathf.Sign(points[i + 1])) {
//find zero with secant
float x1 = i / 10.0f * range;
float x2 = (x1 + ((i + 1) / 10.0f * range)) * 0.5f;
float y1 = Derivative(x1);
float y2 = Derivative(x2);
int id = 0;
while (true) {
id++;
if(id > 100) {
break;
}
if (Mathf.Abs(y2) < 0.000001f || Mathf.Abs(y2 - y1) < 0.000001f) {
break;
}
float d = (x2 - x1) / (y2 - y1);
var x2_ = x2;
x2 = x1 - Derivative(x1) * d;
x1 = x2_;
y1 = Derivative(x1);
y2 = Derivative(x2);
}
result.Add(x2);
}
}
}
return result;
}
private void OnDrawGizmos()
{
Gizmos.matrix = transform.localToWorldMatrix;
var extremums = FindExtremums(4.0f);
var maxPoint = extremums.Max();
var minPoint = extremums.Min();
foreach (var ext in extremums) {
Gizmos.color = Color.red;
Gizmos.DrawLine(new Vector3(ext, 0, 0), new Vector3(ext, 100, 0));
}
Gizmos.color = Color.white;
List<Vector3> oldPlot = new List<Vector3>();
for (int j = 0; j < latSamples; ++j) {
// Gizmos.matrix = Matrix4x4.Rotate(Quaternion.AngleAxis(360/20*j, Vector3.up)) * transform.localToWorldMatrix;
Vector3 last = Vector3.zero;
for (int i = 0; i <= lonSamples; ++i) {
float x = (i / (float)lonSamples - 0.5f) * 2.0f * 4.0f;
float val = Fx(x);
Gizmos.color = (Color.Lerp(Color.red, Color.green, (val - minPoint) / (maxPoint - minPoint)));
Vector3 current = Quaternion.AngleAxis(360 / (float)latSamples * j, Vector3.up) * new Vector3(x, val, 0);
if(oldPlot.Count <= i) {
oldPlot.Add(current);
} else {
Gizmos.DrawLine(oldPlot[i], current);
oldPlot[i] = current;
}
if (i != 0) {
Gizmos.DrawLine(last, current);
}
last = current;
}
}
Gizmos.matrix = transform.localToWorldMatrix;
if (Sampler != null) {
float sagitta = Sagitta(Sampler.transform.position.x);
float derivative = Derivative(Sampler.transform.position.x);
Gizmos.DrawWireSphere(new Vector3(Sampler.transform.position.x, sagitta, 0), 0.1f);
Gizmos.color = Color.red;
Gizmos.DrawLine(new Vector3(Sampler.transform.position.x, sagitta, 0), new Vector3(Sampler.transform.position.x, sagitta, 0) + new Vector3(1, derivative, 0) * 0.5f);
Gizmos.color = Color.blue;
Gizmos.DrawLine(new Vector3(Sampler.transform.position.x, sagitta, 0), new Vector3(Sampler.transform.position.x, sagitta, 0) + Normal(new Vector3(Sampler.transform.position.x, sagitta, 0)));
//float sagitta = Sagitta(Sampler.transform.position.x);
}
if(PlotTraceRay != null) {
Vector3 intersection = Vector3.zero;
if(Intersect(new Ray(PlotTraceRay.transform.position, PlotTraceRay.transform.forward), ref intersection)) {
Gizmos.DrawWireCube(intersection, Vector3.one * 0.1f);
}
}
}
}