19 May, 2019

Polygonise Curve

Distance can be measured with respect to the shape (Geodesic) using a curve's length, or direction (Euclidean) using a straight line's length.

Examples provided by Guido, and Mirco use the Geodesic method, that is they are both based measuring distances or locating points along the curve using EKL's pointoncurve and pointoncurveRatio.

In this post, in addition to showing the Euclidean method (using spheres), I'll also show passing single value spacing, and a list of spacing values. The latter allows for dividing curves using period patterns. The outcome of todays scripts is a polyline representation of the input curve.

Defining The Pattern

In this example, a pattern is defined as a series of numbers that repeat. Let's take an example:

let numbers(List)
numbers = List(100mm, 50mm, 10mm)

Looping through the above numbers is straight forward if we only need to generate 3 divisions--which is the size of the list. If we need to periodically generate more numbers to achieve a pattern, we need to do the following:

/* Action created by melkhaldi 5/18/2019 */

let numbers(List)
numbers = List(100mm, 50mm, 10mm)
let index(Integer)
let i=1
let count = 17
let currentLength(Length)
for i while i<=count{
    if(i<=numbers.Size()){
        index = i
    }
    else{
        index = ceil(mod(i, numbers.Size())):Integer
    }
    if(index==0)
        index =numbers.Size()

    set currentLength =  numbers.GetItem(index)

    Notify("#",currentLength)
}

The above code generates a series of 17 numbers matching the count variables:
100mm, 50mm, 10mm, 100mm, 50mm, 10mm, 100mm, 50mm, 10mm, 100mm, 50mm, 10mm, 100mm, 50mm, 10mm, 100mm, 50mm

Euclidean Polygonise Curve: Single Value

Here is the code to divide a curve with a single value using spheres

/* Action created by Melkhaldi 2/8/2018 */
//set the local variables
let crv(Curve)
crv  =CurveToDivide

let includeStart(Boolean)
includeStart =IncludeStart

let includeEnd(Boolean)
includeEnd = IncludeEnd

let currentLength(Length)
currentLength=Spacing

//set the start and end points
let start, end(Point)
let vList(List)
vList = crv.GetSubElements(0, false)
start = extract(vList.GetItem(1)):Point
end = extract(vList.GetItem(vList.Size())):Point



let divPoints(List)

//include start point if required
if includeStart
    divPoints.Append(start)

let divPt,center(Point)
let pln(Plane)
let arc(Circle)
let revolveAxis(Line)
let sphere(Surface)
let sorted(List)
let index (Integer)

//set the center to be the start point.
center = start

//disable errors
DisableErrors()


let i=1 
for i while true  {

    //exit condition
    if(distance( center, end)<  currentLength){
        break
    }

    else{
        //create an offset plane at the point//plane is notmal to z
        pln = planeoffset( plane(0,0,1,0m), center)
        //create a half circle
        arc = circleCtrRadius( center, pln, currentLength,0, 0deg, 180deg)
        //get the vertexes of the circle
        vList=arc.GetSubElements(0,false)
        //create a cord connecting the end of the half circle
        revolveAxis=line(extract(vList.GetItem(1)):Point, extract(vList.GetItem(2)):Point)

        //revolve the arc to make a sphere using the cord.
        sphere = revolve(arc, revolveAxis, 0deg, 360 deg)

        //intersect the sphere with the curve
        let intersections(List)
        intersections = disassemble(intersect( crv, sphere), false)

        //find the next center
        if (intersections.Size()==1){
            //if there is only one intersection, then take it
            divPt = intersections.GetItem(1):Point
        }
        else{
            //otherwise, take the point closer to the curve end point
            sorted= intersections.Sort("<" , "Point", "Length", "y= distance(x, point( "+end.coord(1) + "," + end.coord(2) + "," + end.coord(3) + "))")
            divPt =sorted.GetItem(1)
        }

        //lastly, if the div point is on the curve (distance 0mm from curve) 
        //and is not still away from the end point, then add it to out list
        if( distance( divPt, crv)==0mm and distance(divPt, end)>0mm ){
            divPoints.Append( divPt)    
        }
        //define the center point again to be last the div point
        center = divPt  
    }

}
//if the user wanted to add the end point, and it is not at the same position as the last point in the list then add it
if includeEnd and distance( end, divPoints.GetItem(divPoints.Size()))>0mm
    divPoints.Append(end)

//enable errors 
let err(List)
err = EnableErrors()
let poly(Curve)
//if we have no errors, then build a polygon
if(err.Size()==0){
    let lines(List)
    i=1
    let aLine(Line)
    for i while i< divPoints.Size(){
        aLine =line(divPoints.GetItem(i), divPoints.GetItem(i+1))
        lines.Append( aLine)
    }
    poly=assemble(lines):Curve  

}
//otherwise, use the curve it self as a result.
else{
    poly=crv
}
//set the output variable to the poly variable
Polyline = poly

Euclidean Polygonise Curve: Pattern

Combining the above code with the code to loop through a number list, we get

/* Action created by Melkhaldi 2/8/2018 */
//set the local variables
let crv(Curve)
crv  =CurveToDivide

let lengthPattern(List)
lengthPattern =LengthPatternList


let includeStart(Boolean)
includeStart =IncludeStart

let includeEnd(Boolean)
includeEnd = IncludeEnd

let currentLength(Length)


//set the start and end points
let start, end(Point)
let vList(List)
vList = crv.GetSubElements(0, false)
start = extract(vList.GetItem(1)):Point
end = extract(vList.GetItem(vList.Size())):Point



let divPoints(List)

//include start point if required
if includeStart
    divPoints.Append(start)

let divPt,center(Point)
let pln(Plane)
let arc(Circle)
let revolveAxis(Line)
let sphere(Surface)
let sorted(List)
let index (Integer)

//set the center to be the start point.
center = start

//disable errors
DisableErrors()


let i=1 
for i while true  {
    //determine the next length value to use from the list
    if(i<=lengthPattern.Size()){
        index = i
    }
    else{
        index = ceil(mod(i, lengthPattern.Size())):Integer
    }
    if(index==0)
        index =lengthPattern.Size()

    set currentLength =  lengthPattern.GetItem(index)

    //exit condition
    if(distance( center, end)<  currentLength){
        break
    }

    else{
        //create an offset plane at the point
        pln = planeoffset( plane(0,0,1,0m), center)
        //create a half circle
        arc = circleCtrRadius( center, pln, currentLength,0, 0deg, 180deg)
        //get the vertexes of the circle
        vList=arc.GetSubElements(0,false)
        //create a cord connecting the end of the half circle
        revolveAxis=line(extract(vList.GetItem(1)):Point, extract(vList.GetItem(2)):Point)

        //revolve the arc to make a sphere using the cord.
        sphere = revolve(arc, revolveAxis, 0deg, 360 deg)

        //intersect the sphere with the curve
        let intersections(List)
        intersections = disassemble(intersect( crv, sphere), false)

        //find the next center
        if (intersections.Size()==1){
            //if there is only one intersection, then take it
            divPt = intersections.GetItem(1):Point
        }
        else{
            //otherwise, take the point closer to the curve end point
            sorted= intersections.Sort("<" , "Point", "Length", "y= distance(x, point( "+end.coord(1) + "," + end.coord(2) + "," + end.coord(3) + "))")
            divPt =sorted.GetItem(1)
        }

        //lastly, if the div point is on the curve (distance 0mm from curve) 
        //and is not still away from the end point, then add it to out list
        if( distance( divPt, crv)==0mm and distance(divPt, end)>0mm ){
            divPoints.Append( divPt)    
        }
        //define the center point again to be last the div point
        center = divPt  
    }

}
//if the user wanted to add the end point, and it is not at the same position as the last point in the list then add it
if includeEnd and distance( end, divPoints.GetItem(divPoints.Size()))>0mm
    divPoints.Append(end)

//enable errors 
let err(List)
err = EnableErrors()
let poly(Curve)
//if we have no errors, then build a polygon
if(err.Size()==0){
    let lines(List)
    i=1
    let aLine(Line)
    for i while i< divPoints.Size(){
        aLine =line(divPoints.GetItem(i), divPoints.GetItem(i+1))
        lines.Append( aLine)
    }
    poly=assemble(lines):Curve  

}
//otherwise, use the curve it self as a result.
else{
    poly=crv
}
//set the output variable to the poly variable
Polyline = poly


Geodesic Polygonise Curve: Single Value

/* Action created by melkhaldi 5/18/2019 */
//set variables
let crv(Curve)
crv  =CurveToDivide


let includeStart(Boolean)
includeStart = IncludeStart

let includeEnd(Boolean)
includeEnd = IncludeEnd


let currentLength(Length)
currentLength =Spacing

let index (Integer)
let start, end(Point)
let vList(List)

//get start and end points
vList = crv.GetSubElements(0, false)
start = extract(vList.GetItem(1)):Point
end = extract(vList.GetItem(vList.Size())):Point

//determine the direction whether true or false. 
//direction is based on how the curve was drawn. 
//so here we confirm by making a test point,
//if the test point is moving on the curve (its distance from curve is zero), 
//then we are using the correct boolean value
//otherwise, we have to reverse.
let dir(boolean)
let tstPoint(Point)
tstPoint = pointoncurve(crv, start,  0.01mm,true )
if distance(tstPoint, crv)==0mm{

    dir = true
}
else{
    dir = false
}

let divPoints(List)
//include start point if needed
if includeStart
    divPoints.Append(start)

let divP(Point)
let i(Integer)
i=1
let poly(Curve)
//disable errors
DisableErrors()

for i while true{
    divP = pointoncurve(crv, divPoints.GetItem(divPoints.Size()),  currentLength,dir )

     //break out of the loop of the generated point is outside the curve
    if( distance( divP, crv)<>0mm) break

  //if we reached here, this means the point is on the curve, so we add it to the list
    divPoints.Append( divP) 

}

//add end point if needed
if includeEnd and distance( end, divPoints.GetItem(divPoints.Size()))>0mm
    divPoints.Append(end)

//enable errors again (every DisableErrors call has to have an EnableErrors call)
let err(List)
err = EnableErrors()

//if we have no errors
if(err.Size()==0){

    //create a polyline
    let lines(List)
    i=1
    let aLine(Line)
    //go over all points, and stop one position before last, because out functions uses i(current point) and i+1 for next point
    for i while i< divPoints.Size(){
        aLine =line(divPoints.GetItem(i), divPoints.GetItem(i+1))
        lines.Append( aLine)
    }
  //assemble the lines as a single curve
    poly=assemble(lines):Curve  

}
else{
  //if there are errors, then just use the input curve as the result
    poly=crv
}
//set the output variable
Polyline = poly

Geodesic Polygonise Curve: Pattern

/* Action created by melkhaldi 5/18/2019 */
//set local variables
let crv(Curve)
crv  =CurveToDivide

let lengthPattern(List)
lengthPattern = LengthPatternList 


let includeStart(Boolean)
includeStart = IncludeStart

let includeEnd(Boolean)
includeEnd = IncludeEnd


let currentLength(Length)

let index (Integer)

let start, end(Point)
let vList(List)
//get start and end points
vList = crv.GetSubElements(0, false)
start = extract(vList.GetItem(1)):Point
end = extract(vList.GetItem(vList.Size())):Point

//confirm direction (boolean) to use for generating points
let dir(boolean)
let tstPoint(Point)
tstPoint = pointoncurve(crv, start,  0.01mm,true )
if distance(tstPoint, crv)==0mm{

    dir = true
}
else{
    dir = false
}

let divPoints(List)

//add start point if needed
if includeStart
    divPoints.Append(start)

let divP(Point)


let i(Integer)
i=1

let poly(Curve)
//disable errors
DisableErrors()
for i while true{
    //loop to get the next length in the pattern
    if(i<=lengthPattern.Size())
        index = i
    else
        index = ceil(mod(i, lengthPattern.Size())):Integer
    if(index==0)
        index =lengthPattern.Size()
    //set the current length variable
    set currentLength =  lengthPattern.GetItem(index)
    //generate a point
    divP = pointoncurve(crv, divPoints.GetItem(divPoints.Size()),  currentLength,dir )

    //test if point is not on the curve--if so, break the loop
    if( distance( divP, crv)<>0mm) break

    //add the point to the list
    divPoints.Append( divP) 

}
//add end point if needed
if includeEnd and distance( end, divPoints.GetItem(divPoints.Size()))>0mm
    divPoints.Append(end)

//enable errors
let err(List)
err = EnableErrors()
//if no errors build a polyline
if(err.Size()==0){

    let lines(List)
    i=1
    let aLine(Line)
    for i while i< divPoints.Size(){
        aLine =line(divPoints.GetItem(i), divPoints.GetItem(i+1))
        lines.Append( aLine)
    }
    poly=assemble(lines):Curve  

}
else{
  //else return the input curve
    poly=crv
}
//set the output variable
Polyline = poly

Running The Scripts

You can find the file in this link on Bitbucket!

Tags:

About : Maher Elkhaldi

Maher Elkhaldi is a senior applications engineer at Tesla Motors. He founded the 3DXAutomation blog to help make knowledge of programming CATIA easier to find, and contribute to the open-source community.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.