I want to be able to generate a quadrilateral surface mesh that is highly regular (each face has, as far as possible, the same area) and aligned with the surface boundary.
The following test .geo file simplifies the type of intended use case:
lc = 0.1;
// vertices.
Point(1) = {0, 0, 0, lc};
Point(2) = {0.5, 0, 0, lc};
Point(3) = {1.0, 0, 0, lc};
Point(4) = {1.0, 0.5, 0.5, lc};
Point(5) = {1.0, 1.0, 1.0, lc};
Point(6) = {1.0, 1.5, 0.5, lc};
Point(7) = {1.0, 2.0, 0.0, lc};
Point(8) = {0.5, 2.0, 0.0, lc};
Point(9) = {0.0, 2.0, 0.0, lc};
Point(10) = {0.0, 1.5, 0.5, lc};
Point(11) = {0.0, 1.0, 1.0, lc};
Point(12) = {0.0, 0.5, 0.5, lc};
// curves.
Spline(1) = {1,2,3};
Spline(2) = {3,4,5,6,7};
Spline(3) = {7,8,9};
Spline(4) = {9,10,11,12,1};
Physical Line("bottom") = {1};
Physical Line("top") = {3};
Curve Loop(1) = {2, 3, 4, 1};
//surface.
Transfinite Curve{1} = 20
Transfinite Surface(1) = {2,3,4,1};
Physical Surface("mysurface") = {1};
When I load this .geo file into gmsh gui (v 4.3.0) and run mesh 1D then 2D (Frontal-Delaunay option) and finally 2D recombination (Blossom option) commands the resulting surface mesh is not that regular:
The console log shows:
Info : Meshing 1D...
Info : Meshing curve 1 (Nurb)
Info : Meshing curve 2 (Nurb)
Info : Meshing curve 3 (Nurb)
Info : Meshing curve 4 (Nurb)
Info : Done meshing 1D (0.008326 s)
Info : 70 vertices 74 elements
Info : Meshing 2D...
Info : Meshing surface 1 (Surface, Frontal)
Info : Done meshing 2D (0.013711 s)
Info : 272 vertices 538 elements
Info : Recombining 2D mesh...
Info : Blossom: 665 internal 62 closed
Info : Blossom recombination completed (0.012128 s): 230 quads, 0 triangles, 0 invalid quads, 0 quads with Q < 0.1, avg Q = 0.799983, min Q = 0.502415
Info : Done recombining 2D mesh (0.012205 s)
I suspect this maybe due to my relative inexperience with geo/gmsh. Advice appreciated.
For this example, I would highly recommend the new (experimental) meshing option in GMSH: 9 – packing for parallelograms.
According to the release notes, this option appeared in GMSH at least in 4.1.1:
4.1.2 (January 21, 2019): fixed full-quad subdivision if Mesh.SecondOrderLinear
is set; fixed packing of parallelograms regression in 4.1.1.
With this code:
Mesh.Algorithm = 9; // Packing for Parallelograms
lc = 0.1;
// vertices.
Point(1) = {0, 0, 0, lc};
Point(2) = {0.5, 0, 0, lc};
Point(3) = {1.0, 0, 0, lc};
Point(4) = {1.0, 0.5, 0.5, lc};
Point(5) = {1.0, 1.0, 1.0, lc};
Point(6) = {1.0, 1.5, 0.5, lc};
Point(7) = {1.0, 2.0, 0.0, lc};
Point(8) = {0.5, 2.0, 0.0, lc};
Point(9) = {0.0, 2.0, 0.0, lc};
Point(10) = {0.0, 1.5, 0.5, lc};
Point(11) = {0.0, 1.0, 1.0, lc};
Point(12) = {0.0, 0.5, 0.5, lc};
// curves.
Spline(1) = {1,2,3};
Spline(2) = {3,4,5,6,7};
Spline(3) = {7,8,9};
Spline(4) = {9,10,11,12,1};
Physical Line("bottom") = {1};
Physical Line("top") = {3};
Curve Loop(1) = {2, 3, 4, 1};
Surface(1) = {1};
Recombine Surface{1};
Physical Surface("mysurface") = {1};
I am able to generate the following mesh:
Notice slight modifications to your GEO file:
I removed Transfinite Surfaces
I explicitly setup the meshing algorithm and recombination inside the GEO
which I had to do to make this model work on my version of GMSH.
Related
I'm trying to setup a CARenderer to draw into a mtlTexture, but all my attempts to get this working in a playground don't draw anything.
The resulting image is solid red, the yellow layer doesn't seem to be rendered at all.
Here's the simplest version of what I've tried:
import QuartzCore
import Metal
let layerTest = CATextLayer()
layerTest.frame = .init(origin: .zero, size: .init(width: 1920, height: 1080))
layerTest.string = "TEST"
layerTest.foregroundColor = .black
layerTest.backgroundColor = CGColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0)
layerTest.position = CGPoint(x:0.0, y:0.0)
layerTest.anchorPoint = CGPoint(x:0.0, y:0.0)
layerTest.masksToBounds = true
let device = MTLCreateSystemDefaultDevice()!
let context = CIContext(mtlDevice: device)
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: 1920, height: 1080, mipmapped: false)
textureDescriptor.usage = [.unknown]
textureDescriptor.storageMode = .private
let bytes = UnsafeMutablePointer<UInt8>.allocate(capacity: 1920 * 1080 * 4)
//fill the buffer with red
let pattern = UnsafeMutablePointer<UInt8>.allocate(capacity: 4)
(pattern + 0).initialize(to: 255)
(pattern + 1).initialize(to: 0)
(pattern + 2).initialize(to: 0)
(pattern + 3).initialize(to: 255)
memset_pattern4(bytes, pattern, 1920 * 1080 * 4)
let mtlBuffer = device.makeBuffer(bytes: bytes, length: 1920 * 1080 * 4)!
let mtlTexture = mtlBuffer.makeTexture(descriptor: textureDescriptor, offset: 0, bytesPerRow: 1920 * 4)!
let render = CARenderer(mtlTexture: mtlTexture)
render.bounds = layerTest.frame
render.layer = layerTest
render.setDestination(mtlTexture)
render.beginFrame(atTime: CACurrentMediaTime(), timeStamp: nil)
render.addUpdate(render.bounds)
render.render()
render.endFrame()
let ciImage = CIImage(mtlTexture: mtlTexture)!
let cgImage: CGImage = context.createCGImage(ciImage, from: ciImage.extent)! //<- this is just red frame
I submitted this to apple DTS and got a reply:
Setting the root layer of the CARenderer requires one implicit CATransaction to transfer ownership of the layer tree to the CARenderer’s context. CARenderer frame render methods will not work correctly until this ownership transfer is complete.
To complete the current transaction we call flush() and then commit() it:
render.layer = layerTest
CATransaction.flush()
CATransaction.commit()
Adding these two lines resolved the issue for me.
Based on this example (Remeshing a Polyhedral Domain with Surfaces in the 3D Mesh Generation Manual), I want to mesh a cube-shaped domain with its midplane protected (see the code below). However, make_mesh_3 throws an assertion violation:
CGAL ERROR: assertion violation!
Expr: minimal_size_ > 0 || sq_d > 0
Nevermind the triviality of the example, it's just to show the problem. Based on this discussion, I think the issue is that detect_features creates polylines that intersect each other (perimeter of the midplane intersects the cube edges, and both are added as features).
Are intersecting polyhedra not allowed in the meshdomain? If so, is there a way to access and manipulate the results of detect_features to process conflicting features? I'm trying to figure out how polylines are stored in the mesh domain, but I'm getting nowhere.
Cube with Midplane
// --- External Includes ---
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_polyhedron_3.h>
#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/make_mesh_3.h>
// CGAL types
namespace cgal {
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using ConcurrencyTag = CGAL::Sequential_tag;
using PolyhedronSurface = CGAL::Mesh_polyhedron_3<Kernel>::type;
using MeshDomain = CGAL::Polyhedral_mesh_domain_with_features_3<Kernel>;
using Tr = CGAL::Mesh_triangulation_3<MeshDomain,CGAL::Default,ConcurrencyTag>::type;
using Triangulation = CGAL::Mesh_complex_3_in_triangulation_3<Tr>;
using MeshCriteria = CGAL::Mesh_criteria_3<Tr>;
using Point = cgal::MeshDomain::Point_3;
}
int main()
{
// Define points for the cube and midPlane
std::vector<cgal::Point> points =
{
cgal::Point( 1.0, 0.0, 0.0),
cgal::Point( 1.0, 1.0, 0.0),
cgal::Point( 0.0, 1.0, 0.0),
cgal::Point( 0.0, 0.0, 0.0),
cgal::Point( 1.0, 0.0, 1.0),
cgal::Point( 1.0, 1.0, 1.0),
cgal::Point( 0.0, 1.0, 1.0),
cgal::Point( 0.0, 0.0, 1.0),
cgal::Point( 1.0, 0.0, 0.5),
cgal::Point( 1.0, 1.0, 0.5),
cgal::Point( 0.0, 1.0, 0.5),
cgal::Point( 0.0, 0.0, 0.5)
};
// Create polyhedra
cgal::PolyhedronSurface cube, midPlane;
cube.make_triangle( points[0], points[3], points[1]);
cube.make_triangle( points[1], points[3], points[2]);
cube.make_triangle( points[4], points[5], points[7]);
cube.make_triangle( points[5], points[6], points[7]);
cube.make_triangle( points[0], points[4], points[3]);
cube.make_triangle( points[3], points[4], points[7]);
cube.make_triangle( points[1], points[2], points[5]);
cube.make_triangle( points[2], points[6], points[5]);
cube.make_triangle( points[2], points[3], points[6]);
cube.make_triangle( points[3], points[7], points[6]);
cube.make_triangle( points[0], points[1], points[5]);
cube.make_triangle( points[0], points[5], points[4]);
midPlane.make_triangle( points[8], points[9], points[10]);
midPlane.make_triangle( points[8], points[10], points[11]);
// Triangulation
cgal::MeshDomain meshDomain( midPlane, cube );
meshDomain.detect_features();
cgal::MeshCriteria meshCriteria( CGAL::parameters::facet_angle = 30,
CGAL::parameters::edge_size = 0.2 );
auto triangulation = CGAL::make_mesh_3<cgal::Triangulation>( meshDomain,
meshCriteria );
return 0;
}
From here:
This surface must be free of intersection.
In your case, the bounding box and midplane intersect. If you want to mesh this geometry, those meshes need to be connected together ahead of meshing.
If you look at the example in section 3.3.2 here, you see a similar example with an internal geometry that is enclosed away from the bounding box.
detect_features() doesn't intersect non-intersecting inputs: it just looks for features in an existing mesh (and adds them to the domain so meshing will respect them).
For your cube it should be easy to construct a polyhedral complex, that is three surfaces, namely the upper and lower part of the cube, and the mid-square. Have a look at this example.
I want to draw a simple triangle and it crashes after I am trying to create MTLBuffer.
static float vertexes[] = {
0.0, 0.5, 0.0,
-0.5f, -0.5f, 0.0,
0.5, -0.5f, 0.0
};
id <MTLBuffer> buffer = [self.device newBufferWithBytes:vertexes
length:sizeof(vertexes) options:MTLResourceStorageModePrivate];
Here is the assert:
-[MTLDebugDevice newBufferWithBytes:length:options:]:392: failed assertion `storageModePrivate incompatible with ...WithBytes variant of newBuffer'
So how to create a buffer from the vertexes using MTLResourceStorageModePrivate option?
You must create a temporary blit buffer and use it to copy the contents to the private buffer. Here's example code:
buffer = [self.device newBufferWithLength:sizeof( vertexes )
options:MTLResourceStorageModePrivate];
id<MTLBuffer> blitBuffer = [self.device newBufferWithBytes:vertexes
length:sizeof( vertexes )
options:MTLResourceCPUCacheModeDefaultCache];
id <MTLCommandBuffer> cmd_buffer = [commandQueue commandBuffer];
id <MTLBlitCommandEncoder> blit_encoder = [cmd_buffer blitCommandEncoder];
[blit_encoder copyFromBuffer:blitBuffer
sourceOffset:0
toBuffer:buffer
destinationOffset:0
size:sizeof( vertexes )];
[blit_encoder endEncoding];
[cmd_buffer commit];
[cmd_buffer waitUntilCompleted];
I am trying to build a quite simple mesh. I have a box:
box_size = 50;
lb = 10.;
Point(1) = {-box_size/2, -box_size/2, -box_size/2, lb};
Point(2) = {box_size/2, -box_size/2, -box_size/2, lb};
Point(3) = {box_size/2, box_size/2, -box_size/2, lb};
Point(4) = {-box_size/2, box_size/2, -box_size/2, lb};
Line(1) = {1, 2};
Line(2) = {2, 3};
Line(3) = {3, 4};
Line(4) = {4, 1};
Line Loop(5) = {1, 2, 3, 4};
Plane Surface(6) = {5};
Extrude {0, 0, box_size} {
Surface{6};
}
This works quite well and gmsh is happy to (3D) mesh it.
The problem is that I would to ensure that certain points inside the box are node points. So my question is, how do I ensure that points, like these
lc = 10;
Point(5) = {7.150548, 1.000000, -6.990684, lc};
Point(6) = {-4.438894, 1.000000, -8.960816, lc};
Point(7) = {-9.893936, 1.000000, 1.452595, lc};
Point(8) = {-1.675894, 1.000000, 9.858569, lc};
Point(9) = {8.858176, 1.000000, 4.640336, lc};
Point(10) = {1.675894, 4.750000, -9.858569, lc};
Point(11) = {-8.858176, 4.750000, -4.640336, lc};
Point(12) = {-7.150548, 4.750000, 6.990684, lc};
Point(13) = {4.438894, 4.750000, 8.960816, lc};
Point(14) = {9.893936, 4.750000, -1.452595, lc};
Point(15) = {7.150548, 8.500000, -6.990684, lc};
are part of the mesh?
The reason I need this is that I need to impose boundary conditions at these specific points.
If this is easier in another software, I am also happy to change. I hope someone can help.
Thank you in advance.
Quite a late answer but it might help anyway. If the index of the Point is p and the one of the volume or surface is q, then :
Point{p} In Volume {q};
Or if it is on a Surface :
Point{p} In Surface {q};
I believe the only option is to divide the structure such that they feature your points and mesh the structure after that. Now you can apply your loads and conditions on the Physical Points or Physical Lines.
Example: if you have a cube you want to mesh. And boundary condition is on the plane in the centre, then divide the cube at that plane. Make the plane a Physical entity, I.e., Physical Surface(14) = {midplane number}.mesh it all and you are good to go!
I have the following Vertex struct in my OpenGL ES app :
typedef struct Vertex {
float Position[3];
float Color[4];
} Vertex;
In my header I then declare :
Vertex *Vertices;
Then in my init method :
int array = 4;
Vertices = (Vertex *)malloc(array * sizeof(Vertex));
I then later setup the mesh as follows, where vertices array in this case has 4 vertices :
- (void)setupMesh {
int count = 0;
for (VerticeObject * object in verticesArray) {
Vertices[count].Position[0] = object.x;
Vertices[count].Position[1] = object.y;
Vertices[count].Position[2] = object.z;
Vertices[count].Color[0] = 0.9f;
Vertices[count].Color[1] = 0.9f;
Vertices[count].Color[2] = 0.9f;
Vertices[count].Color[3] = 1.0f;
count ++;
}
}
Can anyone spot what I am doing wrong here ? When I pass this Vertices object to OpenGL nothing is drawn, whereas if I hard code the Vertices array as :
Vertex Vertices [] = {
{{0.0, 0.0, 0}, {0.9, 0.9, 0.9, 1}},
{{0.0 , 0.0 , 0}, {0.9, 0.9, 0.9, 1}},
{{0.0, 0.0, 0}, {0.9, 0.9, 0.9, 1}},
{{0.0, 0.0, 0}, {0.9, 0.9, 0.9, 1}},
};
Everything works ?
I think the problem is that before you had a array allocated on the stack where now you have a pointer(memory address) to a block of memory on the heap. So when you wright stuff like sizeof(Vertices) your original sizeof(Vertices) would result in 4 vertices each holding 3 floats position and 4 floats color -> 4 * (3 + 4) * 4(float = 4 bytes) = 112 bytes. Where sizeof(aPointer) = 4 bytes. OpenGL is a C library an not super easy to work with so you should really brush up on you C skills before trying to get it running. Also there in a GLKView class now days that will make all the setup allot easier.
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
Try to allocate same size as the array of vertices. In your case 4 * sizeof(Vertex).
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 4, Vertices, GL_STATIC_DRAW);
If that doesn't work you can easily fix the problem by replacing your dynamically allocated array for a statically allocated since you know at compile time how big it needs to be.
Vertex Vertices[4];
Then set the values in your loop as you do.