/// <summary>
      /// Execute a Boolean operation, and catch the exception.
      /// </summary>
      /// <param name="id">The id of the object demanding the Boolean operation.</param>
      /// <param name="secondId">The id of the object providing the second solid.</param>
      /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param>
      /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param>
      /// <param name="opType">The Boolean operation type.</param>
      /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift.  Can be null.</param>
      /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns>
      public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection)
      {
         // Perform default operations if one of the arguments is null.
         if (firstSolid == null || secondSolid == null)
         {
            if (firstSolid == null && secondSolid == null)
               return null;

            switch (opType)
            {
               case BooleanOperationsType.Union:
                  {
               if (firstSolid == null)
                  return secondSolid;

               return firstSolid;
            }
               case BooleanOperationsType.Difference:
            {
               if (firstSolid == null)
                  return null;

               return firstSolid;
            }
               default:
            // for .Intersect
            return null;
         }
         }

         Solid resultSolid = null;

         try
         {
               Solid secondOperand = secondSolid;
               resultSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType);
            }
         catch (Exception ex)
         {
            Importer.TheLog.LogError(id, ex.Message, false);
            resultSolid = firstSolid;
         }

         if (SolidValidator.IsValidGeometry(resultSolid))
            return resultSolid;

         Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false);
         return firstSolid;
      }
Ejemplo n.º 2
0
        /// <summary>
        /// Execute a Boolean operation, and catch the exception.
        /// </summary>
        /// <param name="id">The id of the object demanding the Boolean operation.</param>
        /// <param name="secondId">The id of the object providing the second solid.</param>
        /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="opType">The Boolean operation type.</param>
        /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns>
        public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType)
        {
            if (firstSolid == null || secondSolid == null)
            {
                if (firstSolid == null && secondSolid == null)
                    return null;

                if (opType == BooleanOperationsType.Union)
                {
                    if (firstSolid == null)
                        return secondSolid;

                    return firstSolid;
                }

                if (opType == BooleanOperationsType.Difference)
                {
                    if (firstSolid == null)
                        return null;

                    return firstSolid;
                }

                // for .Intersect
                return null;
            }

            Solid resultSolid = null;

            try
            {
                resultSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondSolid, opType);
            }
            catch (Exception ex)
            {
                IFCImportFile.TheLog.LogError(id, ex.Message, false);
                resultSolid = firstSolid;
            }

            if (SolidValidator.IsValidGeometry(resultSolid))
                return resultSolid;

            IFCImportFile.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false);
            return firstSolid;
        }
      /// <summary>
      /// Execute a Boolean operation, and catch the exception.
      /// </summary>
      /// <param name="id">The id of the object demanding the Boolean operation.</param>
      /// <param name="secondId">The id of the object providing the second solid.</param>
      /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param>
      /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param>
      /// <param name="opType">The Boolean operation type.</param>
      /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift.  Can be null.</param>
      /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns>
      public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection)
      {
         const double footToMillimeter = 1.0 / 304.8;

         // Perform default operations if one of the arguments is null.
         if (firstSolid == null || secondSolid == null)
         {
            if (firstSolid == null && secondSolid == null)
               return null;

            switch (opType)
            {
               case BooleanOperationsType.Union:
                  {
               if (firstSolid == null)
                  return secondSolid;

               return firstSolid;
            }
               case BooleanOperationsType.Difference:
            {
               if (firstSolid == null)
                  return null;

               return firstSolid;
            }
               default:
            // for .Intersect
            return null;
         }
         }

         Solid resultSolid = null;
         bool failedAllAttempts = true;

         // We will attempt to do the Boolean operation 3 times:
         // 1st pass: With the passed-in arguments.
         // 2nd pass: With a 1mm shift in a direction in suggestedShiftDirection, or +Z if suggestedShiftDirection is null
         // 3rd pass: With a 1mm shift in a direction in -suggestedShiftDirection, or -Z if suggestedShiftDirection is null
         for (int ii = 0; ii < 3; ii++)
         {
         try
         {
               resultSolid = null;

               Solid secondOperand = secondSolid;
               if (ii > 0)
               {
                  // 1 mm shift.
                  XYZ shiftDirection = (suggestedShiftDirection == null) ? new XYZ(0, 0, 1) : suggestedShiftDirection;
                  Transform secondSolidShift = Transform.CreateTranslation(shiftDirection * ((ii == 1) ? footToMillimeter : -footToMillimeter));
                  secondOperand = SolidUtils.CreateTransformed(secondOperand, secondSolidShift);
         }

               resultSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType);
               failedAllAttempts = false;
            }
         catch (Exception ex)
         {
               if (ii < 2)
                  continue;
               
            Importer.TheLog.LogError(id, ex.Message, false);
            resultSolid = firstSolid;
         }

         if (SolidValidator.IsValidGeometry(resultSolid))
            {
               // If we got here not on out first attempt, generate a warning, unless we got here because we gave up on our 3rd attempt.
               if (ii > 0 && !failedAllAttempts)
                  Importer.TheLog.LogWarning(id, "The second argument in the Boolean " + 
                     opType.ToString() + 
                     " operation was shifted by 1mm to allow the operation to succeed.  This may result in a very small difference in appearance.", false);
            return resultSolid;
            }
         }

         Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false);
         return firstSolid;
      }
Ejemplo n.º 4
0
        /// <summary>
        /// Return geometry for a particular representation item.
        /// </summary>
        /// <param name="shapeEditScope">The geometry creation scope.</param>
        /// <param name="lcs">Local coordinate system for the geometry, without scale.</param>
        /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param>
        /// <param name="guid">The guid of an element for which represntation is being created.</param>
        /// <returns>The created geometry.</returns>
        public IList <GeometryObject> CreateGeometry(
            IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid)
        {
            IList <GeometryObject> firstSolids = FirstOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid);

            if (firstSolids != null)
            {
                foreach (GeometryObject potentialSolid in firstSolids)
                {
                    if (!(potentialSolid is Solid))
                    {
                        Importer.TheLog.LogError((FirstOperand as IFCRepresentationItem).Id, "Can't perform Boolean operation on a Mesh.", false);
                        return(firstSolids);
                    }
                }
            }

            IList <GeometryObject> secondSolids = null;

            if ((firstSolids != null || BooleanOperator == IFCBooleanOperator.Union) && (SecondOperand != null))
            {
                try
                {
                    using (IFCImportShapeEditScope.BuildPreferenceSetter setter =
                               new IFCImportShapeEditScope.BuildPreferenceSetter(shapeEditScope, IFCImportShapeEditScope.BuildPreferenceType.ForceSolid))
                    {
                        // Before we process the second operand, we are going to see if there is a uniform material set for the first operand
                        // (corresponding to the solid in the Boolean operation).  We will try to suggest the same material for the voids to avoid arbitrary
                        // setting of material information for the cut faces.
                        IFCStyledItem firstOperandStyledItem = GetStyledItemFromOperand(FirstOperand as IFCRepresentationItem);
                        using (IFCImportShapeEditScope.IFCMaterialStack stack =
                                   new IFCImportShapeEditScope.IFCMaterialStack(shapeEditScope, firstOperandStyledItem, null))
                        {
                            secondSolids = SecondOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid);
                        }
                    }
                }
                catch (Exception ex)
                {
                    // We will allow something to be imported, in the case where the second operand is invalid.
                    // If the first (base) operand is invalid, we will still fail the import of this solid.
                    if (SecondOperand is IFCRepresentationItem)
                    {
                        Importer.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false);
                    }
                    else
                    {
                        throw ex;
                    }
                    secondSolids = null;
                }
            }

            IList <GeometryObject> resultSolids = null;

            if (firstSolids == null)
            {
                if (BooleanOperator == IFCBooleanOperator.Union)
                {
                    resultSolids = secondSolids;
                }
            }
            else if (secondSolids == null || BooleanOperator == null)
            {
                if (BooleanOperator == null)
                {
                    Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", false);
                }
                resultSolids = firstSolids;
            }
            else
            {
                BooleanOperationsType booleanOperationsType = BooleanOperationsType.Difference;
                switch (BooleanOperator)
                {
                case IFCBooleanOperator.Difference:
                    booleanOperationsType = BooleanOperationsType.Difference;
                    break;

                case IFCBooleanOperator.Intersection:
                    booleanOperationsType = BooleanOperationsType.Intersect;
                    break;

                case IFCBooleanOperator.Union:
                    booleanOperationsType = BooleanOperationsType.Union;
                    break;

                default:
                    Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", true);
                    break;
                }

                resultSolids = new List <GeometryObject>();
                foreach (GeometryObject firstSolid in firstSolids)
                {
                    Solid resultSolid = (firstSolid as Solid);

                    int secondId = (SecondOperand == null) ? -1 : (SecondOperand as IFCRepresentationItem).Id;
                    XYZ suggestedShiftDirection = (SecondOperand == null) ? null : SecondOperand.GetSuggestedShiftDirection(lcs);
                    foreach (GeometryObject secondSolid in secondSolids)
                    {
                        resultSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, secondId, resultSolid, secondSolid as Solid, booleanOperationsType, suggestedShiftDirection);
                        if (resultSolid == null)
                        {
                            break;
                        }
                    }

                    if (resultSolid != null)
                    {
                        resultSolids.Add(resultSolid);
                    }
                }
            }

            return(resultSolids);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Execute a Boolean operation, and catch the exception.
        /// </summary>
        /// <param name="id">The id of the object demanding the Boolean operation.</param>
        /// <param name="secondId">The id of the object providing the second solid.</param>
        /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="opType">The Boolean operation type.</param>
        /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift.  Can be null.</param>
        /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns>
        public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection)
        {
            const double footToMillimeter = 1.0 / 304.8;

            // Perform default operations if one of the arguments is null.
            if (firstSolid == null || secondSolid == null)
            {
                if (firstSolid == null && secondSolid == null)
                {
                    return(null);
                }

                switch (opType)
                {
                case BooleanOperationsType.Union:
                {
                    if (firstSolid == null)
                    {
                        return(secondSolid);
                    }

                    return(firstSolid);
                }

                case BooleanOperationsType.Difference:
                {
                    if (firstSolid == null)
                    {
                        return(null);
                    }

                    return(firstSolid);
                }

                default:
                    // for .Intersect
                    return(null);
                }
            }

            Solid resultSolid       = null;
            bool  failedAllAttempts = true;

            // We will attempt to do the Boolean operation here.
            // In the first pass, we will try to do the Boolean operation as-is.
            // For subsequent passes, we will shift the second operand by a small distance in
            // a given direction, using the following formula:
            // We start with a 1mm shift, and try each of (up to 4) shift directions given by
            // the shiftDirections list below, in alternating positive and negative directions.
            // In none of these succeed, we will increment the distance by 1mm and try again
            // until we reach numPasses.
            // Boolean operations are expensive, and as such we want to limit the number of
            // attempts we make here to balance fidelity and performance.  Initial experimentation
            // suggests that a maximum 3mm shift is a good first start for this balance.
            IList <XYZ> shiftDirections = new List <XYZ>()
            {
                suggestedShiftDirection,
                XYZ.BasisZ,
                XYZ.BasisX,
                XYZ.BasisY
            };

            const int numberOfNudges     = 4;
            const int numPasses          = numberOfNudges * 8 + 1; // 1 base, 8 possible nudges up to 0.75mm.
            double    currentShiftFactor = 0.0;

            for (int ii = 0; ii < numPasses; ii++)
            {
                try
                {
                    resultSolid = null;

                    Solid secondOperand = secondSolid;
                    if (ii > 0)
                    {
                        int shiftDirectionIndex = (ii - 1) % 4;
                        XYZ shiftDirectionToUse = shiftDirections[shiftDirectionIndex];
                        if (shiftDirectionToUse == null)
                        {
                            continue;
                        }

                        // ((ii + 3) >> 3) * 0.25mm shift.  Basically, a 0.25mm shift for every 8 attempts.
                        currentShiftFactor = ((ii + 1) >> 3) * 0.25;
                        int       posOrNegDirection = (ii % 2 == 1) ? 1 : -1;
                        double    scale             = currentShiftFactor * posOrNegDirection * footToMillimeter;
                        Transform secondSolidShift  = Transform.CreateTranslation(scale * shiftDirectionToUse);
                        secondOperand = SolidUtils.CreateTransformed(secondOperand, secondSolidShift);
                    }

                    resultSolid       = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType);
                    failedAllAttempts = false;
                }
                catch (Exception ex)
                {
                    string msg = ex.Message;

                    // This is the only error that we are trying to catch and fix.
                    // For any other error, we will re-throw.
                    if (!msg.Contains("Failed to perform the Boolean operation for the two solids"))
                    {
                        throw ex;
                    }

                    if (ii < numPasses - 1)
                    {
                        continue;
                    }

                    Importer.TheLog.LogError(id, msg, false);
                    resultSolid = firstSolid;
                }

                if (SolidValidator.IsValidGeometry(resultSolid))
                {
                    // If we got here not on out first attempt, generate a warning, unless we got here because we gave up on our 3rd attempt.
                    if (ii > 0 && !failedAllAttempts)
                    {
                        Importer.TheLog.LogWarning(id, "The second argument in the Boolean " +
                                                   opType.ToString() +
                                                   " operation was shifted by " + currentShiftFactor +
                                                   "mm to allow the operation to succeed.  This may result in a very small difference in appearance.", false);
                    }
                    return(resultSolid);
                }
            }

            Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false);
            return(firstSolid);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Execute a Boolean operation, and catch the exception.
        /// </summary>
        /// <param name="id">The id of the object demanding the Boolean operation.</param>
        /// <param name="secondId">The id of the object providing the second solid.</param>
        /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param>
        /// <param name="opType">The Boolean operation type.</param>
        /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift.  Can be null.</param>
        /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns>
        public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection)
        {
            const double footToMillimeter = 1.0 / 304.8;

            // Perform default operations if one of the arguments is null.
            if (firstSolid == null || secondSolid == null)
            {
                if (firstSolid == null && secondSolid == null)
                {
                    return(null);
                }

                switch (opType)
                {
                case BooleanOperationsType.Union:
                {
                    if (firstSolid == null)
                    {
                        return(secondSolid);
                    }

                    return(firstSolid);
                }

                case BooleanOperationsType.Difference:
                {
                    if (firstSolid == null)
                    {
                        return(null);
                    }

                    return(firstSolid);
                }

                default:
                    // for .Intersect
                    return(null);
                }
            }

            Solid resultSolid       = null;
            bool  failedAllAttempts = true;

            // We will attempt to do the Boolean operation 3 times:
            // 1st pass: With the passed-in arguments.
            // 2nd pass: With a 1mm shift in a direction in suggestedShiftDirection, or +Z if suggestedShiftDirection is null
            // 3rd pass: With a 1mm shift in a direction in -suggestedShiftDirection, or -Z if suggestedShiftDirection is null
            for (int ii = 0; ii < 3; ii++)
            {
                try
                {
                    resultSolid = null;

                    Solid secondOperand = secondSolid;
                    if (ii > 0)
                    {
                        // 1 mm shift.
                        XYZ       shiftDirection   = (suggestedShiftDirection == null) ? new XYZ(0, 0, 1) : suggestedShiftDirection;
                        Transform secondSolidShift = Transform.CreateTranslation(shiftDirection * ((ii == 1) ? footToMillimeter : -footToMillimeter));
                        secondOperand = SolidUtils.CreateTransformed(secondOperand, secondSolidShift);
                    }

                    resultSolid       = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType);
                    failedAllAttempts = false;
                }
                catch (Exception ex)
                {
                    if (ii < 2)
                    {
                        continue;
                    }

                    Importer.TheLog.LogError(id, ex.Message, false);
                    resultSolid = firstSolid;
                }

                if (SolidValidator.IsValidGeometry(resultSolid))
                {
                    // If we got here not on out first attempt, generate a warning, unless we got here because we gave up on our 3rd attempt.
                    if (ii > 0 && !failedAllAttempts)
                    {
                        Importer.TheLog.LogWarning(id, "The second argument in the Boolean " +
                                                   opType.ToString() +
                                                   " operation was shifted by 1mm to allow the operation to succeed.  This may result in a very small difference in appearance.", false);
                    }
                    return(resultSolid);
                }
            }

            Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false);
            return(firstSolid);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Internal constructor to make a solid by boolean operation.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="operationType"></param>
        internal Solid(Autodesk.Revit.DB.Solid a, Autodesk.Revit.DB.Solid b, BooleanOperationsType operationType)
        {
            Autodesk.Revit.DB.Solid result = null;

            switch (operationType)
            {
                case BooleanOperationsType.Difference:
                    result = BooleanOperationsUtils.ExecuteBooleanOperation(a, b, BooleanOperationsType.Difference);
                    break;
                case BooleanOperationsType.Intersect:
                    result = BooleanOperationsUtils.ExecuteBooleanOperation(a, b, BooleanOperationsType.Intersect);
                    break;
                case BooleanOperationsType.Union:
                    result = BooleanOperationsUtils.ExecuteBooleanOperation(a, b, BooleanOperationsType.Union);
                    break;
            }

            if (result == null)
            {
                throw new Exception("A boolean operation could not be completed with the provided solids.");
            }

            this.InternalSolid = result;
        }