Place object according the normal



  • On 18/03/2018 at 08:16, xxxxxxxx wrote:

    I want to place an object B perpendicular on a polygon of object A.
    So I have to use the normal of the selected polygon.

    However, as soon as I move or rotate object A, object B is not positioned perpendicular.

    Here the test code.
    Polygon 0 of object A is used and object B is a Cylinder.

    import c4d
    from c4d import gui
    #Welcome to the world of Python
      
    # Calculate a polygons' face normal
    def CalculateNormal(index, obj) :
        poly = obj.GetPolygon(index)
        pA = obj.GetPoint(poly.a)
        pB = obj.GetPoint(poly.b)
        pC = obj.GetPoint(poly.c)
        pD = obj.GetPoint(poly.d)
        normal = (pA - pC).Cross(pB - pD)
        normal.Normalize()
        
        # another way to calculate the normal: (pB-pC)%(pC-pA)
        return normal
      
    def CalculatePolyMid(index,obj) :
        poly = obj.GetPolygon(index)
        pA = obj.GetPoint(poly.a)
        pB = obj.GetPoint(poly.b)
        pC = obj.GetPoint(poly.c)
        pD = obj.GetPoint(poly.d)
        
        if (pC == pD) : return (pA+pB+pC)/3
        return (pA+pB+pC+pD)/4  
        
    def CalcNormalPoint(index, op) :
        normal = CalculateNormal(index, op)
        polyMid = CalculatePolyMid(index,op)
        return polyMid, normal
      
    def main() :
        
        cyl = doc.SearchObject("Cylinder")
      
        point, normal = CalcNormalPoint(0, op)
        normalPHB = c4d.utils.VectorToHPB(normal)	#nodig!
        cyl[c4d.ID_BASEOBJECT_REL_POSITION] = point
        cyl[c4d.ID_BASEOBJECT_REL_ROTATION] = normalPHB         
        c4d.EventAdd()
        
    if __name__=='__main__':
        main()
      
    
    


  • On 18/03/2018 at 08:25, xxxxxxxx wrote:

    I guess it is already answered by monkeytack. 
    See https://plugincafe.maxon.net/topic/8497/11094_get-rotational-vector-that-matches-normal-solved



  • On 18/03/2018 at 09:47, xxxxxxxx wrote:

    Even using monkeytacks tip, I cannot make it to work.
    The axes do not align correctly (always).

    I am using a cube to test all directions.

    What to do?

    -Pim



  • On 19/03/2018 at 03:06, xxxxxxxx wrote:

    Hi Pim,

    The main issue with your code is you are actually doing all your calculations in local space of op.
    Keep in mind, GetPoint/GetPolygon give are in local space according the global matrice, so if you want the real position you have to adapt your CalculateNormal and CalculatePolyMid like so:

    def CalculateNormal(index, obj) :
        poly = obj.GetPolygon(index)
        pA = obj.GetPoint(poly.a) * obj.GetMg() # Get the point position in worldspace
        pB = obj.GetPoint(poly.b) * obj.GetMg()
        pC = obj.GetPoint(poly.c) * obj.GetMg()
        pD = obj.GetPoint(poly.d) * obj.GetMg()
        normal = (pA - pC).Cross(pB - pD)
        normal.Normalize()
      
        return normal
      
    def CalculatePolyMid(index,obj) :
        poly = obj.GetPolygon(index)
        pA = obj.GetPoint(poly.a) * obj.GetMg()
        pB = obj.GetPoint(poly.b) * obj.GetMg()
        pC = obj.GetPoint(poly.c) * obj.GetMg()
        pD = obj.GetPoint(poly.d) * obj.GetMg()
        
        if (pC == pD) : return (pA+pB+pC)/3
        return (pA+pB+pC+pD)/4
    

    On a side note I also suggest you to (re)read Marix Fundamental manual.

    I hope it's answers all you questions, in any case do not hesitate

    Cheers,
    Maxime



  • On 19/03/2018 at 03:46, xxxxxxxx wrote:

    Hi Maxime,

    Thanks for this explanation.
    The object is now positioned according to the normal.
    However, the 2 other axes are not correctly positioned.

    Some more information.
    I have a cube as object A and an other rectangular object B (not cylinder).
    B is now positioned on A in the right direction, however, the other axis are not positioned correctly.
    Not in the same way as Cube A.



  • On 20/03/2018 at 02:36, xxxxxxxx wrote:

    Hi Pim,

    A normal is nothing more than a vector wich express a direction in a 3D space which is perpendicular to another element.
    And your object is truly perpendicular to the polygon, so it's aligned to the normal.

    Originally posted by xxxxxxxx

    However, the 2 other axes are not correctly positioned.

    Can you show me or tell me what will be for you the correct orientation? aligned to the polygon? aligned to the object? Aligned with an up vector?

    Cheers,
    Maxime



  • On 20/03/2018 at 12:27, xxxxxxxx wrote:

    Yes, aligned to the cube in my example.

    Actually, I select some polygons of the site of the cube and split this selection into a new object. Then object B is placed perpendicular on this new object (the selected polygons). 
    Object B should now be aligned with this new object.

    I hope this makes things clear.

    But if you could tell me how to aligned object B with the cube, that would help me very much.

    -Pim



  • On 21/03/2018 at 02:00, xxxxxxxx wrote:

    Hi Pim,

    I guess I misspoke, so I will try to reformulate. Actually, the Z axis is correctly aligned and perpendicular to the polygon (your initial question if I'm right).
    So now X and Y axis is the problem. But what is the correct orientation for them?
    Should the Y axis point to the world axis? Should the Y axis be equals to the source object Y axis (what happens if Y axis of source object is completely flipped and can't be aligned in order to stick perpendicularity of Z axis?)
    Should the Y axis be aligned in the same way, when a polygon is selected and orientation mode set to Axis?)

    And the same question applies to X axis.
    So for the moment, the only condition you get is Z-axis need to be perpendicular to polygon selected. And that's the case. But you need to define other rules for X and Y axis. Because you told aligned, but aligned to what? Specific Point? Polygon? World? Other Object? Pre-define value?

    Sorry if my last post was not clear. Hope it makes more sense now.

    Cheers,
    Maxime.



  • On 21/03/2018 at 02:20, xxxxxxxx wrote:

    Yes, the Z axis is now perpendicular.
    Let's call that the up axis and the other 2 (x, and y) the ground axis.

    So, the up axis is now correct and next the ground axis should be aligned with the ground axis of object A.

    For below example this means:
    Object A Y axis is aligned to Object B Z axis (perpendicular)
    Object A Z axis is aligned to Object B -Y axis 
    Object A X axis is aligned to Object B X axis



  • On 21/03/2018 at 03:45, xxxxxxxx wrote:

    But what happens in the case where object axis is not perpendicular to the polygon selection (which will happens pretty much everytime even for a simple object like torus or sphere) as shown for example in this picture.

    Z can't remain perpendicular to the polygon selection and get his -Y axis aligned to the Z axis and X axis aligned to the X axis.

    Maybe there is a mode on the Modeling Axis tab you want to mimic?
    Remember that you could get this axis by using GetModelingAxis



  • On 21/03/2018 at 05:44, xxxxxxxx wrote:

    For the first version of the plugin is the assumption that the selection is one plane (flat).
    So, either the x, y or  size is 0.

    GetModelingAxis gives me the same information as GetMp().
    What is the difference?

    Is it possible to perpendicular align to another axis than the z axis?



  • On 21/03/2018 at 06:16, xxxxxxxx wrote:

    Again what I am trying to do:
    Put an object perpendicular to each side of a cube and aligned with that side.



  • On 22/03/2018 at 06:30, xxxxxxxx wrote:

    Hi Pim,

    I think you don't understand the problem, you are still missing a constraint here.
    In the following picture, you can see 4 different results which are matching your rules while the initial cube is the same with same rotation for each one.

    Moreover, if you only want to deal with cube. You can hardcode all rotations and then multiply by the matrix of the cube and offset with the cube size.
    But it will only work for cube and not other geometry.
    So your final goal is to work only with cube or any kind of object?

    Originally posted by xxxxxxxx

    GetModelingAxis gives me the same information as GetMp().
    What is the difference?

    GeModelingAxis actually return a matrix of the current Handle in the viewport (so if you select a polygon in polygon mode GetModelingAxis will return you the matrix of this selection)
    While GetMp returns you a vector which represents the center of an object according to its bounding box, in its local space.



  • On 22/03/2018 at 11:03, xxxxxxxx wrote:

    Hi MaximeA,

    Thanks for your patience.
    I guess we do not understand each other.

    I agree that the rotation is dependend on the axis.
    Indeed for a cube I can hardcode that, but what for other objects?

    My rules:
    - (polygon) selection is done using object A
    - selection is a flat rectangular surface
    - object to be placed (object B) is placed at the middle of the selection (GetMp()) 
    - object to be placed (object B) is placed perpendicular on the selection.
    - object to be placed (object B) is scaled acoording to the selection.
    - object to be placed (object B) is aligned with object A

    The intent is to place an object where the selection is defined.

    Perhaps you have another method to do this?

    What do you mean with "missing a constraint"?
    Which constraint?

    -Pim



  • On 22/03/2018 at 15:29, xxxxxxxx wrote:

    Hi Pim!

    I'm new to python, but I've played with matrices and vectors for sometime in Xpresso. I think that you need at least 2 axes aligned with the polygon to call id properly aligned. If only Z is aligned to the normal of the polygon than the result should not be stable (imagine the object spining 360 degrees around the normal). Try to align another axis of the object to one of the edges of the polygon. This should act as an up vector. The problem you are facing is somewhat similar to the align to spline tag. Whenever the driving spline rotates onto itself, the object flips, although it is always oriented along the tangent of the spline. That's why the rails spline or an up vector is for. I hope to have made myself clear.



  • On 23/03/2018 at 06:47, xxxxxxxx wrote:

    Hi Ilirbeqiri, Thanks for coming to my rescue! Welcome at Plugin Cafe ! :)

    Regarding your question Pim, 
    As I understand you want to create a kind of polygnome plugin, which as you can see also get this limitation about rotation, (the user define and rotate the object).
    So your last step is not possible, to be determined mathematically (at least easily), but here is how to do your first steps, but the last step is still not possible... According to what Ilirbeqiri said.

    import c4d
      
    def main() :
        if not op:
            return
        
        saveDocMode = doc.GetMode()
        saveDocAction = doc.GetAction()
        
        doc.SetMode(c4d.Mpolygons)
        doc.SetAction(c4d.ID_MODELING_LIVESELECTION)
        
        toolBc = doc.GetActiveToolData()
        saveToolBc = toolBc.GetClone(c4d.COPYFLAGS_PRIVATE_CONTAINER_COPY_IDENTICAL)
      
        toolBc[c4d.MDATA_AXIS_MODE] = c4d.MDATA_AXIS_MODE_SELECTED
        toolBc[c4d.MDATA_AXIS_ROT] = c4d.MDATA_AXIS_ROT_NORMALS
        toolBc[c4d.MDATA_AXIS_BOUNDS_X] = 0.0
        toolBc[c4d.MDATA_AXIS_BOUNDS_Y] = 0.0
        toolBc[c4d.MDATA_AXIS_BOUNDS_Z] = 0.0
        
        mSelection = op.GetModelingAxis(doc)
        
        cyl = doc.SearchObject("Cylinder")
        if not cyl:
            return
        cyl.SetMg(mSelection)
        
        saveToolBc.CopyTo(toolBc, c4d.COPYFLAGS_PRIVATE_CONTAINER_COPY_IDENTICAL)
        doc.SetMode(saveDocMode)
        doc.SetAction(saveDocAction)
      
        c4d.EventAdd()
      
    if __name__=='__main__':
        main()
    

    Scaling according to polygon size is a bit more challenging, and there is no quick way(at least to know how much you should scale your object to match the polygon area) for doing it.
    But I guess by computing the bounding box of the selected polygons will work then scaling your object to match this bounding box will work.
    If you need some help about that I may spend some more time next week but I can't promise anything.

    About GetMp() I'm really confused why you use it since it returns the position of the object axis. Not the one from the selection.

    Hope it's help anyway.

    Cheers,
    Maxime



  • On 23/03/2018 at 12:20, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Hi Pim!

    I'm new to python, but I've played with matrices and vectors for sometime in Xpresso. I think that you need at least 2 axes aligned with the polygon to call id properly aligned. If only Z is aligned to the normal of the polygon than the result should not be stable (imagine the object spining 360 degrees around the normal). Try to align another axis of the object to one of the edges of the polygon. This should act as an up vector. The problem you are facing is somewhat similar to the align to spline tag. Whenever the driving spline rotates onto itself, the object flips, although it is always oriented along the tangent of the spline. That's why the rails spline or an up vector is for. I hope to have made myself clear.

    Yes,I agree, using an up-vector would do the trick.
    But how youdo that?
    So, how to align another axis of the object to one of the edges of the polygon?

    -pim



  • On 23/03/2018 at 12:37, xxxxxxxx wrote:

    Hi MaximeA,

    Thanks, that code works!
    Hopefully, scaling should not be an issue when using GetMp().

    But, I would like to see how you would handle that.
    So, if you have the time please do.

    About the code: briljant, something I would never have figured out myself.
    Is it true that you get the global matrix of the selection and use that to set object B?

    Note: I split the selection and then GetMp() gives me the size of of that split object.

    -Pim



  • On 23/03/2018 at 15:14, xxxxxxxx wrote:

    Hi Pim!

    I see that Maxime's code has worked for you! I'm glad for you! I learned something new also!

    As I mentioned in my previous post, I'm not experienced in Python, but to prove my point I've built a demo scene using Xpresso. The setup is as follows: There's one polygon object and a null that will be aligned to its normal. The polygon's normal and center are easily attained using the Polygon node. I made use of a Vector2Matrix node to convert the normal vector to a rotation Matrix and feed it's result to a Matrix2HPB node. This yields three rotation values, which you can combine in a Reals2Vector node. The edge I chose for the Up vector is the one between points 0 and 1. A simple vector subtraction returns the direction vector, which is later normalized and fed to a Vector2Matrix node and decmposed into three rotation values. Now comes the combination of the rotation values of the normal and edge into the Reals2Vector node and then the rotation is combined into a Generate Matrix node with the center of the polygon as the offset. I'm sending a link of the scene file for inspection. https://goo.gl/uKxwJv The poin is that whatever transformation you do to the polygon (assuming it stays planar) the Null will always stay in the center of it and aligned to the normal and one of the edges. I'm not sure how it all translates into Python, but I presume a coder  with you experience can easily do that. Actually I'm curious to see you come up with something!

    Respects
    Ilir

    Edited

    Here's an even simpler version with the same result https://goo.gl/tVkZer. The setup is as follows: First get the normal of the polygon (first matrix axis) and it's center (matrix offset) using the Polygon node. Then get the difference of the vectors for the positions of the point 0 and 1, normalize the result (this is the second matrix axis). The third matrix axis can be calculated with the Cross Product of the first and the second axes vectors. Feed the offset and the three direction axes to a Vector2Matrix node and the matrix goes to the Global Matrix port of the Null object. Now whatever transformation is applied to the Polygon object, the null should stay rock solid on the center and aligned to the normal and the first edge.



  • On 26/03/2018 at 04:24, xxxxxxxx wrote:

    Great, thank you.
    I will transform it to python and let you know.

    -Pim


Log in to reply