Beispiel #1
0
        //-------------------------------------------------------------------
        Arc2F arc(double r, bool large, bool sweep)
        {
            Point3F aLast = _poly.LastPoint;

            double[] rr = _parent.scale(r, r);
            double[] p1 = new double[2] {
                aLast.X, aLast.Y
            };
            double[] p2   = _parent.calc(_cursor.X, _cursor.Y);
            bool     left = (rr[0] == rr[1]) ? (sweep != large) : (sweep == large);

            double[]          c1  = Geometry.circle_center(p1, p2, rr [0], left);
            double[]          c2  = Geometry.circle_center(p1, p2, rr [0], !left);
            RotationDirection dir = (sweep == (rr[0] == rr[1])) ? RotationDirection.CCW : RotationDirection.CW;

            _parent.trace("arc    ... c1=[{0}]  c2=[{1}] p1=[{2}]  p2=[{3}] d={4}"
                          , ToString(c1), ToString(c2)
                          , ToString(p1), ToString(p2)
                          , dir);
            //RotationDirection dir = (p1 [0] < p2 [0] && sweep)
            //	? RotationDirection.CCW : RotationDirection.CW;
            Point2F aCC = new Point2F(c1 [0], c1 [1]);
            Point2F aP1 = new Point2F(p1 [0], p1 [1]);
            Point2F aP2 = new Point2F(p2 [0], p2 [1]);
            Arc2F   arc = new Arc2F(aCC, aP1, aP2, dir);

            return(arc);
        }
Beispiel #2
0
        public void Append_leadin_and_leadout(double leadin_angle, double leadout_angle)
        {
            RotationDirection dir    = Dir;
            Point2F           start  = Start;
            Point2F           end    = End;
            Point2F           center = Center;

            Vector2d v1 = new Vector2d(center, start).Rotated(-(int)dir * leadin_angle);
            Vector2d v2 = new Vector2d(center, end).Rotated((int)dir * leadout_angle);

            if (_segments.Count == 1)   // common case
            {
                _segments[0] = new Arc2F(center, center + v1.Point, center + v2.Point, dir);
            }
            else
            {
                _segments[0] = new Arc2F(center, center + v1.Point, _segments[0].P2, dir);
                _segments[_segments.Count - 1] = new Arc2F(center, _segments[_segments.Count - 1].P1, center + v2.Point, dir);
            }
        }
Beispiel #3
0
        public void Get_extrema(ref Point2F min, ref Point2F max)
        {
            // special processing for the very first slice, treat it as ball
            if (_parent == null)
            {
                Get_ball_extrema(ref min, ref max);
                return;
            }

            Arc2F arc;

            if (_segments.Count == 1)
            {
                arc = _segments[0];
            }
            else
            {
                arc = new Arc2F(_segments[0].P1, _segments[_segments.Count - 1].P2, _segments[0].Center, _segments[0].Direction);
            }

            arc.GetExtrema(ref min, ref max);
        }
Beispiel #4
0
        private Vector2F calc_start_tangent(Polyline poly)
        {
            object   obj     = poly.GetSegment(0);
            Vector2F tangent = new Vector2F();

            if (obj is Line2F)
            {
                Line2F line = (Line2F)obj;
                tangent = new Vector2F(line.p1, line.p2);
            }
            else if (obj is Arc2F)
            {
                Arc2F arc = (Arc2F)obj;
                tangent = new Vector2F(arc.Center, arc.P1).Normal();
                if (arc.Direction == RotationDirection.CW)
                {
                    tangent.Invert();
                }
            }

            return(tangent);
        }
Beispiel #5
0
        private Sliced_path generate_path(Slice_sequence sequence)
        {
            Sliced_path_generator gen;

            if (!_should_smooth_chords)
            {
                gen = new Sliced_path_generator(_general_tolerance);
            }
            else
            {
                gen = new Sliced_path_smooth_generator(_general_tolerance, 0.1 * _tool_r);
            }

            // NOTE: this manipulations are to make the beginning of spiral tangent to the polyline
            object   obj     = _poly.GetSegment(0);
            Vector2d tangent = new Vector2d();

            if (obj is Line2F)
            {
                Line2F line = (Line2F)obj;
                tangent = new Vector2d(line.p1, line.p2);
            }
            else if (obj is Arc2F)
            {
                Arc2F arc = (Arc2F)obj;
                tangent = new Vector2d(arc.Center, arc.P1).Normal();
                if (arc.Direction == RotationDirection.CW)
                {
                    tangent = tangent.Inverted();
                }
            }

            gen.Append_spiral(sequence.Root_slice.Center, sequence.Root_slice.End, tangent, _max_ted, _tool_r, _dir == RotationDirection.Unknown ? RotationDirection.CCW : _dir);
            gen.Append_slice_sequence(sequence);

            return(gen.Path);
        }
Beispiel #6
0
        private static object[] calc_arcs(Point2F p1, Vector2d t1, Point2F p2, Vector2d t2, Point2F pm)
        {
            object[] segs = new object[2];

            Vector2d n1 = t1.Normal();

            Vector2d pm_minus_p1 = new Vector2d(pm - p1);
            double   c1_denom    = 2 * (n1 * pm_minus_p1);

            if (c1_denom == 0)  // c1 is at infinity, replace arc with line
            {
                segs[0] = new Line2F(p1, pm);
            }
            else
            {
                Point2F           c1   = p1 + (pm_minus_p1 * pm_minus_p1 / c1_denom * n1).Point;
                RotationDirection dir1 = new Vector2d(p1 - c1) * n1 > 0 ? RotationDirection.CW : RotationDirection.CCW;
                segs[0] = new Arc2F(c1, p1, pm, dir1);
            }

            Vector2d n2          = t2.Normal();
            Vector2d pm_minus_p2 = new Vector2d(pm - p2);
            double   c2_denom    = 2 * (n2 * pm_minus_p2);

            if (c2_denom == 0)  // c2 is at infinity, replace arc with line
            {
                segs[1] = new Line2F(pm, p2);
            }
            else
            {
                Point2F           c2   = p2 + (pm_minus_p2 * pm_minus_p2 / c2_denom * n2).Point;
                RotationDirection dir2 = new Vector2d(p2 - c2) * n2 > 0 ? RotationDirection.CW : RotationDirection.CCW;
                segs[1] = new Arc2F(c2, pm, p2, dir2);
            }

            return(segs);
        }
Beispiel #7
0
        public Slice(Slice parent, Point2F center, double radius, RotationDirection dir, double tool_r, Point2F magnet)
        {
            _parent = parent;
            _ball   = new Circle2F(center, radius);

            double dist       = Point2F.Distance(center, parent.Center);
            double delta_r    = this.Radius - parent.Radius;
            double coarse_ted = dist + delta_r;

            // 1) one ball is inside other, no intersections
            // 2) balls are spaced too far and do not intersect, TED is 0, distance is positive
            // 3) balls are intersect, TED is valid
            if (dist <= Math.Abs(delta_r))
            {
                _placement = Slice_placement.INSIDE_ANOTHER;
                return;
            }

            if (dist >= this.Radius + parent.Radius)
            {
                _placement = Slice_placement.TOO_FAR;
                return;
            }
            // TED can't be more >= tool diameter, this check prevents fails during the calculation of real angle-based TED
            if (coarse_ted > tool_r * 1.999)
            {
                _placement = Slice_placement.TOO_FAR;
                return;
            }

            Line2F insects = _parent.Ball.CircleIntersect(this._ball);

            if (insects.p1.IsUndefined || insects.p2.IsUndefined)
            {
                // try to return meaningful result even if CB routine had failed (unlikely)
                Logger.err("no intersections found there intersections should be (_parent.Ball.CircleIntersect(_ball))");
                _placement = (dist <= this.Radius || dist <= parent.Radius) ? Slice_placement.INSIDE_ANOTHER : Slice_placement.TOO_FAR;
                return;
            }

            _placement = Slice_placement.NORMAL;

            Vector2d          v_move      = new Vector2d(_parent.Center, this.Center);
            Vector2d          v1          = new Vector2d(_parent.Center, insects.p1);
            RotationDirection default_dir = v_move.Det(v1) > 0 ? RotationDirection.CW : RotationDirection.CCW;

            if (dir == RotationDirection.Unknown)
            {
                if (magnet.IsUndefined)
                {
                    dir = RotationDirection.CCW;    // just something
                }
                else
                {
                    if (insects.p1.DistanceTo(magnet) < insects.p2.DistanceTo(magnet))  // match, use existing dir
                    {
                        dir = default_dir;
                    }
                    else
                    {
                        dir = (default_dir == RotationDirection.CCW) ? RotationDirection.CW : RotationDirection.CCW;   // flip
                    }
                }
            }

            Arc2F arc;

            if (default_dir == dir)
            {
                arc = new Arc2F(this.Center, insects.p1, insects.p2, dir);
            }
            else
            {
                arc = new Arc2F(this.Center, insects.p2, insects.p1, dir);
            }

            _segments.Add(arc);

            calc_teds(tool_r);

            if (_mid_ted <= 0)
            {
                Logger.warn("forced to patch mid_ted");
                _mid_ted = coarse_ted;    // shouldn't be, but ok
            }

            _max_ted = Math.Max(_mid_ted, _entry_ted);
        }
Beispiel #8
0
        public void Refine(List <Slice> colliding_slices, double end_clearance, double tool_r)
        {
            double clearance = end_clearance;

            // check if arc is small. refining is worthless in this case
            // criterion for smallness: there should be at least 4 segments with chord = clearance, plus
            // one segment to space ends far enough. A pentagon with a 5 segments with edge length = clearance
            // will define the min radius of circumscribed circle. clearance = 2 * R * sin (Pi / 5),
            // R = clearance / 2 / sin (Pi / 5)

            if (_segments.Count != 1)
            {
                throw new Exception("attempt to refine slice with n segments != 1");
            }

            Arc2F arc = _segments[0];

            double r_min = clearance / 2 / Math.Sin(Math.PI / 5.0);

            if (arc.Radius <= r_min)
            {
                return;
            }

            if (colliding_slices.Contains(this))
            {
                throw new Exception("attempt to collide slice with itself");
            }

            // now apply the colliding slices. to keep things simple and robust, we apply just one slice - the one who trims
            // us most (removed length of arc is greatest).

            // end clearance adjustment:
            // to guarantee the tool will never hit the unmilled area while rapiding between segments,
            // arc will always have original ends, trimming will happen in the middle only.
            // to prevent the tool from milling extra small end segments and minimize numeric errors at small tangents,
            // original ends would always stick at least for a clearance (chordal) length.
            // i.e. if the point of intersection of arc and colliding circle is closer than clearance to the end,
            // it is moved to clearance distance.

            // there is a two cases of intersecting circles: with single intersection and with a double intersection.
            // double intersections splits arc to three pieces (length of the middle part is the measure),
            // single intesections splits arc in two (the part inside the circle is removed, its length is the measure).
            // in both cases the intersection points are subject to "end clearance adjustment".
            // single intersections are transformed to the double intersections, second point being one of the end clearances.

            // TODO: calculate clearance point the right way, with math :-)
            Line2F  c1_insects = arc.CircleIntersect(new Circle2F(arc.P1, clearance));
            Line2F  c2_insects = arc.CircleIntersect(new Circle2F(arc.P2, clearance));
            Point2F c1         = c1_insects.p1.IsUndefined ? c1_insects.p2 : c1_insects.p1;
            Point2F c2         = c2_insects.p1.IsUndefined ? c2_insects.p2 : c2_insects.p1;

            Line2F max_secant = new Line2F();
            double max_sweep  = 0;

            foreach (Slice s in colliding_slices)
            {
                if (s == _parent)
                {
                    continue;  // no reason to process it
                }
                Line2F secant = arc.CircleIntersect(s.Ball);

                if (secant.p1.IsUndefined && secant.p2.IsUndefined)
                {
                    continue;
                }

                if (secant.p1.IsUndefined || secant.p2.IsUndefined)
                {
                    // single intersection
                    Point2F splitpt = secant.p1.IsUndefined ? secant.p2 : secant.p1;
                    if (arc.P1.DistanceTo(s.Center) < arc.P2.DistanceTo(s.Center))
                    {
                        if (splitpt.DistanceTo(arc.P1) < clearance)
                        {
                            continue;  // nothing to remove
                        }
                        else if (splitpt.DistanceTo(arc.P2) < clearance)
                        {
                            secant = new Line2F(c1, c2);
                        }
                        else
                        {
                            secant = new Line2F(c1, splitpt);
                        }
                    }
                    else
                    {
                        // remove second segment
                        if (splitpt.DistanceTo(arc.P2) < clearance)
                        {
                            continue;
                        }
                        else if (splitpt.DistanceTo(arc.P1) < clearance)
                        {
                            secant = new Line2F(c1, c2);
                        }
                        else
                        {
                            secant = new Line2F(splitpt, c2);
                        }
                    }
                }
                else
                {
                    // double intersection
                    if (secant.p1.DistanceTo(arc.P1) < clearance)
                    {
                        secant.p1 = c1;
                    }
                    else if (secant.p1.DistanceTo(arc.P2) < clearance)
                    {
                        secant.p1 = c2;
                    }

                    if (secant.p2.DistanceTo(arc.P1) < clearance)
                    {
                        secant.p2 = c1;
                    }
                    else if (secant.p2.DistanceTo(arc.P2) < clearance)
                    {
                        secant.p2 = c2;
                    }
                }

                if (secant.p1.DistanceTo(secant.p2) < clearance * 2) // segment is too short, ignore it
                {
                    continue;
                }

                // sort insects by sweep (already sorted for single, may be unsorted for the double)
                Vector2d v_p1   = new Vector2d(arc.Center, arc.P1);
                Vector2d v_ins1 = new Vector2d(arc.Center, secant.p1);
                Vector2d v_ins2 = new Vector2d(arc.Center, secant.p2);


                double sweep = angle_between_vectors(v_ins1, v_ins2, arc.Direction);

                if (angle_between_vectors(v_p1, v_ins1, arc.Direction) > angle_between_vectors(v_p1, v_ins2, arc.Direction))
                {
                    secant = new Line2F(secant.p2, secant.p1);
                    sweep  = 2.0 * Math.PI - sweep;
                }

                if (sweep > max_sweep)
                {
                    // ok, a last check - removed arc midpoint should be inside the colliding circle
                    Arc2F check_arc = new Arc2F(arc.Center, secant.p1, secant.p2, arc.Direction);
                    if (check_arc.Midpoint.DistanceTo(s.Center) < s.Radius)
                    {
                        max_sweep  = sweep;
                        max_secant = secant;
                    }
                }
            }

            if (max_sweep == 0)
            {
                return;
            }

            Arc2F head = new Arc2F(arc.Center, arc.P1, max_secant.p1, arc.Direction);
            Arc2F tail = new Arc2F(arc.Center, max_secant.p2, arc.P2, arc.Direction);

            _segments.Clear();
            _segments.Add(head);
            _segments.Add(tail);

            // recalculate max TED accounting for the new endpoints.
            // this TED is 'virtual' and averaged with previous max_ted to make arcs look more pretty

            // if ends of removed segment are at the same side of direction vector,
            // midpoint is still present, _max_ted is valid and is maximum for sure
            Vector2d v_move       = new Vector2d(_parent.Center, this.Center);
            Vector2d v_removed_p1 = new Vector2d(_parent.Center, max_secant.p1);
            Vector2d v_removed_p2 = new Vector2d(_parent.Center, max_secant.p2);

            if (v_move.Det(v_removed_p1) * v_move.Det(v_removed_p2) > 0)
            {
                return;
            }

            double ted_head_end   = calc_ted(max_secant.p1, tool_r);
            double ted_tail_start = calc_ted(max_secant.p2, tool_r);

            double ted = Math.Max(ted_head_end, ted_tail_start);

            if (ted <= 0)
            {
                Logger.err("max TED vanished after refining the slice !");
                return;
            }

            ted      = (ted + _mid_ted) / 2;
            _max_ted = Math.Max(ted, _entry_ted);
        }