So far in my research, it appears to be a matter of mathematically generating an arc of points between two points [V2, V3] based on their distance from a third angular point [V1] and a radius.
I'd like to get this result (sorry, I have B & C swapped in my scene example):
I am not sure how I should limit the radius size or to get the correct arc direction for the arc & arc points. Here are my current code & scene file:
import c4d,math,random
global C,startAngle,endAngle,radius,subdivisions
v1Obj = doc.SearchObject("V1")
v2Obj = doc.SearchObject("V2")
v3Obj = doc.SearchObject("V3")
aObj = doc.SearchObject("A")
bObj = doc.SearchObject("B")
cObj = doc.SearchObject("C")
archB = doc.SearchObject("Arc B")
obj0 = doc.SearchObject("0")
obj1 = doc.SearchObject("1")
obj2 = doc.SearchObject("2")
obj3 = doc.SearchObject("3")
txt = doc.SearchObject("Text")
def GetPointsOfArc():
global C,startAngle,endAngle
FullCircleAngle = 2 * math.pi
normalizedEndAngle = None
if startAngle < endAngle:
normalizedEndAngle = endAngle
else:
normalizedEndAngle = endAngle + FullCircleAngle
angleRange = normalizedEndAngle - startAngle
angleRange = FullCircleAngle if angleRange > FullCircleAngle else angleRange
step = angleRange / subdivisions
currentAngle = startAngle
while (currentAngle <= normalizedEndAngle):
arcPt = c4d.Vector()
arcPt.x = C.x + radius * math.cos(currentAngle)
arcPt.y = C.y + radius * math.sin(currentAngle)
yield arcPt
currentAngle += step;
archB[c4d.PRIM_ARC_START] = startAngle
archB[c4d.PRIM_ARC_END] = endAngle
degStartAngle = c4d.utils.Deg(startAngle)
degEndAngle = c4d.utils.RadToDeg(endAngle)
print("*"*50)
print("startAngle: %s°\nendAngle: %s°\n"\
"angleRange: %s°\nnormalizedEndAngle: %s°"%(round(degStartAngle),
round(degEndAngle),round(angleRange,2),round(normalizedEndAngle,2)))
def draw(bd):
points = GetPointsOfArc()
COLOR_SPHERE = c4d.Vector(0, 1.0, 0)
for pt in points:
bd.DrawSphere(pt, c4d.Vector(4.0), COLOR_SPHERE, c4d.NOCLIP_Z)
def main():
global C,startAngle,endAngle,radius,subdivisions
radius = op[c4d.ID_USERDATA,2]
subdivisions = op[c4d.ID_USERDATA,3]
V1 = v1Obj.GetAbsPos()
V2 = v2Obj.GetAbsPos()
V3 = v3Obj.GetAbsPos()
a = V2-V1
b = V2-V3
a.Normalize()
b.Normalize()
halfang = math.acos((a.Dot(b)))/2
ab = (a+b)/2
ab.Normalize()
C = V2 - radius/math.sin(halfang)*ab
A = V2 - radius/math.tan(halfang)*a
B = V2 - radius/math.tan(halfang)*b
aObj.SetAbsPos(A)
bObj.SetAbsPos(B)
cObj.SetAbsPos(C)
startAngle = math.atan2(A.y - C.y, A.x - C.x);
endAngle = math.atan2(B.y - C.y, B.x - C.x);
SCENE FILE: Bevel.c4d
I pieced a lot of the math code together from online resources, so I don't entirely understand it. Currently, the arc points are sweeping in the wrong direction. When the startAngle is negative, both the arc & arc points are incorrect. When both the startAngle & endAngle are negative, however, both are correct.
Can anyone provide guidance for getting the arc & arc points to go in the correct direction in these cases please? Thank you!
]]>@m_adam Thank you for the reply and confirmation!
]]>But if you still want to implement the bevel yourself, then I guess @zipit already did all the job here :)
Cheers,
Maxime.
sorry, I did not see that you already did that. Very cool little rig in your file ;) But I am sorry, I cannot debug your code above for you. Because I am not so well versed in trigonometry to immediately see what is going wrong there, I would have to work through it myself. And as already stated, I would personally use transforms, i.e. c4d.Matrix
to encode the arcs. You could also use two quaternions and then just spherically interpolate between them. Your code is rather hard to understand in each detail (for me) in this raw form where you write out all the transforms as trigonometric expressions. Maybe some dev here is more well versed in the subject.
And I would also point to curves/splines again, because it will simplify the problem (you do not have to think about circles anymore, just little boxes, i.e. where to place the control point(s) in that box). In case you are lost with splines, I did attach a little example for a quadratic Bezier curve which should work pretty well in your case.
Cheers,
zipit
Filleted edges going different directions do not meet, thank God!
If you take a look at the Python tag in the scene file I attached above, I had already done all of the steps in your list. There appears to be some exceptions though depending on the positions of the three points making the corner:
** When both the startAngle & endAngle are negative, the arc spline and arc points are correct.
As mentioned in the first two posts, those are the issues for which I'm seeking help.
]]>I only looked very briefly at your code to get a rough idea of what you are doing. I also missed the part of you wanting to do this as a "fillet" which might simplify things a lot. The major question here is if there are intersection points where more then two fillet edges meet. If there are, things can get tricky. It would probably best if you would either show the actual geometry you want to fillet or a mock up of it.
Finding the start and end angle would be done geometrically the following way:
But: Unless the "rail" curve of the fillet is very simple, i.e. planar, I would use spline interpolation and not arc segments. While splines can never be mathematically perfect circles, that does not matter here, since discrete geometry, i.e. polygons, can't either.
If you want to have fillets with intersecting corners, .e.g. a cube with filleted edges, you can piece together the geometry with some clever construction of arc segments or curves, but depending on how complex the object is, SDS might be an easier way.
Cheers,
zipit
Forgot this, but should have put this first - Implementing a bevel tool is not trivial. Covering the default case is not that hard, but covering all the corner cases is, especially when you want to maintain secondary mesh attributes like texture coordinates. I would not go for reinventing the wheel here. Is there a reason why you do not want to use Cinema's edge bevel? Does it not work with SendModellingCommand?
I am definitely seeing what you mean regarding 'corner' cases!
Can I use the SendModellingCommand dynamically if the bevel is being set with a float (functionality similar to the Fillet Radius/Subdivision in an Object Generator)? That is what I'm looking to recreate.
My fear of that was that it would be too resource-heavy to use for fast Viewport feedback. I couldn't find any examples on this forum. If that is possible, where would I use it? Currently I have a 'build' function that I'm calling from ObjectData.GetVirtualObjects
.
On a related note, @m_adam implements a Fillet Radius in the py-rounded_tube_r13.pyp demo (variable rrad
) but doesn't use the Bevel tool so that was another reason I didn't think it was a best practice. I am constructing my Polygon Object differently than the Rounded Tube and I didn't understand the math being used. If it is possible to call SendModellingCommand, it would definitely save me the time of implementing all use cases myself.
I don't know if you had a chance to look at the scene file I attached in my original post, but the implementation is very close. I'm now just looking for help in getting the start & end angles of the arc points to work correctly. They are all over the place depending on the placement of V1, V2, & V3: often on the wrong side of the arc. I hope this clarifies my question.
The subdivision links were not for waste: I saved them all as I know I'll need them eventually (or perhaps with this object if I can't get it to work with the current methodology). Thanks again!
]]>the nature of your problem remains a bit unclear to me. You talk about polygonal data and beveling edges, which I would interpret as the question for bevelling in a network of curves, .e.g. a polygonal topology, but you show the image of a curve and also your code operates in a plane. I assume you want interpret your mesh as a series of cross-sections/curves?
That aside, beveling (either curves or curves in surfaces, i.e. edges) is an application of interpolation and can therefor implemented in various forms, here are some approaches:
Piecing in arc segments. This what you are basically trying to do from the looks of your code. You normally would properly define a series of transforms (i.e. "matrices" in Cinema) to transform a base vector and by that construct the vertices making up the arc segment. This solution is not very suited for a data of intersecting curve networks, i.e. surfaces, it works better in something like a 2D Vector app.
Than there are multiple ways to construct a smooth curve over a series points. The easiest one are Chaikin's Curves which also have spawned a whole group of algorithms all following more or less the same one fourth three fourth idea and some of them can also be applied to meshes.
The next obvious candidates would be quadratic or cubic polynomials/curves. They can the can also be viewed as a combination of linear interpolations. This is called De Casteljau's algorithm. To get from multiple curves segments to splines, you just piece them together (there are multiple ways to do this). But in you case of beveling you probably will only need a single curve. Depending on how deep you venture into this topic this reaches from splines and curves (.e.g. a B-Spline or a Bezier spline) to surfaces (e.g. a NURBS surface).
The last one, and that is the one Cinema probably uses for its edge tool is just Subdivision Surfaces. There are also multiple algorithms of varying complexity, but classical Catmull-Clark SDS have very nice properties (producing only quads for example) and are very easy to implement. The problem here is how to restrict the subdivision part of the algorithm so that you only generate new edges along the edge loop and not perpendicular to it and then come up with the correct modified weights for the smoothing.
Its all a bit vague, but your question is very broad, so I thought this might help.
edit: Forgot this, but should have put this first - Implementing a bevel tool is not trivial. Covering the default case is not that hard, but covering all the corner cases is, especially when you want to maintain secondary mesh attributes like texture coordinates. I would not go for reinventing the wheel here. Is there a reason why you do not want to use Cinema's edge bevel? Does it not work with SendModellingCommand
?
Cheers,
zipit