/**
  *  Instantiate a new ProbeOutputImageDecoder
  *  @param output The source of probedata from which to generate image data.
  */
 public BModeOutputImageDecoder(IProbeOutput output)
 {
     UltrasoundDebug.Assert(null != output, "Null probe output used in constructor", this);
     drawColor    = Color.white;
     probeOutput  = output;
     imageEffects = new List <IImagePostProcessor>();
 }
コード例 #2
0
    /// Use this for initialization
    void Start()
    {
#if UNITY_EDITOR
        // Some input validation
        UltrasoundDebug.Assert(NumberOfScanlines > 0,
                               "Number of scanlines should be a positive integer",
                               this, true);
        UltrasoundDebug.Assert(PointsPerScanline > 0,
                               "Points per scanline should be a positive integer",
                               this, true);
        UltrasoundDebug.Assert(ArcSizeInDegrees >= 0 && ArcSizeInDegrees <= 180f,
                               "Arc size should be between 0 and 180 degrees.",
                               this, true);
        UltrasoundDebug.Assert(MinDistance > 0,
                               "Min distance should be greater than 0",
                               this, true);
        UltrasoundDebug.Assert(MaxDistance > 0,
                               "Max distance should be greater than 0",
                               this, true);
        UltrasoundDebug.Assert(MinDistance < MaxDistance,
                               "Min distance should be smaller than max distance",
                               this, true);
#endif

        output     = new HorayProbeOutput(this.gameObject);
        dataSource = new HorayProbe(this.gameObject, output);
    }
    /**
     *  Instantiate a new instance of UltrasoundScanData.
     *  @param config The configuration settings associated with the probe object.
     */
    public UltrasoundScanData(UltrasoundProbeConfiguration config)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != config, "Null config used in constructor", this);
#endif
        this.probeConfig = config;
        scanlines        = new List <UltrasoundScanline>();
    }
    /**
     *  Add an UltrasoundScanline to this UltrasoundScanData.
     *
     *  @param s The UltrasoundScanline to add.
     */
    public void AddScanline(UltrasoundScanline s)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != s,
                               "A null scanline was added to this UltrasoundScanData",
                               this);
#endif
        scanlines.Add(s);
    }
コード例 #5
0
    /**
     *  Add an UltrasoundPoint to this scanline.
     *
     *  @param p The UltrasoundPoint to add.
     *  @throw ArgumentNullException
     */
    public void AddUltrasoundPoint(UltrasoundPoint p)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != p,
                               "A null UltrasoundPoint was added to a Scanline",
                               this);
#endif
        points.Add(p);
    }
    /**
     *  Set the gain of this probe.
     *
     *  @param gain A non-negative float value.
     */
    public void SetGain(float gain)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(gain > 0f,
                               string.Format("Tried to set gain to {0}", gain),
                               this);
#endif
        this.gain = gain;
    }
    /**
     *  Sets the number of points to check per scanline.
     *  @param count A positive integer value.
     */
    public void SetPointsPerScanline(int count)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(count > 0,
                               string.Format("Tried to set points per scanline to {0}", count),
                               this, false);
#endif
        pointsPerScanline = count;
    }
    /**
     *  Set the number of scanlines to scan.
     *  @param count A positive integer value.
     */
    public void SetNumberOfScanlines(int count)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(count > 0,
                               string.Format("Tried to set scanline count to {0}", count),
                               this, false);
#endif
        numberOfScanlines = Mathf.Clamp(count, 0, int.MaxValue);
    }
    /**
     *  Sets the size of the scanning arc, measured in degrees.
     *  @param degrees A float value between 0 and 180 (inclusive).
     */
    public void SetArcSizeInDegrees(float degrees)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(degrees >= 0f && degrees <= 180f,
                               string.Format("Tried to set degrees to {0}", degrees),
                               this, false);
#endif
        arcSizeInDegrees = Mathf.Clamp(degrees, 0f, 180f);
    }
 /// Use this for initialization
 void Start()
 {
     skin = GameObject.FindGameObjectWithTag("Skin");
     UltrasoundDebug.Assert(null != skin,
                            "Could not find Skin object. Did you forget to tag it?",
                            this);
     UltrasoundDebug.Assert(null != skin.collider,
                            "Skin object does not have a collider.",
                            this);
     dragMode = false;
 }
    /**
     *  Create a new UltrasoundPoint.
     *  @param worldSpaceLocation The location of the point in 3D world space.
     *  @param projectedLocation Within a scanning plane, the 2D position of the point.
     */
    public UltrasoundPoint(Vector3 worldSpaceLocation, Vector2 projectedLocation)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(projectedLocation.y > 0f,
                               "y coordinate of projectedLocation should be positive.",
                               this);
#endif
        this.worldSpaceLocation = worldSpaceLocation;
        this.projectedLocation  = projectedLocation;
        brightness = 0f;
    }
    /**
     *  Sets the maximum scanning distance of the probe.
     *  @param max Clamped within the (exclusive) interval 0 to Positive Infinity.
     */
    public void SetMaxScanDistance(float max)
    {
        maxDistance = Mathf.Clamp(max, float.Epsilon, float.MaxValue);
#if UNITY_EDITOR
        UltrasoundDebug.Assert(maxDistance > minDistance,
                               string.Format("SetMaxScanDistance:" +
                                             "Max distance ({0}) should be greater than min distance ({1})!",
                                             maxDistance, minDistance),
                               this);
#endif
    }
    /**
     *  Add a collection of UltrasoundScanline%s to this scanline.
     *
     *  @param scanlines The UltrasoundScanline%s to add.
     */
    public void AddScanlines(ICollection <UltrasoundScanline> scanlines)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != scanlines,
                               "A null collection of scanlines was added to this UltrasoundScanData",
                               this);
#endif
        foreach (UltrasoundScanline s in scanlines)
        {
            AddScanline(s);
        }
    }
コード例 #14
0
    /**
     *  Add a collection of UltrasoundPoint%s to this scanline.
     *
     *  @param points The UltrasoundPoint%s to add.
     *  @throw ArgumentNullException
     */
    public void AddUltrasoundPoints(ICollection <UltrasoundPoint> points)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != points,
                               "A null collection of points was added to a Scanline",
                               this);
#endif
        foreach (UltrasoundPoint p in points)
        {
            AddUltrasoundPoint(p);
        }
    }
    /**
     *	Sets up a standard HORAY configuration:
     *
     *		probe -> HorayProbeOutput -> BModeOutputImageDecoder (+blur) -> TextureSource
     */
    public static ITextureSource BuildStandardHORAYConfig()
    {
        GameObject probe = GameObject.FindGameObjectWithTag("Probe");

        UltrasoundDebug.Assert(null != probe, "No object with Probe tag in scene.", new DisplayTexturePipelineFactory());

        IProbeOutput horayOutput       = new HorayProbeOutput(probe);
        IImageSource bmodeImageDecoder = new BModeOutputImageDecoder(horayOutput);

        bmodeImageDecoder.AddPostProcessingEffect(new GrayscaleGaussianBlur());
        return(new TextureSource(bmodeImageDecoder));
    }
コード例 #16
0
    /**
     *  Removes organs that are visible, but haven't been seen recently enough.
     *  (i.e. the time that the organ was last hit by a raycast is more than EXPIRATION_TIME_IN_SECONDS ago)
     */
    private void RemoveExpiredObjects()
    {
        /* We store the keys and organs to remove in temporary lists. This is to avoid bugs associated
         * with editing the items in a Dictionary while iterating over it in a foreach loop.
         */
        IList <float>      keysToRemove   = new List <float>();
        IList <GameObject> organsToRemove = new List <GameObject>();

        foreach (float timeLastSeen in currentVisibleOrgans.Keys)
        {
            float timeElapsed = Time.time - timeLastSeen;
            if (timeElapsed >= EXPIRATION_TIME_IN_SECONDS)
            {
                GameObject organToRemove = null;
                currentVisibleOrgans.TryGetValue(timeLastSeen, out organToRemove);
#if UNITY_EDITOR
                UltrasoundDebug.Assert(null != organToRemove,
                                       string.Format("Couldn't find organ added at time {0}", timeLastSeen),
                                       this);
#endif
                keysToRemove.Add(timeLastSeen);
                organsToRemove.Add(organToRemove);
            }
            else
            {
                // Remember that the SortedDictionary implementation will store the oldest objects first.
                // As soon as we reach an object that isn't expired, we know that all the rest won't be expired either.
                break;
            }
        }
#if UNITY_EDITOR
        UltrasoundDebug.Assert(organsToRemove.Count == keysToRemove.Count,
                               string.Format("{0} organs marked for removal, {1} keys marked for removal",
                                             organsToRemove.Count, keysToRemove.Count),
                               this);
#endif
        for (int i = 0; i < organsToRemove.Count; ++i)
        {
            currentVisibleOrgans.Remove(keysToRemove[i]);
            reverseMapLookup.Remove(organsToRemove[i]);
        }

#if UNITY_EDITOR
        UltrasoundDebug.Assert(currentVisibleOrgans.Count == reverseMapLookup.Count,
                               string.Format("currentVisibleOrgans.Count = {0}, reverseMapLookup.Count = {1}",
                                             currentVisibleOrgans.Count, reverseMapLookup.Count),
                               this);
#endif
    }
    /**
     *  Instantiate a new UltrasoundProbeConfiguration with a UnityEngine.Transform.
     *  Other configuration values use the defaults:
     *
     *  - minDistance: float.Epsilon (1.40E-45)
     *  - maxDistance: 10.0
     *  - arcSizeInDegrees: 75
     *  - numberOfScanlines: 40
     *  - pointsPerScanline: 40
     *  - gain: 1.0f
     *
     *  @param probeTransform The UnityEngine.Transform of the probe GameObject.
     */
    public UltrasoundProbeConfiguration(Transform probeTransform)
    {
        UltrasoundDebug.Assert(null != probeTransform,
                               "Null transform passed to UltrasoundProbeConfiguration constructor.",
                               this);
        this.SetRotation(probeTransform.rotation);
        this.SetPosition(probeTransform.position);

        this.minDistance       = float.Epsilon;
        this.maxDistance       = 10f;
        this.arcSizeInDegrees  = 75;
        this.numberOfScanlines = 40;
        this.pointsPerScanline = 40;
        this.gain = 1.0f;
    }
    /**
     *  Copy constructor to instantiate a new UltrasoundProbeConfiguration from another.
     *
     *  @param config The other UltrasoundProbeConfiguration object.
     *  @throw ArgumentNullException
     */
    public UltrasoundProbeConfiguration(UltrasoundProbeConfiguration config)
    {
        UltrasoundDebug.Assert(null != config,
                               "Null UltrasoundProbeConfiguration used for copy constructor.",
                               this);
        this.SetPosition(config.GetPosition());
        this.SetRotation(config.GetRotation());

        this.maxDistance       = config.GetMaxScanDistance();
        this.minDistance       = config.GetMinScanDistance();
        this.arcSizeInDegrees  = config.GetArcSizeInDegrees();
        this.pointsPerScanline = config.GetPointsPerScanline();
        this.numberOfScanlines = config.GetNumberOfScanlines();
        this.gain = config.GetGain();
    }
コード例 #19
0
    /**
     *  Populates an UltrasoundScanData with scan data for this frame.
     *  @param data The object into which to put the scan data.
     */
    public void PopulateData(ref UltrasoundScanData data)
    {
        OnionLogger.globalLog.PushInfoLayer("HORAYProbe populating data");
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != data,
                               "Null data object passed to HorayProbe's PopulateData method.",
                               this);
#endif
        EstablishScanningPlane(ref data);
        IList <GameObject> culledOrganList = culler.HitableOrgansOnScanlines(data.GetScanlines(),
                                                                             data.GetProbeConfig());
        ScanPointsForOrgans(ref data, culledOrganList);

        OnionLogger.globalLog.PopInfoLayer();
    }
コード例 #20
0
    /// Use this for initialization
    void Start()
    {
        initialPosition = this.transform.position;
        initialRotation = this.transform.rotation;

        skin = GameObject.FindGameObjectWithTag("Skin");

        UltrasoundDebug.Assert(null != bodyPivotPoint, "Body object not set!", this);
        UltrasoundDebug.Assert(null != skin,
                               "Could not find Skin object. Did you forget to tag it?",
                               this);
        UltrasoundDebug.Assert(null != skin.collider,
                               "The skin object does not seem to have a collider.",
                               this);
    }
    /**
     *  Instantiate a new HorayProbeOutput.
     *	@param gameObject The probe from which data will be transmitted.
     *	@throw ArgumentException If the probe object does not have the correct components.
     */
    public HorayProbeOutput(GameObject gameObject)
    {
        UltrasoundDebug.Assert(null != gameObject,
                               "Null GameObject passed to HorayProbeOutput constructor.",
                               this);

        if (null == gameObject.GetComponent <HorayBehavior>())
        {
            string str =
                "The probe object used to instantiate this output " +
                "does not have the required HorayBehavior script.";
            Debug.LogError(str);
        }
        this.probeGameObject = gameObject;
        probe = new HorayProbe(probeGameObject, this);
    }
コード例 #22
0
    /**
     *	Instantiate a new HorayOrganCuller.
     */
    public HorayOrganCuller()
    {
        currentVisibleOrgans = new SortedDictionary <float, GameObject>();
        reverseMapLookup     = new Dictionary <GameObject, float>();

        allOrgansInScene = new List <GameObject>();
        object[] allGameObjectsInScene = GameObject.FindObjectsOfType <GameObject>();

        foreach (Object anObject in allGameObjectsInScene)
        {
            GameObject gameObject = (GameObject)anObject;
            if (null != gameObject.GetComponent <HorayMaterialProperties>())
            {
                allOrgansInScene.Add(gameObject);
            }
        }
        UltrasoundDebug.Assert(allOrgansInScene.Count > 0,
                               "No valid HORAY organs were found.",
                               this);
    }
コード例 #23
0
    /**
     *	Instantiate a new HorayProbe.
     *	@param probe The GameObject that represents the scanning array.
     *	@param output An IProbeOutput to pass information to.
     */
    public HorayProbe(GameObject probe, IProbeOutput output)
    {
#if UNITY_EDITOR
        UltrasoundDebug.Assert(null != probe,
                               "Null probe GameObject passed to HorayProbe constructor.",
                               this);
        UltrasoundDebug.Assert(null != output,
                               "Null IProbeOutput passed to HorayProbe constructor.",
                               this);
        if (null == probe.GetComponent <HorayBehavior>())
        {
            string str =
                "The probe object used to instantiate this class " +
                "does not have the required HorayBehavior script.";
            Debug.LogError(str);
        }
#endif
        this.probeGameObject = probe;
        this.output          = output;
        this.culler          = new HorayOrganCuller();
    }
    /// Initialization when the scene is started. This sets up the source of dynamic texture generation for the display.
    /// Based on the current choice of DisplayModes, the DisplayTexturePipelineFactory will provide the appropriate
    /// ITextureSource.
    void Start()
    {
        OnionLogger.globalLog.PushInfoLayer("Initializing Display");

        texture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGB24, false);
        this.renderer.material.mainTexture = texture;

        switch (displayMode)
        {
        case (DisplayModes.HORAY):
            textureSource = DisplayTexturePipelineFactory.BuildStandardHORAYConfig();
            break;

        case (DisplayModes.InvHORAY):
            textureSource = DisplayTexturePipelineFactory.BuildBlackOnWhiteHORAYConfig();
            break;

        case (DisplayModes.FakeTexture):
            textureSource = DisplayTexturePipelineFactory.BuildFakeTextureSource();
            break;

        case (DisplayModes.FakeImage):
            textureSource = DisplayTexturePipelineFactory.BuildWithFakeImageSource();
            break;

        case (DisplayModes.FakeProbeOutput):
            textureSource = DisplayTexturePipelineFactory.BuildWithFakeBModeProbeOutput();
            break;

        case (DisplayModes.GaussianBlurTest):
            textureSource = DisplayTexturePipelineFactory.BuildWithGaussianBlurImageSource();
            break;

        default:
            UltrasoundDebug.Assert(false, "Unknown display mode!", this);
            break;
        }
        OnionLogger.globalLog.PopInfoLayer();
    }
コード例 #25
0
    /// Checks an individual scanline for valid organs.
    /// @param scanline The UltrasoundScanline being tested
    /// @return A list of organs that are on the scanline.
    private IList <GameObject> ValidOrgansOnScanline(UltrasoundScanline scanline)
    {
        IList <UltrasoundPoint> points = scanline.GetPoints();

#if UNITY_EDITOR
        UltrasoundDebug.Assert(points.Count > 0, "Zero points on scanline", this);
#endif
        Vector3            terminalPoint   = points[points.Count - 1].GetWorldSpaceLocation();
        Ray                ray             = new Ray(scanline.origin, terminalPoint - scanline.origin);
        float              raycastDistance = (terminalPoint - scanline.origin).magnitude;
        RaycastHit[]       hits            = Physics.RaycastAll(ray, raycastDistance);
        IList <GameObject> validOrgans     = new List <GameObject>();
        foreach (RaycastHit hit in hits)
        {
            if (allOrgansInScene.Contains(hit.collider.gameObject))
            {
                validOrgans.Add(hit.collider.gameObject);
            }
        }

        return(validOrgans);
    }
    /**
     *  Draw a single UltrasoundPoint into a color buffer.
     *  @param point The UltrasoundPoint data to draw.
     *  @param index The index in the buffer at which to place the pixel.
     *  @param bitmap The bitmap into which to draw the pixel.
     */
    protected virtual void DrawPoint(UltrasoundPoint point, int index, ref ColorBitmap bitmap)
    {
        long bufferSize = bitmap.colors.Length;

#if UNITY_EDITOR
        UltrasoundDebug.Assert(index < bufferSize && index >= 0,
                               string.Format("{0} should be in the interval[0, {1})", index, bufferSize),
                               this);
        UltrasoundDebug.Assert(point.GetBrightness() >= 0f && point.GetBrightness() <= 1f,
                               string.Format("Pixel brightness {0} should be in the interval [0,1]",
                                             point.GetBrightness()),
                               this);
#endif
        if (index >= bufferSize || index < 0)
        {
            return;
        }

        Color pointColor = drawColor;
        // minimum pixel brightness of 0.1f
        pointColor          *= Mathf.Max(0.1f, point.GetBrightness());
        bitmap.colors[index] = pointColor;
    }
 public void AddPostProcessingEffect(IImagePostProcessor effect)
 {
     UltrasoundDebug.Assert(null != effect, "Null effect added to ImageSource", this);
     imageEffects.Add(effect);
 }
 /**
  * Instantiate a new TextureSource.
  * @param imageSource The IImageSource providing a bitmap every frame.
  */
 public TextureSource(IImageSource imageSource)
 {
     UltrasoundDebug.Assert(null != imageSource, "Null ImageSource used in constructor", this, true);
     this.imageSource = imageSource;
 }
コード例 #29
0
    /**
     *	Checks a list of scanlines for visible objects. If an object on the scanlines is hit by
     *	a raycast, it is marked as visible (added to the currentVisibleOrgans and reverseMapLookup dictionaries).
     *
     *	Visible objects are given an expiration time (EXPIRATION_TIME_IN_SECONDS after the present).
     *	If an object that is already known to be visible is detected, its expiration time is reset.
     *
     *	This method only checks a few scanlines each frame (as defined in the SCANLINES_PER_FRAME constant).
     *	We attempt to check scanlines that are spread apart, rather than checking sequentially, so that new
     *	objects are detected more quickly. For example, if there are 10 scanlines in total, it is more helpful
     *	to check scanlines (0, 3, 6, 9) in a single frame than to check (0, 1, 2, 3).
     *
     *	@param scanlines The set of all scanlines sent out by this probe.
     */
    private void CheckScanlines(IList <UltrasoundScanline> scanlines)
    {
        int scanlineCount = scanlines.Count;

#if UNITY_EDITOR
        UltrasoundDebug.Assert(scanlineCount > 0, "Zero scanlines passed to culler!", this);
#endif

        // Rather than checking scanlines sequentially, we try to check them somewhat uniformly so
        // that new objects are more likely to be noticed within a few frames. Therefore, we generate
        // a number smaller than scanlineCount, but one that is NOT a divisor of scanlineCount.
        int scanlineStepSize = (scanlineCount / (SCANLINES_PER_FRAME + 1));
        while (scanlineCount % scanlineStepSize == 0)
        {
            scanlineStepSize++;
        }

        for (int i = 0; i < SCANLINES_PER_FRAME; ++i)
        {
            scanlineIndex = (scanlineIndex + scanlineStepSize) % scanlineCount;

            IList <GameObject> organsToAdd = (ValidOrgansOnScanline(scanlines[scanlineIndex]));
            foreach (GameObject organ in organsToAdd)
            {
#if UNITY_EDITOR
                UltrasoundDebug.Assert(null != organ.GetComponent <HorayMaterialProperties>(),
                                       "organ without HORAY properties was added",
                                       this);
#endif
                if (!reverseMapLookup.ContainsKey(organ))
                {
                    // Store in a temp variable since Time.time may change between assignments below.
                    float keyTime = Time.time;

                    /* On faster machines, two raycasts may be completed before Time.time has been measureably
                     * incremented. In this situation, we can nudge the time forward a little bit to avoid a
                     * key collision.
                     */
                    while (currentVisibleOrgans.ContainsKey(keyTime))
                    {
                        keyTime += 0.01f;
                    }

                    currentVisibleOrgans.Add(keyTime, organ);
                    reverseMapLookup.Add(organ, keyTime);
                }

                else
                {
                    float oldTime = float.NegativeInfinity;
                    reverseMapLookup.TryGetValue(organ, out oldTime);
#if UNITY_EDITOR
                    UltrasoundDebug.Assert(oldTime > float.NegativeInfinity,
                                           string.Format("Organ {0} wasn't in reverse map", organ.name),
                                           this);
#endif
                    currentVisibleOrgans.Remove(oldTime);
                    reverseMapLookup.Remove(organ);
                    float newTime = Time.time;

                    /* On faster machines, two raycasts may be completed before Time.time has been measureably
                     * incremented. In this situation, we can nudge the time forward a little bit to avoid a
                     * key collision.
                     */
                    while (currentVisibleOrgans.ContainsKey(newTime))
                    {
                        newTime += 0.01f;
                    }

                    currentVisibleOrgans.Add(newTime, organ);
                    reverseMapLookup.Add(organ, newTime);
                }
            }
        }
    }