slot on curve

Here's the place for discussion related to CAM/CNC and the development of the Path module.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

Russ4262 wrote: Sat Jan 02, 2021 3:20 pm
freman wrote: Sat Jan 02, 2021 2:49 pm Simple answer is no / Simple answer is yes;

Thanks for the correction. That is not very polished but it makes it a perfectly usable feature. I did not want a centred slot so it's perfect.
You are most welcome sir. I also updated the wiki to provide some guidance for users, that was sorely lacking.

Cheers,
Russell
On closer inspection there are some oddities when using arc extensions.

Firstly, as noted they go the wrong way. Negative extends, +ve shortens. I think I have a fix working now.
Secondly, there is some oddity with extents between 0 and 1. I think I've managed to correct this in the code too.
Also there seems to be an inversion of start and end, at least w.r.t. where the cut starts. If I select single pass, Extend Path Start is affecting the end of the cut. If I select multipass, start and end are at the same place, so it's a bit arbitrary but if we take the last cut as definitive, it has the same sense as single, thus backwards.

Before trying invert this too, I want to make sure I'm not misreading what this means.

What determines the "start" of such a slot? Is that typical or just something about my slots?

Bug or feature? Any explanations?

TIA.
Attachments
slot-start-end.FCStd
(126.49 KiB) Downloaded 40 times
Russ4262
Posts: 941
Joined: Sat Jun 30, 2018 3:22 pm
Location: Oklahoma
Contact:

Re: slot on curve

Post by Russ4262 »

Evening,
freman wrote: Mon Jan 04, 2021 1:14 pm ... On closer inspection there are some oddities ...
For the record, most everything I code has them. Period.

freman wrote: Mon Jan 04, 2021 1:14 pm ... I think I have a fix working now.
... I think I've managed to correct this in the code too. ...
Great ... and even better.
freman wrote: Mon Jan 04, 2021 1:14 pm ... Also there seems to be an inversion of start and end, at least w.r.t. where the cut starts. If I select single pass, Extend Path Start is affecting the end of the cut. If I select multipass, start and end are at the same place, so it's a bit arbitrary but if we take the last cut as definitive, it has the same sense as single, thus backwards.
...
In my view there are two types of slots: through and terminal. Through clearly enter one side of the stock/material and exit the other. Terminal enter the material and terminate therein, requiring a reverse return path or a retraction. For through slots, the physical path start and end are subjective. For terminal slots, the start is the point whereupon the tool enters the material and the end is the opposite - being the retraction point on a single-pass path.

That said, we (thanks for your help by the way) will want the `Extend Path Start` to always affect the point where the tool drops down from initial clearance height to either first step or final depth (multi-pass vs. single-pass).

On multi-pass cuts, if the pass count is odd, the path start and end are different. If even, then the end of the path is the same (x, y) coordinate as the start, but this is not the end of the slot. So perhaps `Extend Path Start` and `Extend Path End` should be changed to `Extend Slot Start` and `Extend Slot End` so they refer to geometrical points and not path points. But then we still have confusion with through slots anyhow. So, the definitive reference is that start refers to the initial drop of the tool from clearance to z-height for the cut, whether single or multi-pass.

freman wrote: Mon Jan 04, 2021 1:14 pm ... Bug or feature? ...
While coding, I generally fight fearlessly and with fidelity for the latter, only to frequently find fiendish finite figures of the former when finished.

Good day sir!
Together it is better.

Russell
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

HI Russ.

Thanks, that was not what I meant but is probably the reply to what I should have asked.

I was meaning what determines which end the code regards as vertexes[0] ? The direction the arc was created in, in sketcher, does not seem to affect it , neither does defining a start point ( which it probably should ).

This seems to work for straight slots but arcs always seem to turn clockwise and this determines Start rather than where the path hits metal. It's a bit chicken and egg since the user does not know where the path hits metal when he is asked to chose the start extent. Other path tools seem to start nearest the start point ( with default 0,0,0 ) so maybe that is just a missing feature which will clear up the ambiguity.

I have fresh fork for this issue. Up to date with your recent changes for parallel slots and added comments.
https://github.com/J-Dunn/FreeCAD

There is also some accuracy problems with the collision test. In the above FCStd, if I set radius offset to 4 and -4 at each end with an 8mm tool , I still get a collision warning. Using -4.01mm passes. Some tolerance is probably needed on the <0 test.

I have added some comments in code about the arbitrary hardcoded tolerances. One is 10dp another 10^-8, I suspect this is the problem. Shouldn't this be using Job Geometry Tolerence which also takes account of mm/inch settings?

Hard coded constants are not so good here, something I've picked up elsewhere in FC.

[EDIT]
PS. The problem with Extend Path End affecting the start of the slot seems to come from XY geometry being +ve anti-clockwise and this determines the vertex order, but G2 is CW and thus order is reversed. I flipped the args to fix it. I don't know if we could find a better excuse or a more logical place for the flip but it seems to work fine. Start / end are now correct and seem consistent with line slots and the various options like zig-zag and reversing.

Code: Select all

            # Arc segment
            # Apply extensions to slot path
            (p1, p2) = pnts
            begExt = obj.ExtendPathStart.Value
            endExt = obj.ExtendPathEnd.Value
            # invert endExt, begExt args to get correct extentions, since XY geom is postitive CCW
            pnts = self._extendArcSlot(p1, p2, self.arcCenter, endExt, begExt)
Current file fixes three bugs.
https://github.com/FreeCAD/FreeCAD/pull/4253
Last edited by freman on Wed Jan 06, 2021 4:52 pm, edited 4 times in total.
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

@Russ4262 maybe you could test the PR and let me know if you see any quirks.
User avatar
onekk
Veteran
Posts: 6149
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: slot on curve

Post by onekk »

Here some code to demostrate the proof of concept of my the approach

Code: Select all

import FreeCAD
from FreeCAD import Base, Rotation, Vector
import Part
import Path
import Draft
import DraftVecUtils
import draftgeoutils.arcs as dgu

import math as mt 

import numpy as np

DOC_NAME = "Pippo"

DOC = FreeCAD.activeDocument()

def clear_doc():
    """
    Clear the active document deleting all the objects
    """
    for obj in DOC.Objects:
        DOC.removeObject(obj.Name)

if DOC is None:
    FreeCAD.newDocument(DOC_NAME)
    FreeCAD.setActiveDocument(DOC_NAME)
    DOC = FreeCAD.activeDocument()
    VIEW = FreeCAD.Gui.ActiveDocument.ActiveView
else:
    clear_doc()
    VIEW = FreeCAD.Gui.ActiveDocument.ActiveView

def setview():
    """Rearrange View"""
    DOC.recompute()
    VIEW.viewAxometric()
    VIEW.setAxisCross(True)
    VIEW.fitAll()

def a2P(a, b):
    """calculate the angle between two vectors
        angle is returned in radians
    """
    deltaY = b.y - a.y
    deltaX = b.x - a.x
    # atan2 return a value in radians
    angle = mt.atan2(deltaY, deltaX)
    return angle


def dest_pt(pt, angle, length):
    """calculate destination point
        Parameters:
        pt    = starting point
        angle =  rad
        length = units
    """

    dpx = pt.x + mt.cos(angle) * length
    dpy = pt.y + mt.sin(angle) * length

    return Vector(dpx, dpy, pt.z)


def get_arc_center(firstPt, lastPt, center):
    radius1 = firstPt.sub(center).Length
    radius2 = lastPt.sub(center).Length

    thirdPt = Vector(firstPt.sub(center).add(lastPt).sub(center))
    thirdPt.normalize()
    thirdPt.scale(radius1, radius1, radius1)
    thirdPt = thirdPt.add(center)

    return thirdPt

EPS = 0.10
EPS_C = EPS * -0.5

VZOR = Vector(0,0,0)
RZO = Rotation(0, 0, 0)

obj1 = Part.makeCylinder(30, 10, Vector(0,0,0), Vector(0,0,1), 70)
obj1.Placement = FreeCAD.Placement(Vector(0,0,0), Rotation(32,0,0))

#Part.show(obj1)

segment = obj1.Edges[0]

print(dgu.isClockwise(segment))

anobj = segment.Curve

rad = anobj.Radius

print("Original segment perimeter = {}".format(2*rad*mt.pi))
print("Original segment Length = {}".format(segment.Length))

center = anobj.Center

p1 = segment.Vertexes[0].Point
p2 = segment.Vertexes[-1].Point

midarcpt = get_arc_center(p1, p2, center)

line1 = Part.makeLine(center, p1)
line2 = Part.makeLine(center, p2)
line3 = Part.makeLine(center, midarcpt)

Part.show(segment)
Part.show(line1)
Part.show(line2)
Part.show(line3)

offset = - 10
red_f = 2

a1 = a2P(center, p1)
a2 = a2P(center, p2)
a3 = a2P(center, midarcpt)

spt1 = dest_pt(center, a1, (line1.Length  + offset) )
spt2 = dest_pt(center, a2, (line2.Length  + offset) )
spt3 = dest_pt(center, a3, (line3.Length  + offset) )

newarc = Part.Edge(Part.Arc(spt2, spt3, spt1))
#Part.show(newarc)

v1 = spt1.sub(center)
v2 = spt2.sub(center)
if newarc.Curve.Axis.z > 0:
    # clockwise
    ang1 = -DraftVecUtils.angle(v1)
    ang2 = -DraftVecUtils.angle(v2)
else:
    # counterclockwise
    ang2 = -DraftVecUtils.angle(v1)
    ang1 = -DraftVecUtils.angle(v2)

print(mt.degrees(ang1))
print(mt.degrees(ang2))

gen_circ = newarc.Curve.Radius * 2 * mt.pi
nalen = newarc.Length

na_sect = mt.degrees(ang1 - ang2)

ang_adv = gen_circ / 360

red_f = red_f / ang_adv # ang_adv in degree

e_ang = mt.radians(mt.degrees(ang2) + red_f)
s_ang = mt.radians(mt.degrees(ang1) - red_f)

print("New Arc Length = {} gen circumf = {}".format(nalen, gen_circ))
print("New Arc sector width {} ang adv = {}".format(na_sect, ang_adv))

slot = Part.Circle()
slot.Center = center
slot.Radius = newarc.Curve.Radius
slot_sh = slot.toShape(e_ang, s_ang)

Part.show(slot_sh)

setview()

Code is pretty self.explanatory.

I have to cope with several problem, as many calculations are not in FreeCAD, or deal with some complex assumptions.

My basic assumptio is that the esdge lay on XY plane, and Z is costant, some calculations are done with this assumption.

I scavenged some code from Draft.ImportDXF and draftgeoutils, to make the calculus, as the entire methods are not useful.

I have generated a section of cylinder, and rotate it to make some difficult calculations, and extrated the "upper external" edge of the solid (not shown)

Based on this edge you have to supply two parameters

Code: Select all

offset = - 10
red_f = 2
an offset that is negative as it is based on the radius of the generating arc of the circular edge
a reduction factor red_f in mm for the edge of the generated arc.

The line are cosmetic but are used in some calculations, to get the two extrema of the arc

The reduction factor is calculated from the length of the arc using the angular advanced that correspond to 1 degree and then added or subtracted to the start and end angles of the generated segment.

using the same techinque and determining the points it could be easily traced the profile for the pocket to obtain a pocket to use if the tool diameter is smaller than the pocket width.

Hope it helps

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

thanks, a quick scan seems similar to much which is done in the existing code.

What it this providing that is not already there and now working with my PR?
User avatar
onekk
Veteran
Posts: 6149
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: slot on curve

Post by onekk »

Not exactly in your code you refer to the bounding box of the segment and use the center, this is not correct and lead to some wrong basic assumption.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

It's not my code beyond what is in the PR. It is credited to Russ.

IIRC the bound box was only used in a specific case.

What are the assumptions and errors you refer to. Apart from unfinished features ( like offset from a straight edge ) the only bug I see here now is the collision test is finding small intersection volume with my test case and an 8mm end mill with 4mm offset and -4mm extensions. This is probably some kind of arithmetic inaccuracy.
User avatar
onekk
Veteran
Posts: 6149
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: slot on curve

Post by onekk »

Code: Select all

def _extendArcSlot(self, p1, p2, cent, begExt, endExt):
        """_extendArcSlot(p1, p2, cent, begExt, endExt)...
        This function extends an arc defined by two end points, p1 and p2, and the center.
        The arc is extended along the circumference with begExt and endExt values.
        The function returns the new end points as tuple (n1, n2) to replace p1 and p2."""
        cancel = True
        n1 = p1
        n2 = p2

        def getArcLine(length, rads):
            rads = abs(length / self.newRadius)
            x = self.newRadius * math.cos(rads)
            y = self.newRadius * math.sin(rads)
            a = FreeCAD.Vector(self.newRadius, 0.0, 0.0)
            b = FreeCAD.Vector(x, y, 0.0)
            return Part.makeLine(a, b)

        if begExt or endExt:
            cancel = False
        if cancel:
            return (p1, p2)

        # Convert extension to radians
        origin = FreeCAD.Vector(0.0, 0.0, 0.0)
        if begExt:
            # Create arc representing extension
            rads = abs(begExt / self.newRadius)
            line = getArcLine(begExt, rads)

            rotToRads = self._xyToRadians(p1.sub(self.arcCenter))
            if begExt < 1:
                rotToRads -= rads
            rotToDeg = math.degrees(rotToRads)
            # PathLog.debug('begExt angles are: {},  {}'.format(rotToRads, rotToDeg))

            line.rotate(origin, FreeCAD.Vector(0, 0, 1), rotToDeg)
            line.translate(self.arcCenter)
            self._addDebugObject(line, 'ExtendStart')
            v1 = line.Vertexes[1]
            if begExt < 1:
                v1 = line.Vertexes[0]
            n1 = FreeCAD.Vector(v1.X, v1.Y, 0.0)

        if endExt:
            # Create arc representing extension
            rads = abs(endExt / self.newRadius)
            line = getArcLine(endExt, rads)

            rotToRads = self._xyToRadians(p2.sub(self.arcCenter)) - rads
            if endExt < 1:
                rotToRads += rads
            rotToDeg = math.degrees(rotToRads)
            # PathLog.debug('endExt angles are: {},  {}'.format(rotToRads, rotToDeg))

            line.rotate(origin, FreeCAD.Vector(0, 0, 1), rotToDeg)
            line.translate(self.arcCenter)
            self._addDebugObject(line, 'ExtendEnd')
            v1 = line.Vertexes[0]
            if endExt < 1:
                v1 = line.Vertexes[1]
            n2 = FreeCAD.Vector(v1.X, v1.Y, 0.0)

        return (n1, n2)

What is the purpouse of getting the arc line, maybe calculate the midpoint?

Plus some test have make me think that some calculation internally done by FreeCAD are wrong, as they don't return the correct value.

The Vector.angle() in particular is returning values dslighly out of the real angle, i.e if you draw an arc from 0 to 90 degree and calculate with point 0 is p1
and point = 90 degree p2

p1.angle(p1) will return 84 and something when you are expeting 90 or maybe -90 if the order is changed.

I have troubled at least 3 hours scavenging the code to make my code work as expected.

using as example another function as valueAt using the edge.valueAt(Length) for an arc it work if valueAt is 0 midpoint or the length, but each other value will give strange results, if you try to plot the points to visualize them using a simple tricks.

Code: Select all

obj = Part.makeSphere(0.3, pnt)
Part.show(obj)
You wiil note this behaviour.

Not to criticize your code, simply as I work mostly using scripting, I have encountered many quirks like this in my struggle to get some "not too simple" things-

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
User avatar
freman
Veteran
Posts: 2203
Joined: Tue Nov 27, 2018 10:30 pm

Re: slot on curve

Post by freman »

These bugs you say are happening could be very important. Could you provide a complete code snippet which will demonstrate the 84 degree angle and the bugs in ValueAt ?
Post Reply