CADability dotNET

Here you find code examples for using CADability.

C#: Position an IGeoObject on the surface of a Solid or Face

This sample code implements an Action derived from ConstructAction. The Action requires three inputs: A filename for a Picture, a location (preferrable but not necesarrily on a surface) and an optional scaling factor for the Picture. When the filename is specified and the mouse cursor is moved, the bitmap will be moved accordingly. If the mouse cursor hovers above a Surface, the bitmap is positioned parallel to that surface in that point with a small offset above the surface.

Source Code

using System;
using System.Collections.Generic;
using System.Text;
using CADability.UserInterface;
using CADability.GeoObject;
using CADability.Shapes;
using CADability.Curve2D;
using CADability.Attribute;
using Wintellect.PowerCollections;
using CADability.Actions;
using CADability;

namespace TestCADability
    /// <summary>
    /// Sample code for a CADability Action which places a bitmap picture object onto a surface
    /// of some solid object. To use this Action call Frame.SetAction
    /// </summary>
    class PositionObjectAction : ConstructAction
        StringInput fileNameInput; // input field for the filename of the bitmap
        GeoPointInput positionInput; // input field for the position of the bitmap
        DoubleInput scalingFactorInput; // optional input field for a scaling factor for the bitmap
        Picture picture; // the picture object beeing placed
        string fileName; // the filename for the bitmap
        double scalingFactor; // the scaling factor
        GeoPoint location; // the location of the picture object

        /// <summary>
        /// Must be overriden, returns some ID for the action
        /// </summary>
        /// <returns>The ID</returns>
        public override string GetID()
            return "PositionObjectAction";

        /// <summary>
        /// Overrides ConstructAction.OnSetAction. Provides the input fields and some initialsation
        /// </summary>
        public override void OnSetAction()
            // Create and initialize the filename input field
            fileNameInput = new StringInput("PositionBitmap.Filename"); // Resource ID must be defined in StringTable
            fileNameInput.IsFileNameInput = true; // to enable the openfile dialog
            // you may also wnat to set fileNameInput.FileNameFilter
            fileNameInput.GetStringEvent += new StringInput.GetStringDelegate(OnGetFileName);
            fileNameInput.SetStringEvent += new StringInput.SetStringDelegate(OnSetFileName);
            fileName = ""; // initial filename is empty

            // Create and initialize the position input field
            positionInput = new GeoPointInput("PositionBitmap.Position"); // Resource ID must be defined in StringTable
            positionInput.GetGeoPointEvent += new GeoPointInput.GetGeoPointDelegate(OnGetPosition);
            positionInput.SetGeoPointExEvent += new GeoPointInput.SetGeoPointExDelegate(OnSetPosition);

            // Create and initialize the scaling factor input field
            scalingFactorInput = new DoubleInput("PositionBitmap.Scale"); // Resource ID must be defined in StringTable
            scalingFactorInput.GetDoubleEvent += new DoubleInput.GetDoubleDelegate(OnGetScalingFactor);
            scalingFactorInput.SetDoubleEvent += new DoubleInput.SetDoubleDelegate(OnSetScalingFactor);
            scalingFactorInput.Optional = true; // optinal input
            scalingFactorInput.ForwardMouseInputTo = positionInput; // no mouse input, forward to position input
            scalingFactor = 1.0; // default scaling factor

            // picture must exist prior to SetInput
            // because picture.Location is required by positionInput
            picture = Picture.Construct(); 

            // Define the input fields for the ConstructAction
            base.SetInput(fileNameInput, positionInput, scalingFactorInput);
            base.OnSetAction(); // Default implementation must be called

            // force the snapmode to include SnapToFaceSurface
            base.Frame.SnapMode |= SnapPointFinder.SnapModes.SnapToFaceSurface;

        /// <summary>
        /// Will be called when some number is entered into the scaling factor input field
        /// </summary>
        /// <param name="val">The new value</param>
        /// <returns>true, if accepted</returns>
        bool OnSetScalingFactor(double val)
            if (val > 0.0) // accept only positive values
                scalingFactor = val; // save value
                return true;
            return false;

        /// <summary>
        /// Called by the scaling factor input field to determine which number to display
        /// </summary>
        /// <returns></returns>
        double OnGetScalingFactor()
            return scalingFactor;

        /// <summary>
        /// Called when the position input is changed
        /// </summary>
        /// <param name="p">The new location point</param>
        /// <param name="didSnap">Information on point snapping</param>
        /// <returns>true, if point is accepted</returns>
        bool OnSetPosition(GeoPoint p, SnapPointFinder.DidSnapModes didSnap)
            location = p;
            // default vectors for the two sides of the bitmap
            GeoVector dirWidth = GeoVector.XAxis;
            GeoVector dirHeight = GeoVector.ZAxis;
            if (didSnap == SnapPointFinder.DidSnapModes.DidSnapToFaceSurface)
            {   // if there was a snap on the surface of a face we want to center the picture object
                // there. We also want to make it parallel to the surface at this point and stand upright.
                Face fc = base.LastSnapObject as Face; // this object was involved in the snapping
                if (fc != null) // should always be the case
                    GeoPoint2D pos = fc.Surface.PositionOf(location); // position in the surface u/v system
                    GeoVector normal = fc.Surface.GetNormal(pos); // normal vector at this position
                    Projection projection = base.CurrentMouseView.Projection; // the projection of the view
                    if (projection != null)
                    {   // make sure that the normal vector points away from the user.
                        // On faces not in a solid both sides of the face are displayed and you don't know
                        // on which side you are
                        if (projection.Direction * normal < 0.0) normal = -normal;
                    location = location - 0.001 * normal.Normalized; // moves the location point a little bit 
                        // in the direction of the normal vector to the user so
                        // that the bitmap hoovers a little above the surface to avoid display artefacts
                    if (Precision.SameDirection(normal, GeoVector.ZAxis, false))
                    {   // the surface is parallel to the x/y plane: orient the bitmap to the x/y axis
                        dirWidth = GeoVector.XAxis;
                        dirHeight = GeoVector.YAxis;
                    {   // some arbitrary surface direction: calculate the base direction of the bitmap to
                        // be parallel to the x/y plane and the up direction rectangular to the base and the normal.
                        // this makes the bitmap appear upright (in the sense of the z-axis)
                        dirWidth = normal ^ GeoVector.ZAxis;
                        dirHeight = dirWidth ^ normal;
                        dirWidth.Norm(); // we need normalized vectors
            double w = 1; // default width and height if there is no bitmap yet
            double h = 1;
            if (picture.Bitmap != null)
            {   // width and height of the bitmap
                w = picture.Bitmap.PhysicalDimension.Width;
                h = picture.Bitmap.PhysicalDimension.Height;
            // provide the picture object with proper aspect ratio and scaling according to the scaling factor
            picture.DirectionWidth = w * scalingFactor * dirWidth;
            picture.DirectionHeight = h * scalingFactor * dirHeight;
            // set the location of the object so that the center of the bitmap occures at the position of location
            // Since picture.Location is the lower left point of the object we must move it left and down
            // with half of it's size
            picture.Location = location - 0.5 * picture.DirectionWidth - 0.5 * picture.DirectionHeight;
            return true; // this was OK

        /// <summary>
        /// Called by the position input field to get the current location
        /// </summary>
        /// <returns></returns>
        GeoPoint OnGetPosition()
            return location;

        /// <summary>
        /// Called by the filename input field when the filename changes
        /// </summary>
        /// <param name="val">The new file name</param>
        void OnSetFileName(string val)
            fileName = val; // save the name
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(fileName); // load the bitmap
            picture.Bitmap = bmp; // set the bitmap to the picture object
            base.ActiveObject = picture; // set the active object:
            // now that we have a bitmap we set this object as the active object, which means
            // that is will be displayed while the location is positioned and at the end of the
            // action it will automatically be inserted into the model

        /// <summary>
        /// Called by the filename input field when the filename string is required
        /// </summary>
        /// <returns>the filename</returns>
        string OnGetFileName()
            return fileName;