Beispiel #1
0
        // with the manual startpoint, which is a hint
        private static Point2F prepare_segments_w_hinted_starpoint(Topographer topo, List <Line2F> segments, Segpool pool, double min_dist_to_wall, double general_tolerance, Point2F startpoint)
        {
            // same as automatic, but seek the segment with the closest end to startpoint
            double  min_dist   = double.MaxValue;
            Point2F tree_start = Point2F.Undefined;

            foreach (Line2F seg in segments)
            {
                double r1 = topo.Get_dist_to_wall(seg.p1);
                double r2 = topo.Get_dist_to_wall(seg.p2);

                if (r1 >= min_dist_to_wall)
                {
                    pool.Add(seg, false);
                    double dist = startpoint.DistanceTo(seg.p1);
                    if (dist < min_dist)
                    {
                        min_dist   = dist;
                        tree_start = seg.p1;
                    }
                }
                if (r2 >= min_dist_to_wall)
                {
                    pool.Add(seg, true);
                    double dist = startpoint.DistanceTo(seg.p2);
                    if (dist < min_dist)
                    {
                        min_dist   = dist;
                        tree_start = seg.p2;
                    }
                }
            }

            return(tree_start);
        }
Beispiel #2
0
        private bool should_flip_opened_startpoint(Polyline poly, Point2F startpoint)
        {
            Point2F start = (Point2F)poly.FirstPoint;
            Point2F end   = (Point2F)poly.LastPoint;

            return(startpoint.DistanceTo(end) < startpoint.DistanceTo(start));
        }
Beispiel #3
0
        public bool Contains_point(Point2F pt)
        {
            ulong[] h = hash(pt);

            for (int i = 0; i < 4; i++)
            {
                if (!_pool.ContainsKey(h[i]))
                {
                    continue;
                }

                foreach (Line2F seg in _pool[h[i]])
                {
                    if (pt.DistanceTo(seg.p1) < _tolerance)
                    {
                        return(true);
                    }
                    if (pt.DistanceTo(seg.p2) < _tolerance)
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
Beispiel #4
0
        public Point2F Get_parametric_pt(double u)
        {
            if (_points.Count < 2)
            {
                return(_points[0]);
            }

            double offset = u * _len;

            int seg = _seg_offsets.BinarySearch(offset);

            // if there is no exact element found, binary search returns 1's complemented index of the
            // first larger element, so we use it to find last smaller element
            if (seg < 0)
            {
                seg = ~seg - 1;
            }

            offset -= _seg_offsets[seg];

            Point2F p1   = _points[seg];
            Point2F p2   = _points[seg + 1];
            double  dist = p2.DistanceTo(p1);
            double  x    = p1.X + offset / dist * (p2.X - p1.X);
            double  y    = p1.Y + offset / dist * (p2.Y - p1.Y);

            return(new Point2F(x, y));
        }
Beispiel #5
0
        public List <Point2F> Pull_follow_points(Point2F join_pt)
        {
            List <Point2F> followers = new List <Point2F>();
            List <Line2F>  processed = new List <Line2F>();

            ulong[] h = hash(join_pt);

            for (int i = 0; i < 4; i++)
            {
                if (!_pool.ContainsKey(h[i]))
                {
                    continue;
                }

                foreach (Line2F seg in _pool[h[i]])
                {
                    if (processed.Contains(seg))
                    {
                        continue;  // already got it
                    }
                    double d1 = join_pt.DistanceTo(seg.p1);
                    double d2 = join_pt.DistanceTo(seg.p2);

                    if (d1 > _tolerance && d2 > _tolerance)
                    {
                        continue;
                    }

                    if (d1 < d2)
                    {
                        followers.Add(seg.p2);
                    }
                    else
                    {
                        followers.Add(seg.p1);
                    }
                    processed.Add(seg);
                }
            }

            foreach (Line2F seg in processed)
            {
                Remove(seg);
            }

            return(followers);
        }
Beispiel #6
0
        // with the manual startpoint
        private static Point2F prepare_segments_w_manual_starpoint(Topographer topo, List <Line2F> segments, Segpool pool, double min_dist_to_wall, double general_tolerance, Point2F startpoint)
        {
            // same as automatic, but seek the segment with the closest end to startpoint
            if (!topo.Is_line_inside_region(new Line2F(startpoint, startpoint), general_tolerance))
            {
                Logger.warn("startpoint is outside the pocket");
                return(Point2F.Undefined);
            }

            if (topo.Get_dist_to_wall(startpoint) < min_dist_to_wall)
            {
                Logger.warn("startpoint radius < tool radius");
                return(Point2F.Undefined);
            }

            double  min_dist   = double.MaxValue;
            Point2F tree_start = Point2F.Undefined;

            foreach (Line2F seg in segments)
            {
                double r1 = topo.Get_dist_to_wall(seg.p1);
                double r2 = topo.Get_dist_to_wall(seg.p2);

                if (r1 >= min_dist_to_wall)
                {
                    pool.Add(seg, false);
                    double dist = startpoint.DistanceTo(seg.p1);
                    if (dist < min_dist && topo.Is_line_inside_region(new Line2F(startpoint, seg.p1), general_tolerance))
                    {
                        min_dist   = dist;
                        tree_start = seg.p1;
                    }
                }
                if (r2 >= min_dist_to_wall)
                {
                    pool.Add(seg, true);
                    double dist = startpoint.DistanceTo(seg.p2);
                    if (dist < min_dist && topo.Is_line_inside_region(new Line2F(startpoint, seg.p2), general_tolerance))
                    {
                        min_dist   = dist;
                        tree_start = seg.p2;
                    }
                }
            }

            return(tree_start);
        }
Beispiel #7
0
        public void Change_startpoint(Point2F p1)
        {
            if (_parent != null)
            {
                throw new Exception("startpoint may be changed for the root slice only");
            }
            if (Math.Abs((p1.DistanceTo(this.Center) - this.Radius) / this.Radius) > 0.001)
            {
                throw new Exception("new startpoint is outside the initial circle");
            }

            create_arc_circle(p1, Dir);
        }
Beispiel #8
0
        public void Append_spiral(Point2F start, Point2F end, Vector2d start_tangent, double ted, double tool_r, RotationDirection dir)
        {
            Sliced_path_item spiral = new Sliced_path_item(Sliced_path_item_type.SPIRAL);

            double spacing = Spiral_generator.Calc_reverse_spacing(end.DistanceTo(start), tool_r, ted, _general_tolerance);

            foreach (Biarc2d biarc in Spiral_generator.Gen_archimedean_spiral(start, end, start_tangent, spacing, dir))
            {
                spiral.Add(biarc, _general_tolerance);
            }

            Path.Add(spiral);
        }
Beispiel #9
0
        public List <Point2F> sample_curve_exact(Polyline p, double step)
        {
            List <Point2F> points = new List <Point2F>();

            foreach (Point3F pt in PointListUtils.CreatePointlistFromPolylineStep(p, step).Points)
            {
                points.Add((Point2F)pt);
            }

            Point2F last_sample = points[points.Count - 1];
            Point2F poly_end    = (Point2F)p.LastPoint;

            if (last_sample.DistanceTo(poly_end) > step * 0.001)
            {
                points.Add(poly_end);
            }

            return(points);
        }
Beispiel #10
0
        public void Bisect(Branch_visitor visitor, ref double t, double stop_distance)
        {
            if (t < 0.0 || t > 1.0)
            {
                throw new Exception("branch bisector was called with a wrong range");
            }

            double left  = t;
            double right = 1.0;

            double mid;

            while (true)
            {
                mid = (left + right) / 2;
                Point2F pt = _curve.Get_parametric_pt(mid);

                int result = visitor(pt);

                if (result == 0)
                {
                    break;
                }

                if (result < 0)
                {
                    right = mid;
                }
                else if (result > 0)
                {
                    left = mid;
                }

                Point2F other = _curve.Get_parametric_pt(left == mid ? right : left);
                if (pt.DistanceTo(other) < stop_distance)   // range has shrinked, stop search
                {
                    break;
                }
            }

            t = mid;
        }
Beispiel #11
0
        // we are collecting all the intersections and tracking the list of balls we're inside
        // at any given point. If list becomes empty, we can't shortcut
        public bool Is_line_inside_region(Line2F line, double tolerance)
        {
            Point2F a = line.p1;
            Point2F b = line.p2;

            SortedList <double, List <Circle2F> > intersections = new SortedList <double, List <Circle2F> >();
            List <Circle2F> running_balls = new List <Circle2F>();

            foreach (Circle2F ball in find_intersecting_balls(line))
            {
                Line2F insects = ball.LineIntersect(line, tolerance);

                if (insects.p1.IsUndefined && insects.p2.IsUndefined)
                {
                    // no intersections: check if whole path lay inside the circle
                    if (a.DistanceTo(ball.Center) < ball.Radius + tolerance &&
                        b.DistanceTo(ball.Center) < ball.Radius + tolerance)
                    {
                        return(true);
                    }
                }
                else if (insects.p1.IsUndefined || insects.p2.IsUndefined)
                {
                    // single intersection. one of the path ends must be inside the circle, otherwise it is a tangent case
                    // and should be ignored
                    if (a.DistanceTo(ball.Center) < ball.Radius + tolerance)
                    {
                        running_balls.Add(ball);
                    }
                    else if (b.DistanceTo(ball.Center) < ball.Radius + tolerance)
                    {
                        ;
                    }
                    else
                    {
                        continue;
                    }

                    Point2F c = insects.p1.IsUndefined ? insects.p2 : insects.p1;
                    double  d = c.DistanceTo(a);
                    if (!intersections.ContainsKey(d))
                    {
                        intersections.Add(d, new List <Circle2F>());
                    }
                    intersections[d].Add(ball);
                }
                else
                {
                    // double intersection
                    double d = insects.p1.DistanceTo(a);
                    if (!intersections.ContainsKey(d))
                    {
                        intersections.Add(d, new List <Circle2F>());
                    }
                    intersections[d].Add(ball);

                    d = insects.p2.DistanceTo(a);
                    if (!intersections.ContainsKey(d))
                    {
                        intersections.Add(d, new List <Circle2F>());
                    }
                    intersections[d].Add(ball);
                }
            }

            if (running_balls.Count == 0)
            {
                return(false);
            }

            foreach (var ins in intersections)
            {
                foreach (Circle2F s in ins.Value)
                {
                    if (running_balls.Contains(s))
                    {
                        running_balls.Remove(s);
                    }
                    else
                    {
                        running_balls.Add(s);
                    }
                }

                if (running_balls.Count == 0 && (ins.Key + tolerance < a.DistanceTo(b)))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #12
0
        // simple Archimedean spiral:
        // r = a * theta + c
        //
        // in cartesian coordinates:
        // x = center_x + (a * theta + c) * cos(theta)
        // y = center_y + (a * theta + c) * sin(theta)
        //
        // tangent vector (derivate relative to theta):
        // x' = a * cos(theta) - sin(theta) * r
        // y' = a * sin(theta) + cos(theta) * r

        // we output biarcs to approximate spiral, 6 biarcs (12 arcs) per loop.
        // real number of biarcs would be more, since in general case there would be incomplete loops
        // last biarc is exact to the endpoint

        // if start tangent is defined, spiral will start from it (spacing will be reduced a little to make a fit).
        // otherwise start tangent is choosed automatically and spacing is exact.
        public static List <Biarc2d> Gen_archimedean_spiral(Point2F center, Point2F end, Vector2d start_tangent, double spacing, RotationDirection dir)
        {
            double   r_max = center.DistanceTo(end);
            Vector2d v_end = new Vector2d(center, end);


            double theta_end  = v_end.Ccw_angle;
            double theta_step = 2 * Math.PI / 6;
            double a          = spacing / (2 * Math.PI);
            double theta_start;

            if (dir == RotationDirection.CW)
            {
                a          = -a;
                theta_step = -theta_step;
            }

            if (double.IsNaN(start_tangent.X))
            {
                theta_start = theta_end - r_max / a;
            }
            else
            {
                theta_start = start_tangent.Ccw_angle;
                double candidate_a;

                if (dir == RotationDirection.CCW)
                {
                    while (theta_start >= theta_end)
                    {
                        theta_start -= 2 * Math.PI;
                    }

                    while (true)
                    {
                        candidate_a = r_max / (theta_end - theta_start);
                        if (candidate_a <= a)
                        {
                            break;
                        }
                        theta_start -= 2 * Math.PI;
                    }
                }
                else
                {
                    while (theta_start <= theta_end)
                    {
                        theta_start += 2 * Math.PI;
                    }

                    while (true)
                    {
                        candidate_a = r_max / (theta_end - theta_start);
                        if (candidate_a >= a)
                        {
                            break;
                        }
                        theta_start += 2 * Math.PI;
                    }
                }
                a = candidate_a;
            }

            int nsegments = (int)Math.Floor((theta_end - theta_start) / theta_step);

            double c = -theta_start * a;

            Point2F        p1     = new Point2F();
            Vector2d       t1     = new Vector2d();
            List <Biarc2d> spiral = new List <Biarc2d>();

            for (int segidx = 0; segidx < nsegments; segidx++)
            {
                double theta = theta_start + segidx * theta_step;
                double r     = a * theta + c;

                double sin = Math.Sin(theta);
                double cos = Math.Cos(theta);

                Point2F  p2 = center + new Point2F(r * cos, r * sin);
                Vector2d t2 = new Vector2d(a * cos - r * sin, a * sin + r * cos).Unit();

                if (dir == RotationDirection.CW)
                {
                    t2 = t2.Inverted();
                }

                if (segidx != 0)
                {
                    spiral.Add(new Biarc2d(p1, t1, p2, t2));
                }

                p1 = p2;
                t1 = t2;
            }

            Vector2d t_end = v_end.Normal().Unit();

            if (dir == RotationDirection.CW)
            {
                t_end = t_end.Inverted();
            }

            spiral.Add(new Biarc2d(p1, t1, end, t_end));

            return(spiral);
        }
Beispiel #13
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);
        }