Search code examples
c#mathdirectioncalculus

Change parabola's direction to follow mouse drag


Im trying to calculate the parabolic trail left in water if an object gets dragged along the surface. I thought using the locus method with Focus, vertex and directrix would be the way to go. which works well for vertical and horizontal movement (depending on directrix axis) but with the directrix constantly moving its axis direction I'm stuck.

this is what i have now (written for vertical movement)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class Parabola : MonoBehaviour {

public GameObject pointD, pointF, pointM;
public LineRenderer line;

public Vector3 mouse=Vector3.one, prev;
public Vector3 directrix;
public Vector3 focusPoint, directrixPointMid, directrixPointL, directrixPointR;
public float directrixLength;
public int resolution;
public Vector3 focalLine;
public float focalLenght;
public Vector3 p;
public List<Vector3> points = new List<Vector3>();

void Start () 
{
    prev = mouse;
    line = GetComponent<LineRenderer>();
    line.positionCount = resolution;
}

void Update () 
{
    if(Input.GetMouseButton(0))
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if(Physics.Raycast(ray, out hit, Mathf.Infinity, 1<<LayerMask.NameToLayer("Plane")))
        {
            mouse = new Vector3(hit.point.x,0,hit.point.z);
            if(prev != mouse)
            {
                focalLine = prev - mouse;
                focusPoint = mouse + focalLine.normalized * focalLenght;
                directrixPointMid = mouse +(-focalLine.normalized  * focalLenght);
                directrix = Vector3.Cross(directrixPointMid-focusPoint, Vector3.up);
                directrixPointL = directrixPointMid +(directrix.normalized * (directrixLength/2));
                directrixPointR = directrixPointMid +(-directrix.normalized * (directrixLength/2));

                Debug.DrawRay(mouse, focalLine.normalized*10,Color.green);
                Debug.DrawRay(directrixPointR, directrix.normalized*directrixLength,Color.red);

                pointD.transform.position = directrixPointMid;
                pointF.transform.position = focusPoint;
                pointM.transform.position = mouse;
                prev = mouse;
            }
        }
        points = new List<Vector3>();
        for(float i = -directrixLength/2; i<=directrixLength/2; i+=directrixLength/resolution)
        {
            //float angle = Vector3.Angle(Vector3.forward,focalLine);

            float z = 1/(2*(focusPoint.z-directrixPointMid.z))*Mathf.Pow((Mathf.Abs(directrixPointMid.x)+i)-focusPoint.x,2)+0.5f*(focusPoint.z+directrixPointMid.z);
            float x = directrixPointMid.x+i;
            p = new Vector3(x,1,z);

            //p = new Vector3(x*Mathf.Cos(angle)-z*Mathf.Sin(angle),1,x*Mathf.Sin(angle) + z*Mathf.Cos(angle));
            //p.z = 2*p.x + Mathf.Sin(p.x);
            //p = new Vector3(p.x*Mathf.Cos(angle)-p.z*Mathf.Sin(angle),1,p.x*Mathf.Sin(angle) + p.z*Mathf.Cos(angle));

            points.Add(p);
        }
        line.SetPositions(points.ToArray());

    }
}
}

there are some commented out attempts but nothing working. screen shot of parabola


Solution

  • I changed my set up to use y = x^2 and made all my coordinates local to the direction of dragging.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Parabola : MonoBehaviour {
    
    public GameObject linePrefab, pointV;
    
    public float offset, amp, speed;
    public int freq;
    public float directrixLength;
    public int resolution;
    
    private Vector3 mouse, prev;
    private Vector3 directrix;
    private Vector3 focusPoint, directrixPointMid, directrixPointL, directrixPointR;
    private Vector3 focalLine;
    private Vector3 p;
    private List<List<Vector3>> pointsR = new List<List<Vector3>>();
    private List<LineRenderer> lines = new List<LineRenderer>();
    public Renderer render;
    
    void Start () 
    {
        render.material.SetInt("_Length", resolution);
        render.material.SetVectorArray("_Points", new Vector4[resolution]); 
    
        prev = mouse;
        int i = 0;
        while(i <= freq)
        {
            GameObject g = Instantiate(linePrefab);
            g.transform.SetParent(transform);
            g.transform.localPosition = Vector3.zero;
            LineRenderer line = g.GetComponent<LineRenderer>();
            line.positionCount = resolution;
            lines.Add(line);
            List<Vector3> lR = new List<Vector3>();
            pointsR.Add(lR);
            i++;
        }
    }
    
    void FixedUpdate () 
    {
        if(Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray, out hit, Mathf.Infinity, 1<<LayerMask.NameToLayer("Plane")))
            {
                mouse = new Vector3(hit.point.x,0,hit.point.z);
                StopCoroutine(CalculateParabola(mouse));
                StartCoroutine(CalculateParabola(mouse));
                transform.position = mouse;
                transform.forward = -focalLine;
            }
        }
    }
    IEnumerator CalculateParabola(Vector3 v)
    {
        while(prev != v)
        {
            focalLine = prev - v;
            //calculate a parabola for each n in frequency, make it go wider the higher n is
            for(int n=0; h<=freq; n++)
            {
                pointsR[n] = new List<Vector3>();
                float s = Vector3.Distance(prev.normalized,v.normalized)*speed;
                for(float i = -directrixLength/2; i<=(directrixLength+1)/2; i+=directrixLength/resolution)
                {
                    p.x = pointV.transform.localPosition.x+(i*(freq-n+Time.fixedDeltaTime*s));
                    p.z = ((amp*(freq-n))*(Time.fixedDeltaTime*s))*-Mathf.Pow(p.x,2)+(1+offset*n);
                    pointsR[n].Add(p);
                }
                lines[h].SetPositions(pointsR[n].ToArray());
                //attempt to translate the points to a shader, only for the first parabola for now
                Vector4[] renderPoints = new Vector4[resolution];
                for(int i=0; i<resolution; i++)
                {
                    renderPoints[i] = transform.TransformPoint(pointsR[0][i]) - render.transform.position;
                }
                render.material.SetVectorArray("_Points", renderPoints); 
            }
            yield return new WaitForSeconds(0.1f);
            prev = v;
        }
    }
    }