Could you please shine some light on the attached trial to get the isothermic goal working? isothermic_v1.gh (24.7 KB)

The mesh edges are currently approximately following principal curvature directions. Even when the goal has a relatively small, close to negligible, strength compared to other control goals, the mesh seems to “explode” nonetheless. How may I properly setup the workflow? Thanks!

For the isothermic goal to work, the initial mesh needs to be either fairly good to start with, or quite free (in terms of fixed corners/boundaries, or pulling onto surfaces).

If a grid is fully aligned with principal curvature directions, there can’t ever be any odd valence irregular vertices. It seems these isothermic meshes can sometimes still work with some, but lots of automatic quad meshers give an output with too many valence 3 and 5s, which tend to cause problems.

Also, at the moment, when the angle of the quads goes beyond some limit, the isothermic goal can become unstable. I think I can fix this to make it stable for a wider angle range, or at least to switch itself off when the limit is passed.
Isothermic meshes have fairly square quads (since each quad has an incircle, and a rectangle cannot have an incircle). So if the aspect ratio of the initial grid isn’t good, and the boundaries are fixed, it might not be able to reach a good solution.

Leaving the boundary free can make it much easier to get to an isothermic grid on a freeform curved surface. One way to do this is if you can extend your surface far enough beyond all the boundaries of the region you want, to allow the grid boundary to slide around.
I’ve also got a new goal, which I’ll post tomorrow, that pulls the points of a grid onto a surface, but also
lets points slide off the edge of the surface, so you can avoid having to create this extended target surface.

I took your mesh and cut out some of the more problematic areas, released the corners and boundaries, and applied that goal I mentioned above that lets points slide off the edges, and this is what happens: TT_reply.gh (41.4 KB)

If you let it slide around a while it finds a more natural orientation on the surface, and then you can slowly increase the strength of the isothermic goal to enforce that constraint exactly

I thought the aspect ratio of the faces in the initial mesh was not too terrible, but was not thinking about the valences and how odd ones may be causing the issue. I was under the impression that by following the curvature directions, it would actually help to find the direction of the quad edges, hence I put together a adhoc script to find a mesh that is made up of quad faces that roughly align with those directions of a given surface; attached is the portion of the script that @ThomasE also asked about above (isothermic_v1_pre.gh (77.9 KB)). The internalized curves are found using the method Tom Jankowski shared in the comment section of this post. The network is meshed with Weaverbird’s Mesh from Lines and then relaxed using K2 and the resultant mesh was what I first shared yesterday.

Might you have any advice on perhaps combining the two workflow instead of separating them into two solver instances? (Also, what is the Range input doing in the slideoff goal?) I was thinking that I could probably just project/pull a square grid onto the initial mesh and let the isothermic goal finds the natural orientations, but am not getting a meaningful result so far (it seems like the initial mesh after your adjustments is still performing better than just a simple square grid?): isothermic_v2.gh (41.5 KB)

The ‘Range’ input in the SlideOff goal is the maximum search distance. For each point in the goal, if it is within this range of the target mesh, and its closest point on the mesh is not on the boundary of the mesh, it gets pulled towards that point.

Optimising the quad mesh geometry at the same time as generating the connectivity would indeed be great. Fully automatic high quality quad meshing remains a tough problem, and it is often necessary to manually design the mesh a bit to get a good result.

Looking at how you generate your initial mesh, it seems a large part of the problem is that the curvature field does not match the boundaries of your surface, yet you are forcing the grid to.

Also as I mentioned above, to match a principal curvature field, only even valence irregular vertices are allowable, but this process is creating several valence 3 and 5 vertices that shouldn’t be there. It looks from the lines you’ve traced there that the only irregular vertex that should really be there is that valence 6 towards the top left (and possibly another 6 in the top right corner, though that might end up outside the boundary).
What I’d do here is create a coarse quad version of that initial mesh manually informed by this curvature field, then subdivide before optimising.

Thank you very much, Daniel.
This is particularly illuminating for me:

Does this imply that if the field, or the subdivision, simultaneously approximately aligns with the curvature and also the boundary, it would be an ideal starting geometry?

Regarding the workflow to obtain a curvature aligned quad mesh as a prerequisite though, is there anything you’d change from what was shown?

If the input surface has its principal curvature directions matching its boundaries, then great - that makes things easier.
Frequently though we have to deal with geometry where this isn’t the case, meaning a planar quad mesh can’t possibly align with both the boundary and the curvature field, since they conflict.

For instance - this patch of a cylinder - it would be impossible to make a planar quad mesh aligning with the boundary, since it is diagonal to the curvature directions:

In these cases it is better to make a mesh matching the curvature directions, but extending beyond the boundaries. It won’t be possible to have full quads meeting the boundary, but you can sometimes still shift the vertices a little to make it node out cleanly with triangular half quads.

That makes a lot of sense - Enabling the finding of a solution by first extending the geometry is something that I haven’t really been thinking about or am used to, so the approaches have been limited; thanks a lot again for all the explanations!

I would like to ask about circle packing in quads.

When circle packed mesh has a quad dual mesh with also packed circles? Is it always a case: when a quad mesh have incircles and its dual has incircles too, or it happens for certain cases.

I am asking this because I looking for a quad mesh offset, that has planar lattice.

For these S-Isothermic meshes (each quad has an incircle, and the points of tangency of these incircles match between adjacent quads), in the plane, you can always draw a circle through the points of tangency around a vertex to form another circle packing (shown in red below).

You can also apply Möbius transformations to these meshes in the plane, so that they instead lie tangent to a sphere, and the red circles stay tangent.

However, I believe that these are the only cases where the points of tangency also lie on circles like this - for S-Isothermic meshes in general, each quad still has an incircle, but the points of tangency only lie on a common sphere, not on the same plane.

Unfortunately conical meshes are not preserved by Möbius transformations.

Circular meshes are though.
(because Möbius transformations take circles to circles, and preserve angles)

Circular meshes also have an offset mesh, with constant vertex-vertex distance(in contrast with the face-face offset with conical meshes).

So far I only added tools for face-face offsets from conical meshes, but it should be possible to add vertex-vertex offsets too (just need to calculate the correct normal). There is also a way to convert between circular and conical meshes with a sort of dual operation using reflections about the edges, and I was planning to add a tool for this at some point.

I’ll also try and post some examples for conical meshes. At the moment this is probably the easiest route (depending what kind of surfaces this is on, and what offsets or support structures you need)

Here are a couple of examples with the Conicalize goal.
Note that (unlike the CyclicQuad and Isothermic goals) this goal doesn’t ensure planarity on its own - you need to add a Planarize goal too. This is because there are some applications where it is useful to apply the angle condition for conical meshes but on non planar quads (in particular, asymptotic gridshells).
Also shown here is the face-face offset mesh component generating torsion free beam layouts.

One shows something you can sculpt by dragging points around, while the other pulls the mesh onto a target surface. With both you start with the conical/planar strength low while shaping, and slide it all the way up at the end. The offset component will give strange results before the mesh is properly conical. conical_demo_pull.gh (21.7 KB) conical_demo.gh (52.0 KB)

…and here’s a similar example with a circular mesh, also including generation of the vertex-vertex offset mesh: circular_mesh_demo.gh (21.3 KB)
(I realised that for V-V offset, the standard Weaverbird offset works)

I believe mobius transform cannot maintain the planarity of hex meshes as in quads?
The only way is to go through relaxation (for the case of simple sphere)?

After simple planarization (projection to plane 300 iterations) I used Mesh.Offset method from Rhino.Common to offset mesh in both directions.
The lofting edges does not produces planar extrusion.

I attached the grasshopper file with transformation. Planarization.gh (34.2 KB)

Question:
Is there specific method to calculate the vertex normal to offset mesh edges that will remain planar?

Möbius transformations will only preserve planar hexagons if their vertices start out on a common circle - so only the regular hexagonal grid or other Möbius transformations of it (as shown in this post).

The correct vertex normal to use for the offset to ensure planar beams is the intersection of the bisecting planes between each of the surrounding faces.

(Actually this is true for V-V offsets of quad conical meshes too - I think maybe it was only a coincidence that it was working with the standard offset in the example I posted above)