Hololens2的CT显示&定位的简化准确且视野随动版

原有例子很繁琐,虽然前一篇我改成了cylinder模拟probe,能使CTplane上实时模拟出screws的方位。但还是在原有例子的枷锁之下完成的。原例子有以下几大“罪状”:1.CTplane是固定的,不符合实际手术场景需求,要求改成随视野动且垂直 2.无聊弄蒙人的旋转太多,根物体Bone1一开始就旋转了90度,仅仅是为了CT的比较长的一侧(这里为CT的个体Z坐标方向)在一开始横在观察者面前,因Dicom数据是x,y,z的方向依次列出的,作者无法旋转CT即Dicom数据,因此为了实现上面的目的,把它的父物体给转了90度,然后造成一些列的麻烦,如CTplane又旋转了-90度转回去了(因父物体Bone1),就为了能在一开始垂直视野,导致我们想改CTplane为随动的时候,它不是正对视野的而是始终侧面对着视野,你始终追看不到CTplane上的内容,哈哈,气人吧,j为啥呢?经测试物体的正对视野随动只管自身的local坐标系,这里的CTplane虽然全局跟世界坐标系相同,但是自身转了-90度实现的,即随动的时候,CTplane是转了-90度呈现在你面前的。3.两个层级的不同比例的scale放大,让人从全局推算局部移动的时候晕头转向的,把我整晕了三次,每次2天呢!要不是拿出来try and errors的绝招,还过不了这一关呢!4.无用的物体太多,如screws的操控按钮和代码,还有一个什么dummyholder的鬼东西,设为了我的CT边缘小球的parent,害的我到现在都没弄明白为何没有这鬼东西我的CT边缘小球就显示不出来,为何换个其它物体作为parent或不指定parent也显示不出来。 我要改改改。。。这些东西太多余了。

首先 CTplane从Bone1中独立出来,自成一家,角度转回去,直接拖出来就行,比例自动调节的不用管,其实是保持不变的。其次Bone1改名Bone,旋转角度归零。全部坐标跟世界坐标一样了,哈哈,当然我那不争气的OBJ的不是,因它跟CT不是一套,我懵懂的时候全手工调出来的重合方位呢,这辈子是不可能更改了,下次的CT跟OBJ一定要一起产生,没那么多麻烦事啦。最后不相关的组件和代码全部删掉,一切从简,让人好理解好学习好借鉴点不好么,太气人了。

改造成功了,运行效果很好,很精确。上图。

 

 

 

 

 

 程序主界面

 

 CTReader.cs代码:

using Microsoft.MixedReality.Toolkit.UI;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using UnityEngine;

public class CTReader : MonoBehaviour
{
    public TextAsset ct;
    public ComputeShader slicer;
//    public GameObject sliderH, sliderV;
//    public GameObject quadH, revQuadH, quadV, revQuadV, ctPlaneV, ctPlaneH;
    public GameObject  ctPlaneV, ctPlaneH;
    int kernel;

    [HideInInspector]
    public GameObject bottomBackLeft, bottomBackRight, topBackLeft,
        topBackRight, bottomFrontLeft, bottomFrontRight, topFrontLeft,
        topFrontRight, center;
    [HideInInspector]
    public float ctLength, ctDepth;
    [HideInInspector]
    public Vector3 ctCenter;
    [HideInInspector]
    public byte[] ct_bytes;
    [HideInInspector]
    public float minx, maxx, miny, maxy, minz, maxz, width, height, depth;
    private NRRD nrrd;
 
    public void Init()
    {
        ct_bytes = ct.bytes;
        nrrd = new NRRD(ct_bytes);

        kernel = slicer.FindKernel("CSMain");
        var buf = new ComputeBuffer(nrrd.data.Length, sizeof(float));
        buf.SetData(nrrd.data);
        slicer.SetBuffer(kernel, "data", buf);
        slicer.SetInts("dims", nrrd.dims);
        PointCloud(nrrd);
/*
        //        Vector3 refCenter = CTConstants.REFERENCE_CENTER;
        Vector3 refCenter = new Vector3(-3.244141f, -226.2559f, -67.4f);
        Vector3 newCenter = GetCenterOfCt();

        //OBJ对齐
        float xTranslation = refCenter.x - newCenter.x,
            yTranslation = refCenter.y - newCenter.y,
            zTranslation = refCenter.z - newCenter.z;
        GameObject.Find("Patient4").transform.localPosition = new Vector3(xTranslation, yTranslation, zTranslation);

        foreach (GameObject go in GetPoints())//以下CT对齐
        {
            Vector3 goPos = go.transform.localPosition;
            go.transform.localPosition = new Vector3(
                goPos.x + xTranslation, goPos.y + yTranslation, goPos.z + zTranslation);
        }
        CenterToCCCT();
*/
    }

    private void PointCloud(NRRD nrrd)
    {
//        Debug.Log("PointCloud******************");
 //       AutoAlign autoAlign = oo.GetComponent<AutoAlign>();
//        autoAlign.SetGo(false);
 //       DummyTransformHandler dummyHandler = oo.GetComponent<DummyTransformHandler>();
 //       dummyHandler.GoToZero();

        float lengthDirection = nrrd.lengthDirection, lengthSize = nrrd.dims[2];
        ctLength = Math.Abs(lengthDirection * lengthSize);
        float depthDirection = nrrd.depthDirection, depthSize = nrrd.dims[1];
        ctDepth = Math.Abs(depthDirection * depthSize);
        ctCenter = new Vector3(
            -1 * (nrrd.origin.x + ((int)Math.Ceiling((double)(nrrd.dims[0] / 2) - 1) * nrrd.scale.x)),
            nrrd.origin.y + ((int)Math.Ceiling((double)(nrrd.dims[1] / 2) - 1) * nrrd.scale.y),
            nrrd.origin.z + ((int)Math.Ceiling((double)(nrrd.dims[2] / 2) - 1) * nrrd.scale.z)
            );

        ComputeMinMaxFloats(nrrd);//CT的界限坐标转换成了mm单位了(nrrd中的dim*scale)

        bottomBackLeft = CreateSphereFromPos(minx, miny, minz, "bottomBackLeft");
        bottomBackRight = CreateSphereFromPos(minx, miny, maxz, "bottomBackRight");
        topBackLeft = CreateSphereFromPos(minx, maxy, minz, "topBackLeft");
        topBackRight = CreateSphereFromPos(minx, maxy, maxz, "topBackRight");
        bottomFrontLeft = CreateSphereFromPos(maxx, miny, minz, "bottomFrontLeft");
        bottomFrontRight = CreateSphereFromPos(maxx, miny, maxz, "bottomFrontRight");
        topFrontLeft = CreateSphereFromPos(maxx, maxy, minz, "topFrontLeft");
        topFrontRight = CreateSphereFromPos(maxx, maxy, maxz, "topFrontRight");
        Vector3 ccct = FindCenter(minx, maxx, miny, maxy, minz, maxz);
        center = CreateSphereFromPos(ccct.x, ccct.y, ccct.z, "center");
        center.transform.localScale = new Vector3(0f, 0f, 0f);
//        Debug.Log($"ccct center's position {ccct}");
        width = maxz - minz;//就是dim[2]*scale[2][2],因爷爷物体Bone_1绕y轴旋转了90度,Bone_1内部的相互之间x,y,z正常加减,但与其外部的要用全局方向。全局中ct的z方向和x方向对换了即你看到的最长的width在全局中是x轴上的
        height = maxy - miny;//就是dim[1]*scale[1][1],对应全局的y轴不变
        depth = maxx - minx;//就是dim[0]*scale[0][0],因爷爷物体Bone_1绕y轴旋转了90度,所以全局中对应z轴
        /*        Vector3 quadLocalScaleH = new Vector3(depth, height, 1f),
                    revQuadLocalScaleH = new Vector3(-depth, height, 1f),
                    quadLocalScaleV = new Vector3(width, height, 1f),
                    revQuadLocalScaleV = new Vector3(width, -height, 1f);
                quadH.transform.localScale = quadLocalScaleH * 0.0025f;
                revQuadH.transform.localScale = revQuadLocalScaleH * 0.0025f;
                quadV.transform.localScale = quadLocalScaleV * 0.0025f;
                revQuadV.transform.localScale = revQuadLocalScaleV * 0.0025f;*/
        //        Debug.Log($"quadH's Scale {quadH.transform.localScale}");
        //        Debug.Log($"quadV's Scale {quadV.transform.localScale}");
        /*        Vector3 ctPlaneVLocalScale = new Vector3(ctPlaneV.transform.localScale.y / Math.Abs(height / width),ctPlaneV.transform.localScale.y, ctPlaneV.transform.localScale.z);//官网的ctPlaneV的设置,以原有的ctPlaneV的y值为基础,x值放大width/height倍
                ctPlaneV.transform.localScale = ctPlaneVLocalScale;*/

        //        Vector3 ctPlaneVLocalScale = new Vector3(width , height, ctPlaneV.transform.localScale.z);//自己后来加的,CT图像原尺寸显示
        //        Vector3 ctPlaneHLocalScale = new Vector3(depth, height, ctPlaneH.transform.localScale.z);//自己后来加的,CT图像原尺寸显示
        Vector3 ctPlaneVLocalScale = new Vector3(width * 2, height * 2, ctPlaneV.transform.localScale.z);//自己后来加的,CT图像放大2倍显示用,想几倍显示把2改为几就行了,不影响screwslice.cs中的screw位置显示,因screw是ctplane的子物体会自动跟随放大
        Vector3 ctPlaneHLocalScale = new Vector3(depth * 2, height * 2, ctPlaneH.transform.localScale.z);//自己后来加的,CT图像放大2倍显示用,想几倍显示把2改为几就行了,不影响screwslice.cs中的screw位置显示,因screw是ctplane的子物体会自动跟随放大
        ctPlaneV.transform.localScale = ctPlaneVLocalScale;//显示
        ctPlaneH.transform.localScale = ctPlaneHLocalScale ;//自己后来加的
//        Debug.Log($"ctPlaneV's Scale {ctPlaneV.transform.localScale}");
//        Debug.Log($"ctPlaneH's Scale {ctPlaneH.transform.localScale}");

        transform.localPosition = center.transform.localPosition;
        transform.localScale = new Vector3(height, depth, width);
//        Debug.Log($"CT's new Position {transform.localPosition}");
//        Debug.Log($"CT's new Scale {transform.localScale}");
 //       autoAlign.SetGo(true);
    }

    public void CenterToCCCT()
    {
        if (transform.localPosition != center.transform.localPosition)
        {
            transform.localPosition = center.transform.localPosition;
            Debug.Log($"adjusted cc local position {transform.localPosition}");
        }
    }

    /*
    public void ComputeOffsets()
    {
        foreach (var pt in GetPoints())
        {
            pt.GetComponent<AutoAlign>().ComputeOffset();
        }
    }
    */

    private void ComputeMinMaxFloats(NRRD nrrd)
    {
        int rounds = 2;
        minx = float.MaxValue;
        maxx = float.MinValue;
        miny = float.MaxValue;
        maxy = float.MinValue;
        minz = float.MaxValue;
        maxz = float.MinValue;

        for (int i = 0; i < rounds; i++)
        {
            // i : 10 == x : dim --> x = dim*i/10
            int dx = (int)(nrrd.dims[0] * i / (rounds - 1));
            for (int j = 0; j < rounds; j++)
            {
                int dy = (int)(nrrd.dims[1] * j / (rounds - 1));
                for (int k = 0; k < rounds; k++)
                {
                    int dz = (int)(nrrd.dims[2] * k / (rounds - 1));
                    float x = -1 * (nrrd.origin.x + dx * nrrd.scale.x);
                    float y = nrrd.origin.y + dy * nrrd.scale.y;
                    float z = nrrd.origin.z + dz * nrrd.scale.z;
                    minx = Math.Min(minx, x);
                    maxx = Math.Max(maxx, x);
                    miny = Math.Min(miny, y);
                    maxy = Math.Max(maxy, y);
                    minz = Math.Min(minz, z);
                    maxz = Math.Max(maxz, z);
                }
            }
        }
    }

    private GameObject CreateSphereFromPos(float x, float y, float z, String n)
    {
        GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        sphere.transform.position = new Vector3(x, y, z);
        sphere.transform.localScale = new Vector3(1.0f,1.0f,1.0f);
        sphere.name = n;
//        sphere.transform.parent = oo.transform;

        return sphere;
    }

    private Vector3 FindCenter(float minx, float maxx, float miny, float maxy, float minz, float maxz)
    {
        float middlex = (maxx + minx) / 2,
            middley = (maxy + miny) / 2,
            middlez = (maxz + minz) / 2;

        return new Vector3(middlex, middley, middlez);
    }

    public Vector3 GetCenterOfCt()
    {
/*        AutoAlign autoAlign = oo.GetComponent<AutoAlign>();
        autoAlign.SetGo(false);
        DummyTransformHandler dummyHandler = oo.GetComponent<DummyTransformHandler>();
        dummyHandler.GoToZero();*/

        float lengthDirection = nrrd.lengthDirection, lengthSize = nrrd.dims[2];
        ctLength = Math.Abs(lengthDirection * lengthSize);
        float depthDirection = nrrd.depthDirection, depthSize = nrrd.dims[1];
        ctDepth = Math.Abs(depthDirection * depthSize);
        ctCenter = new Vector3(
            -1 * (nrrd.origin.x + ((int)Math.Ceiling((double)(nrrd.dims[0] / 2) - 1) * nrrd.scale.x)),
            nrrd.origin.y + ((int)Math.Ceiling((double)(nrrd.dims[1] / 2) - 1) * nrrd.scale.y),
            nrrd.origin.z + ((int)Math.Ceiling((double)(nrrd.dims[2] / 2) - 1) * nrrd.scale.z)
            );//不知为何x要加负号

        ComputeMinMaxFloats(nrrd);
        Vector3 ccct = FindCenter(minx, maxx, miny, maxy, minz, maxz);

 //       dummyHandler.RestoreBackup();
 //       autoAlign.SetGo(true);
        return ccct;
    }

    public GameObject[] GetPoints()
    {
        GameObject[] arr = new GameObject[] { bottomBackLeft, bottomBackRight, topBackLeft, topBackRight, bottomFrontLeft, bottomFrontRight, topFrontLeft, topFrontRight, center };
        return arr;
    }

    public void Slice(Vector3 orig, Vector3 dx, Vector3 dy, Texture2D result, bool disaligned, Vector4 bCol) {
        var rtex = new RenderTexture(result.width, result.height, 1);
        rtex.enableRandomWrite = true;
        rtex.Create();
        slicer.SetTexture(kernel, "slice", rtex);
        slicer.SetInts("outDims", new int[] { rtex.width, rtex.height });

        dx = dx * RtexConstants.SCALE;
        dy = dy * RtexConstants.SCALE;

        slicer.SetFloats("orig", new float[] { orig.x, orig.y, orig.z }); 
        slicer.SetFloats("dx", new float[] { dx.x, dx.y, dx.z });
        slicer.SetFloats("dy", new float[] { dy.x, dy.y, dy.z });
        slicer.SetFloats("borderColor", new float[] { bCol.x, bCol.y, bCol.z, bCol.w });
        slicer.Dispatch(kernel, (rtex.width + 7) / 8, (rtex.height + 7) / 8, 1);

        var oldRtex = RenderTexture.active;
        RenderTexture.active = rtex;
        result.ReadPixels(new Rect(0, 0, rtex.width, rtex.height), 0, 0);
        result.Apply();
        RenderTexture.active = oldRtex;
        rtex.Release();
    }

    public Vector3 TransformWorldCoords(Vector3 p) {//此函数仅是给官网例子中的HandSlice.cs用的
        return GetComponent<Transform>().InverseTransformPoint(p);
    }

    public Vector3 GetPositionFromSlider(float v, SliderSlice.Axis ax) {
        // (v+0.5) : 1 == x : delta
        Vector3 pos = new Vector3(center.transform.localPosition.x,
            center.transform.localPosition.y,
            center.transform.localPosition.z);
        switch (ax)
        {
            case SliderSlice.Axis.X:
                // depth
                pos.x = bottomFrontLeft.transform.localPosition.x +
                    ((bottomBackLeft.transform.localPosition.x - 
                    bottomFrontLeft.transform.localPosition.x) * (v + 0.5f));
                break;
            case SliderSlice.Axis.Y:
                // height
                pos.y = bottomFrontLeft.transform.localPosition.y +
                    ((topFrontLeft.transform.localPosition.y -
                    bottomFrontLeft.transform.localPosition.y) * (v + 0.5f));
                break;
            case SliderSlice.Axis.Z:
                // length
                pos.z = bottomFrontLeft.transform.localPosition.z +
                    ((bottomFrontRight.transform.localPosition.z -
                    bottomFrontLeft.transform.localPosition.z ) * (v + 0.5f));
                break;
        }
        return pos;
    }
}

public class NRRD {
    readonly public Dictionary<String, String> headers = new Dictionary<String, String>();
    readonly public float[] data;
    readonly public int[] dims;

    readonly public float lengthDirection;
    readonly public float depthDirection;

    readonly public Vector3 origin = new Vector3(0, 0, 0);
    readonly public Vector3 scale = new Vector3(1, 1, 1);

    public NRRD(byte[] bytes) {
        using (var reader = new BinaryReader(new MemoryStream(bytes))) {
            for (string line = reader.ReadLine(); line.Length > 0; line = reader.ReadLine()) {
                if (line.StartsWith("#") || !line.Contains(":")) continue;
                var tokens = line.Split(':');
                var key = tokens[0].Trim();
                var value = tokens[1].Trim();
                headers.Add(key, value);
            }

            if (headers["dimension"] != "3") throw new ArgumentException("NRRD is not 3D");
            if (headers["type"] != "float") throw new ArgumentException("NRRD is not of type float");
            if (headers["endian"] != "little") throw new ArgumentException("NRRD is not little endian");
            if (headers["encoding"] != "gzip") throw new ArgumentException("NRRD is not gzip encoded");

            dims = Array.ConvertAll(headers["sizes"].Split(), s => int.Parse(s));
            if (headers.ContainsKey("space origin")) {
                var origin = Array.ConvertAll(headers["space origin"].Substring(1, headers["space origin"].Length - 2).Split(','), v => float.Parse(v, CultureInfo.InvariantCulture));
                this.origin = new Vector3(origin[0], origin[1], origin[2]);
            }
            if (headers.ContainsKey("space directions")) {
                var scale = Array.ConvertAll(headers["space directions"].Split(), s => Array.ConvertAll(s.Substring(1, s.Length - 2).Split(','), v => float.Parse(v, CultureInfo.InvariantCulture)));
                if (scale[0][0] == 0 || scale[1][1] == 0 || scale[2][2] == 0) throw new ArgumentException("NRRD has 0 scale value");
                if (scale[0][1] != 0 || scale[1][0] != 0 || scale[2][0] != 0 ||
                    scale[0][2] != 0 || scale[1][2] != 0 || scale[2][1] != 0) throw new ArgumentException("NRRD is not axis-aligned");
                this.scale = new Vector3(scale[0][0], scale[1][1], scale[2][2]);
                depthDirection = scale[1][1];
                lengthDirection = scale[2][2];
            }
//            Debug.Log($"NRRD scale is {this.scale}");
            var mem = new MemoryStream();
            using (var stream = new GZipStream(reader.BaseStream, CompressionMode.Decompress)) stream.CopyTo(mem);
            data = new float[dims[0] * dims[1] * dims[2]];
//            Debug.Log($"data size dims[0] is {dims[0]};dims[1] is {dims[1]};dims[2] is {dims[2]}");
            Buffer.BlockCopy(mem.ToArray(), 0, data, 0, data.Length * sizeof(float));
//            Debug.Log($"data length is {data.Length}");
            mem.Dispose();
            reader.Dispose();
        }
    }
}

public static class BinaryReaderExtension {
    public static string ReadLine(this BinaryReader reader) {
        var line = new StringBuilder();
        for (bool done = false; !done;) {
            var ch = reader.ReadChar();
            switch (ch) {
                case '\r':
                    if (reader.PeekChar() == '\n') reader.ReadChar();
                    done = true;
                    break;
                case '\n':
                    done = true;
                    break;
                default:
                    line.Append(ch);
                    break;
            }
        }
        return line.ToString();
    }
}
/*static class CTConstants
{
    //   public static readonly Vector3 REFERENCE_CENTER = new Vector3(-3.244141f, -226.2559f, -248.5f);
    public static readonly Vector3 REFERENCE_CENTER = new Vector3(-3.244141f, -226.2559f, -67.4f);
}*/
public static class BorderColors
{
    public static readonly Vector4 YELLOW = new Vector4(1, 1, 0, 1);
    public static readonly Vector4 RED = new Vector4(1, 0, 0, 1);
    public static readonly Vector4 CYAN = new Vector4(0, 1, 1, 1);
}

static class RtexConstants
{
    public static readonly float SCALE = 0.001961119675f;
}

Screwslice.cs代码:

using Microsoft.MixedReality.Toolkit.UI;
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Linq;
using UnityEngine;

public class Screwslice : MonoBehaviour
{
    public CTReader ct;
    public int width, height;
    public bool disaligned=false;
    public int interval;
    int curInterval = 0;
    public Texture2D tex;
    Vector4 bCol;
    Vector3 Oldtip = Vector3.zero;
    Vector3 tip = Vector3.zero;
    public enum Axis { X, Y, Z };
    public Axis axis;
    void Start()
    {
        Init();
    }

    public void Init()
    {
        tex = NewTexture(width, height, Color.black);
        /* 以下两句是以前打算挂在到screw上的,后来遇到点儿问题放弃了,现在改挂在CTplane上*/
        //        GameObject.Find("CTPlane1").GetComponent<Renderer>().material.mainTexture = tex;
        //        GameObject.Find("CTPlane2").GetComponent<Renderer>().material.mainTexture = tex;
        GetComponent<Renderer>().material.mainTexture = tex;
        Oldtip = GameObject.Find("Screw1/DownEnd").transform.position;//顶端初始化
        tip = GameObject.Find("Screw1/DownEnd").transform.position;//顶端初始化
    }

    private Texture2D NewTexture(int width, int height, Color color)
    {
        var texture = new Texture2D(width, height);
        Color[] pixels = Enumerable.Repeat(color, width * height).ToArray();
        texture.SetPixels(pixels);
        texture.Apply();
        return texture;
    }

    public void UpdateHelper()
    {
              
        var orig = Vector3.zero;
        var dx = Vector3.zero;
        var dy = Vector3.zero;//以上给H面用
        var val = 0.5f;// (tip[0] - ct.transform.position.x) / (ct.width / 1000);//前面的是m为单位的,所以width要除以1000转成m
        orig = new Vector3(0, 0, val);
        var distance = tip - ct.transform.position;//ct.transform.position就是CT的中心的坐标,相减后产生一个从CT中心到tip点的向量distance
        bCol = BorderColors.CYAN;
        switch (axis)
        {
            case Axis.X://V面即CTplane2选这个
               // val = (tip[2] - ct.transform.position.z) / (ct.depth / 1000);//前面的是m为单位的,所以width要除以1000转成m
               //上面那句不严谨,当CT的x,y,z轴跟全局坐标不重合即发生旋转时,上面的话就不对了
                val = Vector3.Dot(distance, ct.transform.right) / (ct.depth / 1000);//前面的是m为单位的,所以width要除以1000转成m
                orig = new Vector3(val, 0, 0);
                dx = new Vector3(0, 0, 1);
                dy = new Vector3(0, 1, 0);
                //                float rxv = transform.GetChild(0).localEulerAngles.x;
                //                float ryv = transform.GetChild(0).localEulerAngles.y;
                //                float rzv= GameObject.Find("Screw1").transform.rotation.eulerAngles.z;
                //               transform.GetChild(0).localEulerAngles = new Vector3(rxv,ryv,rzv);
                float projectxV = Vector3.Dot(GameObject.Find("Screw1").transform.up,ct.transform.right);//点乘,对后面的单位向量来说就是在其上的投影长度
                float projectyV = Vector3.Dot(GameObject.Find("Screw1").transform.up, ct.transform.up);//在在CT的y轴上的投影
                float projectzV = Vector3.Dot(GameObject.Find("Screw1").transform.up, ct.transform.forward);//在CT的z轴上的投影
//                float angleV = 360-Mathf.Atan(projectzV/ projectyV) * Mathf.Rad2Deg;//没有考虑两个方向的不同缩放引起的角度变形
                float angleV = 360 - Mathf.Atan((projectzV / ct.width) / (projectyV / ct.height)) * Mathf.Rad2Deg;//因ctplane的长宽方向的localscale不同,所以求角度的长度也要从广域尺寸变为局部尺寸
                                                                                                                  //即除以相应的要去到的层级的所有父物体的localscale的乘积projectzV/(ct.width*0.001)和projectyV/(ct.height*0.001)
                                                                                                                  //                transform.GetChild(0).up= new Vector3(normalx, normaly, normalz);
                transform.GetChild(0).localEulerAngles = new Vector3(0,0,angleV);
                float distancexv = Vector3.Dot(distance, ct.transform.right);//点乘,对后面的单位向量来说就是在其上的投影长度
                float distanceyv = Vector3.Dot(distance, ct.transform.up);//在在CT的y轴上的投影
                float distancezv = Vector3.Dot(distance, ct.transform.forward);//在CT的z轴上的投影
                var translatevalv = (transform.GetChild(0).transform.up) * (transform.GetChild(0).transform.localScale.y);
                //                transform.GetChild(0).localPosition = new Vector3(distancezv / (ct.width / 1000) + translatevalv[0], distanceyv / (ct.height / 1000) + translatevalv[1], 0);//旋转后不准
                //                transform.GetChild(0).localPosition = new Vector3(distancezv / (ct.width / 1000), distanceyv / (ct.height / 1000), 0);//中点很准
                //                  transform.GetChild(0).localPosition = new Vector3(distancezv/ (ct.width / 1000)+ Mathf.Sin(angleV-270)/ 2, distanceyv/ (ct.height / 1000) + Mathf.Cos(angleV-270) / 2, 0);//角度不行,不知为何               
                float cylinderVx = Vector3.Dot(transform.GetChild(0).transform.up, transform.right);
                float cylinderVy = Vector3.Dot(transform.GetChild(0).transform.up, transform.up);
                transform.GetChild(0).localPosition = new Vector3(distancezv / (ct.width / 1000) + cylinderVx / 2, distanceyv / (ct.height / 1000) + cylinderVy / 2, 0);//下端点,很准,即使CTplane成倍放大也不用改此句,因screw是ctplane的子物体会自动跟随放大
              
                Debug.Log($"V localEulerAngles is {transform.GetChild(0).localEulerAngles}");
                Debug.Log($"translatevalv is {translatevalv},distancezv is {distancezv},distanceyv is {distanceyv}");
                break;
            case Axis.Y://官方例程的设置,不知为何
                //val = (tip[1] - ct.transform.position.y) / (ct.height / 1000);//前面的是m为单位的,所以width要除以1000转成m
                val = Vector3.Dot(distance, ct.transform.up) / (ct.height / 1000);//前面的是m为单位的,所以width要除以1000转成m
                orig = new Vector3(val, 0, 0);
                dx = new Vector3(0, 0, 1);
                dy = new Vector3(-1, 0, 0);
                break;
            case Axis.Z://H面即CTplane1选这个
                //val = (tip[0] - ct.transform.position.x) / (ct.width / 1000);//前面的是m为单位的,所以width要除以1000转成m
                val = Vector3.Dot(distance, ct.transform.forward) / (ct.width / 1000);//前面的是m为单位的,所以width要除以1000转成m,本来是ct.transform.right对应着width的,因CT cube的z对应着全局的x轴,所以这里用forward看UNITY内容
                orig = new Vector3(0, 0, val);
                dx = new Vector3(1, 0, 0);
                dy = new Vector3(0, 1, 0);
                //                float rxh = transform.GetChild(0).localEulerAngles.x;
                //                float ryh = transform.GetChild(0).localEulerAngles.y;
                //                float rzh = GameObject.Find("Screw1").transform.rotation.eulerAngles.x;
                //                transform.GetChild(0).localEulerAngles = new Vector3(rxh, ryh, rzh);
                
                float projectxH = Vector3.Dot(GameObject.Find("Screw1").transform.up, ct.transform.right);//点乘,对后面的单位向量来说就是在其上的投影长度
                float projectyH = Vector3.Dot(GameObject.Find("Screw1").transform.up, ct.transform.up);//在在CT的y轴上的投影
                float projectzH = Vector3.Dot(GameObject.Find("Screw1").transform.up, ct.transform.forward);//在CT的z轴上的投影
               //  float angleH = 360-Mathf.Atan(projectxH / projectyH)* Mathf.Rad2Deg;//没有考虑两个方向的不同缩放引起的角度变形
                float angleH = 360 - Mathf.Atan((projectxH / ct.depth) / (projectyH / ct.height)) * Mathf.Rad2Deg;//因ctplane的长宽方向的localscale不同,所以求角度的长度也要从广域尺寸变为局部尺寸
                                                                                                                  //即除以相应的要去到的层级的所有父物体的localscale的乘积projectxH/(ct.depth*0.001)和projectyH/(ct.height*0.001)
                transform.GetChild(0).localEulerAngles = new Vector3(0, 0, angleH);
                float distancexH = Vector3.Dot(distance, ct.transform.right);//点乘,对后面的单位向量来说就是在其上的投影长度
                float distanceyH = Vector3.Dot(distance, ct.transform.up);//在在CT的y轴上的投影
                float distancezH = Vector3.Dot(distance, ct.transform.forward);//在CT的z轴上的投影
                var translatevalH= (transform.GetChild(0).transform.up) * (transform.GetChild(0).transform.localScale.y);
                //                transform.GetChild(0).localPosition = new Vector3(distancexH / (ct.depth / 1000), distanceyH / (ct.height/1000), 0);//中点很准
                //                 transform.GetChild(0).localPosition = new Vector3(distancexH/(ct.depth / 1000)+ Mathf.Sin(angleH-270) /2, distanceyH/ (ct.height/1000) + Mathf.Cos(angleH-270) / 2, 0);//角度不行,不知为何
                float cylinderHx = Vector3.Dot(transform.GetChild(0).transform.up, -transform.forward);
                float cylinderHy = Vector3.Dot(transform.GetChild(0).transform.up, transform.up);
                transform.GetChild(0).localPosition = new Vector3(distancexH / (ct.depth / 1000) + cylinderHx / 2, distanceyH / (ct.height / 1000) + cylinderHy / 2, 0);//很准,即使CTplane成倍放大也不用改此句,因screw是ctplane的子物体会自动跟随放大
                //                transform.GetChild(0).localPosition = new Vector3(distancexH / (ct.depth / 1000) + translatevalH[0], distanceyH / (ct.height/1000) + translatevalH[1], 0);//旋转后不准
                Debug.Log($"H localEulerAngles is {transform.GetChild(0).localEulerAngles}");
                Debug.Log($"translatevalH is {translatevalH},distancexH is {distancexH},distanceyH is {distanceyH}");
                break;
        }
        //        Debug.Log($"CT's width:{ct.width},depth:{ct.depth},height:{ct.height}");//长宽高都是以mm为单位的,所以下面的计算中除了1000
        //        Debug.Log($"orig is {orig}");
       
        ct.Slice(orig, dx, dy, tex, disaligned, bCol);

/*       var orig2 = Vector3.zero;//以下给V面用
        var dx2 = Vector3.zero;
        var dy2 = Vector3.zero;
        var val2 = (tip[2] - ct.transform.position.z)/ct.depth;前面的是m为单位的,所以depth要除以1000转成m,val2在-0.5--0.5之间
        orig2 = new Vector3(val2, 0, 0);
        dx2 = new Vector3(0, 0, 1);
        dy2 = new Vector3(0, 1, 0);
        bCol = BorderColors.YELLOW;
        Debug.Log($"CT's center location:{ct.transform.position}");       
        Debug.Log($"orig2 is {orig2}");
        ct.Slice(orig2, dx2, dy2, tex, disaligned, bCol);//给V面即CTPlane2*/
    }

    void Update()
    {
        if (++curInterval < interval)
        {
            return;
        }
        curInterval = 0;
//        Debug.Log($"tip eulerAngles are {GameObject.Find("Screw1").transform.rotation.eulerAngles}");
 //       Debug.Log($"tip rotation is {GameObject.Find("Screw1").transform.rotation}");
//        Debug.Log($"tip localRotation is {GameObject.Find("Screw1").transform.localRotation}");
        tip = GameObject.Find("Screw1/DownEnd").transform.position; ;//时时更新顶端当前值
        
        if (Oldtip != tip)//如果当前值发生变化即移动了此screw
        {
//            Debug.Log($"cyclinder eulerAngles are {GameObject.Find("Screw1").transform.rotation.eulerAngles}");
//            Debug.Log($"cyclinder rotation is {GameObject.Find("Screw1").transform.rotation}");
//            Debug.Log($"cyclinder localRotation is {GameObject.Find("Screw1").transform.localRotation}");
//            Debug.Log($"CTplane eulerAngles are {GameObject.Find("BoneManipulation/Bone_1/CTGroup/CTPlane1").transform.eulerAngles}");
//            Debug.Log($"CTplane eulerAngles are {GameObject.Find("BoneManipulation/Bone_1/CTGroup/CTPlane1").transform.rotation.eulerAngles}");
          Debug.Log($"CTplane up Angles is {Vector3.Angle(transform.up, Vector3.up)}");
//            Debug.Log($"CTplane&RIGHT Angles is {Vector3.Angle(transform.up, Vector3.right)}");
//            Debug.Log($"ScrewHview up Angles is {Vector3.Angle(GameObject.Find("BoneManipulation/Bone_1/CTGroup/CTPlane1/ScrewHview").transform.up, Vector3.up)}");
//            Debug.Log($"cyclinder NORMALized is {GameObject.Find("Screw1").transform.up.normalized}");
//            Debug.Log($"cyclinder&RIGHT Angles is {Vector3.Angle(GameObject.Find("Screw1").transform.up, Vector3.right)}");
//            Debug.Log($"cyclinder vector is {GameObject.Find("Screw1").transform.up}");
            var bottompoint = GameObject.Find("Screw1").transform.position - (GameObject.Find("Screw1").transform.up) * (GameObject.Find("Screw1").transform.localScale.y);//圆柱的底端点
//            Debug.Log($"bottompoint position is {bottompoint}");
//            Debug.Log($"tip position is {tip}");
//            Debug.Log($"CT right vector is {ct.transform.right},forward vector is {ct.transform.forward},up vector is {ct.transform.up}");
//            Debug.Log($"CT right vector normalized is {ct.transform.right.normalized},forward normalized is {ct.transform.forward.normalized},up normalized is {ct.transform.up.normalized}");
            //            Vector3.Angle(GameObject.Find("BoneManipulation/Bone_1/CTGroup/CTPlane1/ScrewHview").transform.up, Vector3.up);
            var distance = tip - ct.transform.position;//ct.transform.position就是CT的中心的坐标,相减后产生一个从CT中心到tip点的向量distance
            Debug.Log($"ct.transform.position is {ct.transform.position}");
            Debug.Log($"bone.transform.position is {GameObject.Find("Bone").transform.position}");
            Debug.Log($"obj.transform.position is {GameObject.FindGameObjectWithTag("patientobj").transform.position}");
            Debug.Log($"ct.ctcenter position is {ct.ctCenter}");
            Debug.Log($"ct.GetCenterOfCt is {ct.GetCenterOfCt()}");

            //            Debug.Log($"tip position is {tip}");
            //            Debug.Log($"distance is {distance}");
            //         if ((Math.Abs(distance[0]) <= ct.width/2000)&& (Math.Abs(distance[1]) <= ct.height/2000)&& (Math.Abs(distance[2]) <= ct.depth/2000))//如果screw已经在在ct内部,长宽高要另外除以1000转成m为单位
            if ((Math.Abs(Vector3.Dot(distance, ct.transform.forward)) <= ct.width / 2000) && (Math.Abs(Vector3.Dot(distance, ct.transform.up)) <= ct.height / 2000) && (Math.Abs(Vector3.Dot(distance, ct.transform.right)) <= ct.depth / 2000))//上面的那个不严谨,应该是投影到CT上再比对距离而不似乎简单的xyz比较,因CT有可能有转角呀!
                UpdateHelper();
            Oldtip = tip;//更新顶端的记忆值
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值