Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
2. Need support? Ask a question to our Community Support category.
3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
4. Be respectful, on topic and if you see a problem, Flag it.

If you would like to contact our Community Manager personally, feel free to send a private message or an email.

Options

# curve fillet help

Member Posts: 2,071 PRO
I'm working on a feature that will need to take two co-planar curves as inputs and create a fillet between them (as part of a bigger thing). I'm not sure how to create an arc segment that will hit tangent to the curves. I recall the fillet code that @ilya_baran (and others?) did for 3D Lines, which is a start for me, but I think this code only works for fillets between lines. I also recall @Alex_Kempen making a curve fillet feature, which used construction surfaces and opFillet to bypass the need for doing all of the math, but I'd like to avoid that if I can for performance (it's a good fallback). Is there something more general purpose that would handle curved inputs and behave more like a sketch fillet? Maybe one of the devs can share some more about how the sketch fillet is done?
Evan Reese / Principal and Industrial Designer with Ovyl
Website: ovyl.io

• Options
Onshape Employees, Developers, HDM Posts: 1,185
Even if the curves are coplanar, constructing a fillet between them in the general case requires solving a nonlinear equation.  Sketch fillet works by setting up appropriate tangent constraints and then letting our constraint solver figure it out.  In FS, I'm not sure if replicating that using a sketch is possible, but if it is, it's extremely tricky (since tangency constraints can resolve in many different ways).  If you could offset edges within the plane, that would help, but that's currently not exposed to FS.  I think doing what Alex did is your best bet.
Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
• Options
Member Posts: 129 PRO
edited December 2022
This should do it. Assuming they are both lines. Regen time is 16 ms I just threw this together quickly so its not cleaned up, however it should work. How you handle the edges at the end is up to you. It currently is extracting the edge to keep and extending if it doesn't touch the lines.

If they are not both lines, then you would need to generate an offset curve similar to how the lines work here, I left the else code empty because that can be very complicated.

```FeatureScript 1930;
import(path : "onshape/std/common.fs", version : "1930.0");

annotation { "Feature Type Name" : "Curve Fillet" }
export const curveFillet = defineFeature(function(context is Context, id is Id, definition is map)
precondition
{
annotation { "Name" : "Edges", "Filter" : EntityType.EDGE && EdgeTopology.WIRE, "MaxNumberOfPicks" : 2 }
definition.edges is Query;
annotation { "Name" : "Radius" }

}
{
var edges =evaluateQuery(context, definition.edges);
var intDist = evDistance(context, {
"side0" : edges[0],
"side1" : edges[1]
});
var edgeBodies =evaluateQuery(context,qUnion([ qOwnerBody(edges[0]),qOwnerBody(edges[1])]));
var tls = [undefined,undefined];
tls[0] = evEdgeTangentLine(context, {
"edge" : edges[0],
"parameter" : intDist.sides[0].parameter
});
tls[1] = evEdgeTangentLine(context, {
"edge" : edges[1],
"parameter" : intDist.sides[1].parameter
});

//Make sure they are pointing away from eachother. adjust directions of the lines here so they are at their minimum vector angle apart
if(intDist.sides[0].parameter +0.01>=1) tls[0].direction *=-1;
if(intDist.sides[1].parameter +0.01>=1) tls[1].direction *=-1;
//now to create a true fillet, we need a plane
var lineInt =intersection(tls[0], tls[1]);
var pln;
if(lineInt.dim !=0)
{

}
if(lineInt.dim==0)
{
pln = plane(lineInt.intersection,cross(tls[0].direction, tls[1].direction));
if(isQueryEmpty(context, qGeometry(edges[0], GeometryType.LINE))==false && isQueryEmpty(context, qGeometry(edges[1], GeometryType.LINE))==false)
{
//2 lines, easy peasy
var plns=[undefined,undefined];
for(var i in [0,1])
{
plns[i] = plane(tls[i].origin,pln.normal,tls[i].direction);
plns[i].yAxis = yAxis(plns[i]);//store for quick use

}
//adjust the stored y axis so that the offset goes in the correct direction
if(angleBetween(plns[0].yAxis, tls[1].direction)> 90*degree)
{
plns[0].yAxis *=-1;
}
if(angleBetween(plns[1].yAxis, tls[0].direction)> 90*degree)
{
plns[1].yAxis *=-1;
}
var offLines = [tls[0],tls[1]];
for(var i in [0,1])
{
offLines[i].origin = offLines[i].origin + plns[i].yAxis * definition.radius;
}
var arcCenterInt =intersection(offLines[0], offLines[1]);
var sk = newSketchOnPlane(context, id+"arcSketch", {
"sketchPlane" : pln
});
var trans = worldToPlane3D(pln);

var centerToApexDirection =normalize( pln.origin - arcCenterInt.intersection);
var arcMid = arcCenterInt.intersection + (centerToApexDirection * definition.radius);
var arcSt = project(tls[0],arcCenterInt.intersection);
var arcEnd = project(tls[1], arcCenterInt.intersection);
arcMid = trans * arcMid;
arcSt = trans * arcSt;
arcEnd = trans * arcEnd;

skArc(sk, "arc", {
"start" : vector(arcSt[0], arcSt[1]),
"mid" : vector(arcMid[0], arcMid[1]),
"end" : vector(arcEnd[0], arcEnd[1])
});
skSolve(sk);
opExtractWires(context, id+"extractArc", {
"edges" : sketchEntityQuery(id+"arcSketch", EntityType.EDGE, "arc")
});
opDeleteBodies(context, id+"cleanup", {
"entities" : qCreatedBy(id+"arcSketch", EntityType.BODY)
});
var edgeDists = [undefined,undefined];
for(var i in [0,1])
{
edgeDists[i] =evDistance(context, {
"side0" :qCreatedBy(id+"extractArc", EntityType.EDGE),
"side1" : edges[i],
"arcLengthParameterization":false
});
if(edgeDists[i].distance <0.001*millimeter)//leave some tolerance as distance may return a very small number
{
edges[i] = makeRobustQuery(context, edges[i]);//probably not needed
opSplitEdges(context, id + "trimEdge" + i + "trim", {
"edges" : edges[i],
"parameters" : [[edgeDists[i].sides[1].parameter]],
"arcLengthParameterization":false
});
var qBdyEdges = edgeBodies[i]->qOwnedByBody( EntityType.EDGE);
qBdyEdges=qUnion(qBdyEdges,qSplitBy(id + "trimEdge" + i + "trim", EntityType.EDGE, false));
qBdyEdges=qUnion(qBdyEdges,qSplitBy(id + "trimEdge" + i + "trim", EntityType.EDGE, true));

var ct =size(evaluateQuery(context, qBdyEdges));
if(ct>1)
{
var distToInt =evDistance(context, {
"side0" :qBdyEdges,
"side1" : tls[i].origin
});
opExtractWires(context, id + "trimEdge" + i + "extractRemainingEdge", {
"edges" : qSubtraction(qBdyEdges, qNthElement(qBdyEdges, distToInt.sides[0].index))
});

}
else
{
println("only : "~ct~"edges found");
}
}
else
{
opFitSpline(context, id + "trimEdge" + i + "extendTo", {
"points" : [edgeDists[i].sides[0].point,edgeDists[i].sides[1].point]
});
}
}

}
else
{
//need to find a way to offset the edge in the same way as 2 lines.
}
}
});```

• Options
Member Posts: 2,071 PRO
This should do it. Assuming they are both lines. Regen time is 16 ms I just threw this together quickly so its not cleaned up, however it should work. How you handle the edges at the end is up to you. It currently is extracting the edge to keep and extending if it doesn't touch the lines.

If they are not both lines, then you would need to generate an offset curve similar to how the lines work here, I left the else code empty because that can be very complicated.
I'll also dig into this when I have time. Thanks for taking so much time to give input!
Evan Reese / Principal and Industrial Designer with Ovyl
Website: ovyl.io