예제 #1
0
파일: PolyCurve.cs 프로젝트: lulzzz/Nucleus
        /// <summary>
        /// Populate another polycurve with the portions of this one which lie within
        /// the specified subDomain
        /// </summary>
        /// <param name="subDomain"></param>
        /// <param name="result"></param>
        private void PopulateWithSubCurves(Interval subDomain, PolyCurve result)
        {
            double segCount = SegmentCount;
            double segStart = 0;

            foreach (Curve subCrv in SubCurves)
            {
                double   subSC  = subCrv.SegmentCount;
                double   segEnd = segStart + subSC;
                Interval crvDom = new Interval(segStart / segCount, segEnd / segCount);
                if (subDomain.Contains(crvDom))
                {
                    result.Add(subCrv.Duplicate());
                }
                else if (subDomain.Overlaps(crvDom))
                {
                    Curve subSubCrv = subCrv.Extract(crvDom.ParameterOf(subDomain.Overlap(crvDom)));
                    if (subSubCrv != null)
                    {
                        result.Add(subSubCrv);
                    }
                }
                segStart = segEnd;
            }
        }
예제 #2
0
        /// <summary>
        /// Get a closed polycurve containing the edges of this section of the path.
        /// The edges of the path must have been generated first.
        /// </summary>
        /// <returns></returns>
        public static PolyCurve GetBoundaryCurve <TPath>(this TPath path)
            where TPath : IWidePath
        {
            var result = new PolyCurve();

            if (path.StartCapLeft != null)
            {
                result.Add(path.StartCapLeft);
            }

            if (path.LeftEdge != null)
            {
                result.Add(path.LeftEdge, true);
            }

            if (path.EndCapLeft != null)
            {
                result.Add(path.EndCapLeft, true);
            }

            if (path.EndCapRight != null)
            {
                result.Add(path.EndCapRight.Reversed(), true);
            }

            if (path.RightEdge != null)
            {
                result.Add(path.RightEdge.Reversed(), true);
            }

            if (path.StartCapRight != null)
            {
                result.Add(path.StartCapRight.Reversed(), true);
            }

            result.Close();

            return(result);
        }
예제 #3
0
        /// <summary>
        /// Split this region into two (or more) sub-regions along a straight line
        /// </summary>
        /// <param name="splitPt">A point on the splitting line</param>
        /// <param name="splitDir">The direction of the line</param>
        /// <param name="splitWidth">Optional.  The width of the split.</param>
        /// <returns>The resultant list of regions.  If the line does not bisect
        /// this region and the region could not be split, this collection will contain
        /// only the original region.</returns>
        public IList <PlanarRegion> SplitByLineXY(Vector splitPt, Vector splitDir, double splitWidth = 0)
        {
            var result    = new List <PlanarRegion>();
            var lineInts  = new List <double>();
            var outerInts = Intersect.CurveLineXY(Perimeter, splitPt, splitDir, null, 0, 1, false, lineInts);

            if (outerInts.Count > 1)
            {
                // Sort intersections by position along curve:
                var sortedInts = new SortedList <double, double>(outerInts.Count);
                for (int i = 0; i < outerInts.Count; i++)
                {
                    sortedInts.Add(outerInts[i], lineInts[i]);
                }

                outerInts = sortedInts.Keys.ToList();
                lineInts  = sortedInts.Values.ToList();
                int offset = lineInts.IndexOfMin();
                outerInts.Shift(offset);
                lineInts.Shift(offset);

                // Create segments data structure
                var segments = new List <PerimeterSegment>(outerInts.Count - 1);
                for (int i = 0; i < outerInts.Count; i++)
                {
                    double t0      = outerInts[i];
                    double t1      = outerInts.GetWrapped(i + 1);
                    double tC0     = lineInts[i];
                    double tC1     = lineInts.GetWrapped(i + 1);
                    var    segment = new PerimeterSegment(Perimeter, t0, t1, tC0, tC1);
                    segments.Add(segment);
                }

                //TODO: void intersections

                bool backwards = true;
                while (segments.Count > 0)
                {
                    var offsets = new List <double>();
                    PerimeterSegment segment = segments.First();

                    PolyCurve newPerimeter = segment.Extract().ToPolyCurve();
                    for (int i = 0; i < newPerimeter.SegmentCount; i++)
                    {
                        offsets.Add(0);
                    }
                    PerimeterSegment nextSegment = FindNextPerimeterSegment(segments, segment.CutterDomain.End, backwards);
                    while (nextSegment != null && nextSegment != segment)
                    {
                        Curve nextCurve = nextSegment.Extract();
                        newPerimeter.AddLine(nextCurve.StartPoint);
                        offsets.Add(splitWidth / 2);
                        newPerimeter.Add(nextCurve);
                        for (int i = 0; i < nextCurve.SegmentCount; i++)
                        {
                            offsets.Add(0);
                        }
                        segments.Remove(nextSegment);
                        nextSegment = FindNextPerimeterSegment(segments, nextSegment.CutterDomain.End, backwards);
                    }
                    segments.RemoveAt(0);
                    if (!newPerimeter.Closed)
                    {
                        /*if (splitWidth > 0)
                         * {
                         *  // Temporary bodge to get rid of 'blades' at ends of split
                         *  Vector endToEnd = (newPerimeter.StartPoint - newPerimeter.EndPoint).Unitize();
                         *  var line = new Line(
                         *      newPerimeter.EndPoint - endToEnd * splitWidth / 4,
                         *      newPerimeter.StartPoint + endToEnd * splitWidth / 4);
                         *  newPerimeter.AddLine(line.StartPoint);
                         *  offsets.Add(0);
                         *  newPerimeter.Add(line);
                         *  offsets.Add(splitWidth / 2);
                         *  newPerimeter.Close();
                         *  offsets.Add(0);
                         * }
                         * else
                         * {*/
                        newPerimeter.Close();
                        offsets.Add(splitWidth / 2);
                        //}
                    }
                    backwards = !backwards;

                    if (splitWidth > 0)
                    {
                        var newNewPerimeter = newPerimeter.OffsetInwards(offsets);
                        // Check offset has not inverted perimeter:
                        // TODO: Do this automatically when offsetting?
                        if (newNewPerimeter != null && newNewPerimeter.IsClockwiseXY() == newPerimeter.IsClockwiseXY())
                        {
                            newPerimeter = newNewPerimeter.ToPolyCurve();
                        }
                        else
                        {
                            newPerimeter = null;
                        }
                    }

                    if (newPerimeter != null)
                    {
                        result.Add(new PlanarRegion(newPerimeter, Attributes?.Duplicate()));
                    }
                }

                // OLD VERSION:

                /*for (int i = 0; i < outerInts.Count; i++)
                 * {
                 *  double t0 = outerInts[i];
                 *  double t1 = outerInts.GetWrapped(i + 1);
                 *  Curve newPerimeter = Perimeter.Extract(new Interval(t0, t1))?.ToPolyCurve();
                 *  //TODO: Cut through and include voids
                 *  if (!newPerimeter.Closed)
                 *  {
                 *      ((PolyCurve)newPerimeter).Close();
                 *      if (splitWidth > 0)
                 *      {
                 *          var offsets = new double[newPerimeter.SegmentCount];
                 *          offsets[offsets.Length - 1] = splitWidth / 2;
                 *          var newNewPerimeter = newPerimeter.OffsetInwards(offsets);
                 *          // Check offset has not inverted perimeter:
                 *          // TODO: Do this automatically when offsetting?
                 *          if (newNewPerimeter != null && newNewPerimeter.IsClockwiseXY() == newPerimeter.IsClockwiseXY())
                 *          {
                 *              newPerimeter = newNewPerimeter;
                 *          }
                 *          else newPerimeter = null;
                 *      }
                 *  }
                 *  if (newPerimeter != null) result.Add(new PlanarRegion(newPerimeter, Attributes?.Duplicate()));
                 * }*/
            }
            else
            {
                result.Add(this); //Return the original
            }
            return(result);
        }
예제 #4
0
파일: PolyCurve.cs 프로젝트: lulzzz/Nucleus
        /// <summary>
        /// Offset this curve on the XY plane by varying distances for
        /// each span.
        /// </summary>
        /// <param name="distances">The offset distance.
        /// Positive numbers will result in the offset curve being to the right-hand
        /// side, looking along the curve.  Negative numbers to the left.</param>
        /// <param name="tidy">If true (default) collapsed segments will be removed.</param>
        /// <returns></returns>
        public override Curve Offset(IList <double> distances, bool tidy = true, bool copyAttributes = true)
        {
            //TODO: Implement collapsed segments tidying

            var result    = new PolyCurve();
            int distIndex = 0;

            // Offset sub curves:
            foreach (Curve crv in SubCurves)
            {
                Curve offsetCrv = crv.Offset(distances.SubListFrom(distIndex));
                distIndex += crv.SegmentCount;
                if (distIndex > distances.Count - 1)
                {
                    distIndex = distances.Count - 1;
                }

                if (offsetCrv != null)
                {
                    if (result.SubCurves.Count > 0)
                    {
                        // Adjust offset curve ends to node out
                        Curve prevCrv = result.SubCurves.Last();
                        MatchEnds(prevCrv.End, offsetCrv.Start);
                    }
                    result.Add(offsetCrv);
                    if (copyAttributes && offsetCrv.Attributes == null)
                    {
                        offsetCrv.Attributes = Attributes;
                    }
                }
            }

            // Match end to start
            if (Closed && result.SubCurves.Count > 1)
            {
                MatchEnds(result.SubCurves.Last().End, result.SubCurves.First().Start);
            }

            if (tidy)
            {
                List <Curve> originals = new List <Curve>();
                originals.AddRange(SubCurves);

                // Removed flipped segments
                int j = 0;
                while (j < result.SubCurves.Count)
                {
                    Curve offset   = result.SubCurves[j];
                    Curve original = originals[j];

                    if (IsOffsetCurveFlipped(original, offset))
                    {
                        if (result.SubCurves.Count > 0)
                        {
                            Curve previous   = result.SubCurves.GetWrapped(j - 1);
                            Curve subsequent = result.SubCurves.GetWrapped(j + 1);
                            bool  worked     = MatchEnds(previous.End, subsequent.Start);
                            //TODO: Deal with failed matches (for e.g. when adjacent edges are parallel)
                            //if (!worked)
                            //return null; //TEMP: prevents invalid offsets, but a bit overkilly!
                        }
                        result.SubCurves.RemoveAt(j);
                        originals.RemoveAt(j);
                        //j--;
                    }
                    else
                    {
                        j++;
                    }
                }
                if (!result.IsValid)
                {
                    return(null);
                }
            }

            return(result);
        }