//------------------------------------------------------------------- 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); }
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); } }
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); }
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); }
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); }
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); }
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); }
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); }