Q: how to Transform a mesh from 4 points to another 4 points

Hi, I am trying to transform a mesh from it’s 4 first bounding box points to 4 other points that are not a 90 degree rectangle, is this easily possible?

Basically scripting moving cageedit with only corner points to a new location, but preferably faster than using the cageedit command.

I’ll just need (if possible) a way to set up a matrix from:
source_pts = [ pt1, pt2, pt3, pt4 ]
to:
target_pts = [ ptA, ptB, ptC, ptD ]

One might implement the Kangaroo2Component.UtilityComponents.MorphToMesh methods. See e.g:

RhinoCommon also has morph methods, but if speed is a criteria Daniels examples certainly go fast🔥

1 Like

Thanks, but I don’t want to use a grasshopper component on this one.

I believe one can still implement those namespace methods outside of Grasshopper. Here’s a quick go using the old EditPythonScript editor in Rhino 8:

I think it implements the SpaceMorph class. So if one of those work that is probably a bit simpler.

2 Likes

How about

mesh_to_target_face.py (1.7 KB)

Thanks, I was onto the path of using “FlowAlongSurface”, but I kind of expected there to be a much faster way :smiley:
And I am not saying it isn’t fast, I just expected there to be a simpler (note easier) way that uses four points in an xform rather than a surfac-to-surface method…

I will adapt it and test it out! Thanks!

Thanks @nathanletwory it was just the input I needed.

Now I have a script that applies a mesh to all quad faces of another mesh.

Here is the updated script. It only applies it to quad faces, so run the QuadRemesh command first if needed.

### applyMeshToMesh.py
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import time

def applyMeshToMesh(source_mesh, target_mesh):
    ### Generate Source Surface
    bbox = rs.BoundingBox(source_mesh)
    source_surface = Rhino.Geometry.NurbsSurface.CreateFromCorners(bbox[0], bbox[1], bbox[2], bbox[3])
    
    ### Get target mesh data
    faces = target_mesh.Faces
    vertices = target_mesh.Vertices
    bigMesh = Rhino.Geometry.Mesh()
    
    ### Define the mesh face
    for i in range(len(faces)):
        ### Check if face is Quad
        if faces[i].IsQuad :
            pt0 = vertices[faces[i][0]]
            pt1 = vertices[faces[i][1]]
            pt2 = vertices[faces[i][2]]
            pt3 = vertices[faces[i][3]]
            
            target_surface = Rhino.Geometry.NurbsSurface.CreateFromCorners(pt0,pt1,pt2,pt3)
            
            ### Make duplicate source mesh and Morph onto the target surface
            mrph = Rhino.Geometry.Morphs.SporphSpaceMorph(source_surface, target_surface)
            mesh = source_mesh.Duplicate()
            mrph.Morph(mesh)
            
            ### Append into bigMesh
            bigMesh.Append(mesh)
    
    ### Check if bigMesh has faces and add to document
    if len(bigMesh.Faces)>0:
        return bigMesh
    else:
        return False


def runScript():
    source_mesh_id = rs.GetObject("Select grass mesh", rs.filter.mesh, preselect=True)
    if not source_mesh_id: return
    target_mesh_id = rs.GetObject("Select target mesh", rs.filter.mesh)
    if not target_mesh_id: return
    
    ### Start Timer
    startTime=time.time()
    
    ### Coerce the meshes
    source_mesh = rs.coercemesh(source_mesh_id)
    target_mesh = rs.coercemesh(target_mesh_id)
    
    bigMesh = applyMeshToMesh(source_mesh, target_mesh)
    bigMesh_id = sc.doc.Objects.Add(bigMesh)
    
    ### Print the time it took
    endTime=time.time()
    print (endTime-startTime)

runScript()
1 Like

Couple of issues:

    if len(bigMesh.Faces)>0:
        return bigMesh
    else:
        return False

rewrite that as:

    if len(bigMesh.Faces)>0:
        return bigMesh
    else:
        return None

And then

    bigMesh = applyMeshToMesh(source_mesh, target_mesh)
    bigMesh_id = sc.doc.Objects.Add(bigMesh)

rewrite as

    bigMesh = applyMeshToMesh(source_mesh, target_mesh)
    if bigMesh is not None:
        sc.doc.Objects.Add(bigMesh)

You’re not using bigMesh_id anyway. And this way you’re not trying to add anything invalid (False) to the document.

Sweet! But why is None better than False? I thought that was the same… :confused:

Thanks for the guidance!

Here is the test result after coding a quick grass patch script:

Only downside is that the mesh is 1.1M faces, (each grass is one face only.)
But can be cool and maybe handy for those scenes when it’s needed.
Or carpets… :smiley: None the less, a good exercise!

(And for that one person maybe who has read this and is still hungry for more: )
I use scripts to generate terrain from boundry curves (+ breaklines and holes) that automatically gives them a given thickness. Then another script to extract the top surfaces and combine those. After that I run QuadRemesh with target edge length.
And finally this new script to apply the mesh patch that I made with another script.

(I see that QuadRemesh messes up the far corner a little bit, making it too short and adding a triangular face, but that would be easy to fix manually if needed)

1 Like

None and False are two different things. You can use both to end up with the same result, but since you’re either returning a mesh or nothing it makes semantically more sense to use None. Then actually testing for that is the biggest thing really that was missing from your code.

That looks neat! This is really what particle systems should be used for - to create this type of grass, fur and other scatter-like of similar items. But that is still daydreams.

edit: I hope that for this you’re “billboarding”. Have a couple of faces with textures on that are the grasses. Instead of having actual grass geometry per grass blade and a bunch of that per square.

1 Like

Interesting, I will read up on this.

I have only compared it like this, since that is what I have needed the last decade, but understanding more leads to more complex results faster, so this bakes nudles :wink:

bigMesh = False
if not bigMesh:
    print "bigMesh does not exist"

bigMesh = None
if not bigMesh:
    print "bigMesh does still not exist"

And the output is:

bigMesh does not exist
bigMesh does still not exist

(And of course Raytraced handles this just fine)

Thanks for all help!

This is not a very good thing to do, since there are more values that are falsey.

For instance if you have bigMesh = "" then if not bigMesh will still be true. That is also why it is better to explicitly test for if bigMesh is not None. None is an actual type you can test against. Make it a habit to return None for failure in your code where for success you’d return actual data (like a mesh, or brep, or some instance of a custom class you’ve implemented). Then test explicitly thing is not None. That way you can be sure you don’t fall accidentally in some of the false-true traps and have unintended logic.

1 Like

Love it! Explained perfectly, thank you!!