예제 #1
0
        /// <summary>
        /// calculates the normal of the plane in which hooks for a certain curve should bend.
        /// </summary>
        /// <param name="curve">
        /// hook normal is calculated for this curve
        /// </param>
        /// /// <param name="face">
        /// face used as a reference for finding the hook normal, together with the curve
        /// </param>
        /// /// <param name="iEnd">
        /// specifies the end at which the hook normal to be calculated
        /// </param>
        /// <returns> the plane normal that was calculated</returns>
        private XYZ computeNormal(Curve curve, TargetFace face, int iEnd)
        {
            XYZ curveTangent        = curve.ComputeDerivatives(iEnd, true).BasisX.Normalize();
            XYZ refPoint            = curve.GetEndPoint(iEnd);
            IntersectionResult proj = face.Face.Project(face.Transform.Inverse.OfPoint(refPoint));

            if (proj == null)
            {
                return(null);
            }
            return(face.Face.ComputeNormal(proj.UVPoint).Negate().CrossProduct(curveTangent));
        }
예제 #2
0
        /// <summary>
        /// Function used to intersect 2 faces to obtain an offseted curve.
        /// </summary>
        /// <param name="firstFace"></param>
        /// <param name="secondFace"></param>
        /// <returns></returns>
        private Curve getOffsetCurveAtIntersection(TargetFace firstFace, TargetFace secondFace)
        {
            Curve firstCurve;
            FaceIntersectionFaceResult result = firstFace.Face.Intersect(secondFace.Face, out firstCurve);

            // if faces do not intersect, or do not return a Line, then consider the input invalid and return error
            if (result == FaceIntersectionFaceResult.NonIntersecting || !(firstCurve is Line))
            {
                return(null);
            }
            XYZ       pointOnCurve    = firstCurve.Evaluate(0, true);
            XYZ       FirstOffsetVec  = firstFace.Face.ComputeNormal(firstFace.Face.Project(pointOnCurve).UVPoint).Normalize();
            XYZ       SecondOffsetVec = secondFace.Face.ComputeNormal(secondFace.Face.Project(pointOnCurve).UVPoint).Normalize();
            XYZ       offsetVec       = (FirstOffsetVec * firstFace.Offset) + (SecondOffsetVec * secondFace.Offset);
            Transform offsetTrf       = Transform.CreateTranslation(offsetVec);

            return(firstCurve.CreateTransformed(offsetTrf.Multiply(firstFace.Transform)));
        }
예제 #3
0
        /// <summary>
        /// function that finds the closest face to a specified end of a curve of the direction of the curve
        /// </summary>
        /// <param name="curve">
        /// curve used to find the closest face
        /// </param>
        /// /// <param name="faces">
        /// list of faces that are parsed to find the closest one
        /// </param>
        /// /// <param name="iEnd">
        /// input parameter specifying the curve end for wich the search is taking place
        /// </param>
        /// <returns> the FaceTrf that is closest to the curve end</returns>
        private TargetFace searchForFace(Curve curve, List <TargetFace> faces, int iEnd)
        {
            TargetFace bestFace    = new TargetFace();
            double     minDistance = Double.MaxValue;
            // create tangent to find intersections on the curve's extension
            Line tangent = Line.CreateUnbound(curve.GetEndPoint(iEnd), curve.ComputeDerivatives(iEnd, true).BasisX.Normalize() * (iEnd == 0 ? -1 : 1));

            // iterate through faces and keep the face closest to the specified end of the curve
            foreach (TargetFace hostFace in faces)
            {
                IntersectionResultArray results;
                // intersect tangent to find faces outside the curve
                if (hostFace.Face.Intersect(tangent.CreateTransformed(hostFace.Transform.Inverse), out results) == SetComparisonResult.Overlap)
                {
                    foreach (IntersectionResult intersect in results)
                    {
                        double distance = hostFace.Transform.OfPoint(intersect.XYZPoint).DistanceTo(curve.GetEndPoint(iEnd));
                        // if intersection is not on the curve( "behind" the tangent origin, considering the direction),
                        // and the distance from the end of the curve to the face is the smallest, keep face.
                        double param = tangent.Project(hostFace.Transform.OfPoint(intersect.XYZPoint)).Parameter;
                        if (param >= 0 && distance < minDistance)
                        {
                            bestFace    = hostFace;
                            minDistance = distance;
                            continue;
                        }
                    }
                }
                if (hostFace.Face.Intersect(curve.CreateTransformed(hostFace.Transform.Inverse), out results) == SetComparisonResult.Overlap)
                {
                    foreach (IntersectionResult intersect in results)
                    {
                        double distance = hostFace.Transform.OfPoint(intersect.XYZPoint).DistanceTo(curve.GetEndPoint(iEnd));
                        if (distance < minDistance)
                        {
                            bestFace    = hostFace;
                            minDistance = distance;
                            continue;
                        }
                    }
                }
            }
            return(bestFace);
        }
예제 #4
0
        /// <summary>
        /// Function used to adjust the computed geometry information of the rebar element and has two logical parts:
        ///  - Selection of structural faces for creation of Start of Bar and End of Bar Constraints when needed
        ///  (Constraints created here are visible and modifiable in the Graphical Constraints Manager in native Revit)
        ///      The constraint search is done by listing all the faces from the structural pointed to by the FirstHandle constraint
        ///      and then picking the face that has the closest intersection point with either the curves in the rebar, or their extensions
        ///  - Adjustments are done to the start/end of the bars according to the corresponding constraints
        ///      Each bar will be lengthened to the intersection point of the tangent in the curve's specified end with the corresponding constraint face,
        ///      or it will be shortened to the intersection point of the curve itself with the corresponding constraint face.
        ///  - Hook normals for each bar are calculated for the newly modified curves.
        ///  This function is called after the successful execution of GenerateCurves
        /// </summary>
        /// <param name="data">Class used to pass information from the external application to the internal Rebar Element.
        /// Interfaces with the Rebar Element and exposes information needed for Constraint creation, face searching, and
        /// receives the result of the Start/End constraint calculation.
        /// updates are done on the element after the entire function finished successfully.
        /// </param>
        /// <returns> true if execution was completed successfully, false otherwise</returns>
        public bool TrimExtendCurves(RebarTrimExtendData data)
        {
            if (getSelectedCurveElement(getCurrentRebar(data.GetRebarUpdateCurvesData()), data.GetRebarUpdateCurvesData()) != null)
            {
                return(true);
            }

            // extract the curves from the element.
            IList <Curve> allbars = new List <Curve>();

            for (int ii = 0; ii < data.GetRebarUpdateCurvesData().GetBarsNumber(); ii++)
            {
                allbars.Add(data.GetRebarUpdateCurvesData().GetBarGeometry(ii)[0]);
            }
            // Place for caching the faces of the host used in constraint search.
            List <TargetFace> hostFaces = new List <TargetFace>();

            // repeat process for each end of the Rebar.
            for (int iBarEnd = 0; iBarEnd < 2; iBarEnd++)
            {
                List <TargetFace> faces = new List <TargetFace>();
                // get current Start/End constraint
                RebarConstraint constraint = (iBarEnd == 0) ? data.GetRebarUpdateCurvesData().GetStartConstraint() :
                                             data.GetRebarUpdateCurvesData().GetEndConstraint();

                //if no constraint present, then search for a new one
                if (constraint == null)
                {
                    if (hostFaces.Count <= 0)// fetch the faces of the structural used for searching constraints.
                    {
                        // used compute references to true to make sure we can create constraints with the faces we find
                        Options geomOptions = new Options();
                        geomOptions.ComputeReferences = true;
                        // the host structural is considered the first structural in the first constraint
                        GeometryElement elemGeometry = data.GetRebarUpdateCurvesData().GetCustomConstraints()[0].GetTargetElement(0).get_Geometry(geomOptions);
                        if (elemGeometry == null)
                        {
                            return(false);
                        }
                        hostFaces = getFacesFromElement(elemGeometry);
                    }

                    // for each bar try to find the closest face that intersects with it, or its extension, at the specified end
                    for (int idx = 0; idx < allbars.Count; idx++)
                    {
                        faces.Add(searchForFace(allbars[idx], hostFaces, iBarEnd));
                    }

                    // gather valid references for constraint creation
                    List <Reference> refs = new List <Reference>();
                    foreach (TargetFace face in faces)
                    {
                        if (face.Face.Reference != null && !refs.Contains(face.Face.Reference))
                        {
                            refs.Add(face.Face.Reference);
                        }
                    }

                    // if we have any valid references, we create the constraint for the specified bar end.
                    if (refs.Count > 0)
                    {
                        if (iBarEnd == 0)
                        {
                            data.CreateStartConstraint(refs, false, 0.0);
                        }
                        else
                        {
                            data.CreateEndConstraint(refs, false, 0.0);
                        }
                    }
                }
                else// if constraint is present, extract needed information to calculate trim/extend
                {
                    for (int nTarget = 0; nTarget < constraint.NumberOfTargets; nTarget++)
                    {
                        var  trf             = Transform.Identity;
                        Face constrainedFace = constraint.GetTargetHostFaceAndTransform(nTarget, trf);
                        if (constrainedFace == null)
                        {
                            continue;
                        }
                        double dfOffset;
                        if (getOffsetFromConstraintAtTarget(data.GetRebarUpdateCurvesData(), constraint, 0, out dfOffset))
                        {
                            faces.Add(new TargetFace()
                            {
                                Face = constrainedFace, Transform = trf, Offset = dfOffset
                            });
                        }
                    }
                }

                // for each bar, find out where it intersects with the selected faces and replace the original curve with a new one that is shorter or longer.
                // first search for extension intersection (use tangent curve in the end point of the curve), then search for actual curve intersection
                for (int idx = 0; idx < allbars.Count; idx++)
                {
                    XYZ   intersection;
                    Curve barCurve = allbars[idx];
                    if (!(barCurve is Line))// this code only deals with input curves that are straight lines
                    {
                        return(false);
                    }
                    Line   tangent  = Line.CreateUnbound(barCurve.GetEndPoint(iBarEnd), barCurve.ComputeDerivatives(iBarEnd, true).BasisX.Normalize() * (iBarEnd == 0 ? -1 : 1));
                    double dfOffset = 0.0;
                    if (getIntersection(tangent, faces, out intersection, out dfOffset) || getIntersection(barCurve, faces, out intersection, out dfOffset))
                    {
                        Curve newCurve = null;
                        try
                        {
                            XYZ barDir = (barCurve.GetEndPoint(1) - barCurve.GetEndPoint(0)).Normalize();
                            if ((iBarEnd == 0))
                            {
                                newCurve = Line.CreateBound(intersection - barDir * dfOffset, barCurve.GetEndPoint(1));
                            }
                            else
                            {
                                newCurve = Line.CreateBound(barCurve.GetEndPoint(0), intersection + barDir * dfOffset);
                            }
                        }
                        catch { }
                        // if new curve available, replace the old one.
                        if (newCurve != null)
                        {
                            allbars[idx] = newCurve;
                        }
                    }
                }
            }


            // get the FirstHandle constraint and extract the target face to use in determining the hook orientation for each bar
            TargetFace firstFace = new TargetFace();
            IList <RebarConstraint> constraints = data.GetRebarUpdateCurvesData().GetCustomConstraints();

            foreach (RebarConstraint constraint in constraints)
            {
                if ((BarHandle)constraint.GetCustomHandleTag() == BarHandle.FirstHandle)
                {
                    Transform tempTrf = Transform.Identity;
                    double    dfOffset;
                    if (!getOffsetFromConstraintAtTarget(data.GetRebarUpdateCurvesData(), constraint, 0, out dfOffset))
                    {
                        return(false);
                    }
                    firstFace = new TargetFace()
                    {
                        Face = constraint.GetTargetHostFaceAndTransform(0, tempTrf), Transform = tempTrf, Offset = dfOffset
                    };
                    break;
                }
            }

            // add each curve as separate bar in the set.
            for (int ii = 0; ii < allbars.Count; ii++)
            {
                List <Curve> barCurve = new List <Curve>();
                barCurve.Add(allbars[ii]);
                data.AddBarGeometry(barCurve);
                // hook normals are reset when adding new bar geometry, so  we need to
                // set the hook normals for each bar that was modified
                for (int i = 0; i < 2; i++)
                {
                    XYZ normal = computeNormal(allbars[ii], firstFace, i);
                    if (normal != null && !normal.IsZeroLength())
                    {
                        data.GetRebarUpdateCurvesData().SetHookPlaneNormalForBarIdx(i, ii, normal);
                    }
                }
            }
            return(true);
        }
예제 #5
0
        /// <summary>
        /// Function used to compute the geometry information of the Rebar element during document regeneration.
        /// Geometry information includes:
        ///  1. Graphical representation of the Rebar or Rebar Set;
        ///  2. Hook placement;
        ///  3. Distribution Path for MRA;
        ///
        /// </summary>
        /// <param name="data">Class used to pass information from the external application to the internal Rebar Element.
        /// Interfaces with the Rebar Element and exposes information needed for geometric calculation during regeneration,
        /// such as constrained geometry, state of changed input information, etc.
        /// Receives the result of the custom constraint calculation and
        /// updates the element after the entire function finished successfully.
        /// </param>
        /// <returns> true if geometry generation was completed successfully, false otherwise</returns>
        public bool GenerateCurves(RebarCurvesData data)
        {
            // used to store the faces and transforms used in generation of curves
            TargetFace firstFace  = new TargetFace();
            TargetFace secondFace = new TargetFace();
            TargetFace thirdFace  = new TargetFace();
            //iterate through the available constraints and extract the needed information
            IList <RebarConstraint> constraints = data.GetRebarUpdateCurvesData().GetCustomConstraints();

            foreach (RebarConstraint constraint in constraints)
            {
                if (constraint.NumberOfTargets > 1)
                {
                    return(false);
                }
                Transform tempTrf  = Transform.Identity;
                double    dfOffset = 0;
                if (!getOffsetFromConstraintAtTarget(data.GetRebarUpdateCurvesData(), constraint, 0, out dfOffset))
                {
                    return(false);
                }

                switch ((BarHandle)constraint.GetCustomHandleTag())
                {
                case BarHandle.FirstHandle:
                {
                    Face face = constraint.GetTargetHostFaceAndTransform(0, tempTrf);
                    firstFace = new TargetFace()
                    {
                        Face = face, Transform = tempTrf, Offset = dfOffset
                    };
                    break;
                }

                case BarHandle.SecondHandle:
                {
                    Face face = constraint.GetTargetHostFaceAndTransform(0, tempTrf);
                    secondFace = new TargetFace()
                    {
                        Face = face, Transform = tempTrf, Offset = dfOffset
                    };
                    break;
                }

                case BarHandle.ThirdHandle:
                {
                    Face face = constraint.GetTargetHostFaceAndTransform(0, tempTrf);
                    thirdFace = new TargetFace()
                    {
                        Face = face, Transform = tempTrf, Offset = dfOffset
                    };
                    break;
                }

                default:
                    break;
                }
            }
            // check if all the input is present for the calculation, otherwise return error(false).
            if (firstFace.Face == null || secondFace.Face == null || thirdFace.Face == null)
            {
                return(false);
            }

            Rebar        thisBar       = getCurrentRebar(data.GetRebarUpdateCurvesData());
            CurveElement selectedCurve = null;

            //if a curve elem is selected, we override the geometry we get from the intersections and use the selected curve to create our bar geometries
            selectedCurve = getSelectedCurveElement(thisBar, data.GetRebarUpdateCurvesData());
            //used to store the resulting curves
            List <Curve> curves      = new List <Curve>();
            Curve        originalBar = null;
            Curve        singleBar   = getOffsetCurveAtIntersection(firstFace, secondFace);

            if (selectedCurve != null)
            {
                Transform trf = Transform.CreateTranslation(singleBar.GetEndPoint(0) - selectedCurve.GeometryCurve.GetEndPoint(0));
                originalBar = singleBar;
                singleBar   = selectedCurve.GeometryCurve.CreateTransformed(trf);
            }
            //we can't make any more bars without the first one.
            if (singleBar == null)
            {
                return(false);
            }

            // check the layout rule to see if we need to create more bars
            // for this example, any rule that is not single will generate bars in the same way,
            // creating them at an equal distance to each other, based only on number of bars
            RebarLayoutRule layout = data.GetRebarUpdateCurvesData().GetLayoutRule();

            switch (layout)
            {
            case RebarLayoutRule.Single:// first bar creation: intersect first face with second face to get a curve
                curves.Add(singleBar);
                break;

            case RebarLayoutRule.FixedNumber:
            case RebarLayoutRule.NumberWithSpacing:
            case RebarLayoutRule.MaximumSpacing:
            case RebarLayoutRule.MinimumClearSpacing:
                curves.Add(singleBar);
                Curve lastBar = getOffsetCurveAtIntersection(firstFace, thirdFace);// create last bar

                // keep the curves pointing in the same direction
                var firstBar = (selectedCurve != null) ? originalBar : singleBar;
                if (lastBar == null || !alignBars(ref firstBar, ref lastBar))
                {
                    return(false);
                }
                if (selectedCurve != null)
                {
                    Transform trf = Transform.CreateTranslation(lastBar.GetEndPoint(0) - selectedCurve.GeometryCurve.GetEndPoint(0));
                    lastBar = selectedCurve.GeometryCurve.CreateTransformed(trf);
                }

                if (!generateSet(singleBar, lastBar, layout,
                                 data.GetRebarUpdateCurvesData().GetBarsNumber(),
                                 data.GetRebarUpdateCurvesData().Spacing, ref curves, selectedCurve == null ? null : selectedCurve.GeometryCurve))
                {
                    return(false);
                }
                curves.Add(lastBar);
                break;

            default:
                break;
            }

            // check if any curves were created
            if (curves.Count <= 0)
            {
                return(false);
            }

            // create the distribution path for the bars that were created;
            // one single bar will not have a distribution path.
            List <Curve> distribPath = new List <Curve>();

            for (int ii = 0; ii < curves.Count - 1; ii++)
            {
                distribPath.Add(Line.CreateBound(curves[ii].Evaluate(0.5, true), curves[ii + 1].Evaluate(0.5, true)));
            }
            // set distribution path if we have a path created
            if (distribPath.Count > 0)
            {
                data.SetDistributionPath(distribPath);
            }

            // add each curve as separate bar in the set.
            for (int ii = 0; ii < curves.Count; ii++)
            {
                List <Curve> barCurve = new List <Curve>();
                barCurve.Add(curves[ii]);
                data.AddBarGeometry(barCurve);

                // set the hook normals for each bar added
                // important!: hook normals set here will be reset if bar geometry is changed on TrimExtendCurves
                // so they need to be recalculated then.
                for (int i = 0; i < 2; i++)
                {
                    XYZ normal = computeNormal(curves[ii], firstFace, i);
                    if (normal != null && !normal.IsZeroLength())
                    {
                        data.GetRebarUpdateCurvesData().SetHookPlaneNormalForBarIdx(i, ii, normal);
                    }
                }
            }
            return(true);
        }