From c638e6aa158954b3bc2e162649d20e38e2122907 Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 11:15:56 +0200 Subject: [PATCH 1/7] add CompleteGrainsAtShapeEdge functionality --- engine/ext_make3dgrains.go | 44 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/engine/ext_make3dgrains.go b/engine/ext_make3dgrains.go index 0d8d8961f..6fd72bdf1 100644 --- a/engine/ext_make3dgrains.go +++ b/engine/ext_make3dgrains.go @@ -7,8 +7,11 @@ import ( "math/rand" ) +var CompleteGrainsAtShapeEdge = false // complete the voronoi grains instead of cutting them off at the shape edge + func init() { DeclFunc("ext_make3dgrains", Voronoi3d, "3D Voronoi tesselation over shape (grain size, starting region number, num regions, shape, seed)") + DeclVar("CompleteGrainsAtShapeEdge", &CompleteGrainsAtShapeEdge, "Enables completion of grains beyond shape edge (default=false)") } func Voronoi3d(grainsize float64, startRegion int, numRegions int, inputShape Shape, seed int) { @@ -107,7 +110,7 @@ func (t *tesselation3d) tabulateCells() []cellLocs { y := cell.Y() z := cell.Z() - if t.shape(x, y, z) { + if (t.shape(x, y, z)|| CompleteGrainsAtShapeEdge) { cells = append(cells, cellLocs{x, y, z}) } } @@ -120,23 +123,30 @@ func (t *tesselation3d) tabulateCells() []cellLocs { return cells } -// Find the nearest Voronoi center to the point (x, y, z). Only points inside the given shape will be -// assigned a region. + func (t *tesselation3d) RegionOf(x, y, z float64) int { - if t.shape(x, y, z) { - nearest := center3d{x, y, z, 0} - mindist := math.Inf(1) - for _, c := range t.centers { - dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z) - if dist < mindist { - nearest = c - mindist = dist - } - } - return int(nearest.region) - } else { - return -1 //When the regions are rendered, any region < 0 will not be rastered. - } + // Check if the point is within the shape or edge grains should be completed + if !(t.shape(x, y, z) || CompleteGrainsAtShapeEdge) { + return -1 // Regions < 0 won't be rastered + } + + // Find the nearest center point to the (x, y, z) position + nearest := center3d{x, y, z, 0} + mindist := math.Inf(1) + for _, c := range t.centers { + dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z) + if dist < mindist { + nearest = c + mindist = dist + } + } + + // Check if the nearest point's region should be returned + if t.shape(x, y, z) || (t.shape(nearest.x, nearest.y, nearest.z) && CompleteGrainsAtShapeEdge) { + return int(nearest.region) + } + + return -1 } // Generate normally distributed numbers; mean = lambda, variance = lambda. If generated number < 0, return 1. From 677d8f9f1bf1124783331885206ab21410ff2295 Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 16:54:53 +0200 Subject: [PATCH 2/7] add test for completegrainsatshapeedge feature --- test/make3dgrains_shape_edge.mx3 | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/make3dgrains_shape_edge.mx3 diff --git a/test/make3dgrains_shape_edge.mx3 b/test/make3dgrains_shape_edge.mx3 new file mode 100644 index 000000000..f8908d0e6 --- /dev/null +++ b/test/make3dgrains_shape_edge.mx3 @@ -0,0 +1,32 @@ +N:=32 +SetMesh(N, N, N, 1e-9,1e-9,1e-9, 0, 0, 0) + +//set material params +alpha=1. +Msat=350e3 +Aex=10e-12 +Ku1=1.0e6 + +//define a spherical shape +diam:=16e-9 +sphere := Ellipsoid(diam,diam,diam) +defregion(1, sphere) + +seed:=238948790 +randSeed(seed) +CompleteGrainsAtShapeEdge=true +ext_make3Dgrains(8e-9, 2, 250, sphere, seed) + +for i:=2; i<254; i+=1 { + anisU.setregion(i, vector(randNorm(),randNorm(),randNorm())) +} + + +//a cell in the sphere for which the nearest voronoi centre also lies in the sphere: nonzero region nr expected +expect("region", regions.getcell(15,15,15), 174, 0) + +//a cell in the sphere for which the nearest voronoi centre lies outside the sphere: region nr 1 expected as it is not affected by make3Dgrains +expect("region", regions.getcell(19,19,15), 1, 0) + +//a cell outside of the sphere for which the neares voronoi centre lies inside the sphere: nonzero region nr expected as it was completed by make3Dgrains +expect("region", regions.getcell(15,6,15), 224, 0) From aa02cd0405b52237e919184b680cea2db3446f97 Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 17:02:58 +0200 Subject: [PATCH 3/7] rename CompleteGrainsAtShapeEdge to GrainCutShape --- engine/ext_make3dgrains.go | 12 ++++++------ test/make3dgrains_shape_edge.mx3 | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/ext_make3dgrains.go b/engine/ext_make3dgrains.go index 6fd72bdf1..a3c8e547f 100644 --- a/engine/ext_make3dgrains.go +++ b/engine/ext_make3dgrains.go @@ -7,11 +7,11 @@ import ( "math/rand" ) -var CompleteGrainsAtShapeEdge = false // complete the voronoi grains instead of cutting them off at the shape edge +var GrainCutShape = false // complete all voronoi grains whose centre lies within the shape. Warning, this also cuts away parts of the shape whose closest voronoi centre lies outside the shape func init() { DeclFunc("ext_make3dgrains", Voronoi3d, "3D Voronoi tesselation over shape (grain size, starting region number, num regions, shape, seed)") - DeclVar("CompleteGrainsAtShapeEdge", &CompleteGrainsAtShapeEdge, "Enables completion of grains beyond shape edge (default=false)") + DeclVar("GrainCutShape", &GrainCutShape, "Adds the complete voronoi grain, if its centre lies within the shape (default=false)") } func Voronoi3d(grainsize float64, startRegion int, numRegions int, inputShape Shape, seed int) { @@ -110,7 +110,7 @@ func (t *tesselation3d) tabulateCells() []cellLocs { y := cell.Y() z := cell.Z() - if (t.shape(x, y, z)|| CompleteGrainsAtShapeEdge) { + if (t.shape(x, y, z)|| GrainCutShape) { cells = append(cells, cellLocs{x, y, z}) } } @@ -125,8 +125,8 @@ func (t *tesselation3d) tabulateCells() []cellLocs { func (t *tesselation3d) RegionOf(x, y, z float64) int { - // Check if the point is within the shape or edge grains should be completed - if !(t.shape(x, y, z) || CompleteGrainsAtShapeEdge) { + // Check if the point is within the shape or if we're cutting the shape along the grains + if !(t.shape(x, y, z) || GrainCutShape) { return -1 // Regions < 0 won't be rastered } @@ -142,7 +142,7 @@ func (t *tesselation3d) RegionOf(x, y, z float64) int { } // Check if the nearest point's region should be returned - if t.shape(x, y, z) || (t.shape(nearest.x, nearest.y, nearest.z) && CompleteGrainsAtShapeEdge) { + if t.shape(x, y, z) || (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape) { return int(nearest.region) } diff --git a/test/make3dgrains_shape_edge.mx3 b/test/make3dgrains_shape_edge.mx3 index f8908d0e6..86f56c641 100644 --- a/test/make3dgrains_shape_edge.mx3 +++ b/test/make3dgrains_shape_edge.mx3 @@ -14,7 +14,7 @@ defregion(1, sphere) seed:=238948790 randSeed(seed) -CompleteGrainsAtShapeEdge=true +GrainCutShape=true ext_make3Dgrains(8e-9, 2, 250, sphere, seed) for i:=2; i<254; i+=1 { From 5d7dc831c2568fae48a26c475cc0981da4e968d1 Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 21:43:18 +0200 Subject: [PATCH 4/7] allow graincutshape to cut away from the shape. now the test succeeds --- engine/ext_make3dgrains.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/ext_make3dgrains.go b/engine/ext_make3dgrains.go index a3c8e547f..77a8bc5be 100644 --- a/engine/ext_make3dgrains.go +++ b/engine/ext_make3dgrains.go @@ -142,7 +142,7 @@ func (t *tesselation3d) RegionOf(x, y, z float64) int { } // Check if the nearest point's region should be returned - if t.shape(x, y, z) || (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape) { + if ((t.shape(x, y, z) && GrainCutShape==false)|| (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape)) { return int(nearest.region) } From dcf1e2fda20c33d74e517626ff0ef8c41cbdcefc Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 23:15:24 +0200 Subject: [PATCH 5/7] use seeded random generator in shufflecells function instead of the unseeded (since go1.20) global one --- engine/ext_make3dgrains.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/ext_make3dgrains.go b/engine/ext_make3dgrains.go index 77a8bc5be..b65745f4b 100644 --- a/engine/ext_make3dgrains.go +++ b/engine/ext_make3dgrains.go @@ -57,9 +57,9 @@ func newTesselation3d(grainsize float64, nRegion int, seed int64, startRegion in // Permutes the slice of cell locations. I don't understand why this needs to be done if we're choosing // random (Intn()) cells out of the slice of cell locations, but hey, it seems to do the trick. -func shuffleCells(src []cellLocs) []cellLocs { +func (t *tesselation3d) shuffleCells(src []cellLocs) []cellLocs { dest := make([]cellLocs, len(src)) - perm := rand.Perm(len(src)) + perm := t.rnd.Perm(len(src)) for i, v := range perm { dest[v] = src[i] } @@ -69,7 +69,7 @@ func shuffleCells(src []cellLocs) []cellLocs { func (t *tesselation3d) makeRandomCenters() { //Make a list of all the cells in the shape. cells := t.tabulateCells() - cells = shuffleCells(cells) + cells = t.shuffleCells(cells) //Choose number of grains to make. Assume volume of grain is given by (4/3)*pi*r^3 shapeVolume := cellVolume() * float64(len(cells)) From 1dfc2c7f3689f5e0955815be8493ee55a36f2ff0 Mon Sep 17 00:00:00 2001 From: jleliaer Date: Thu, 3 Oct 2024 23:30:28 +0200 Subject: [PATCH 6/7] update test to values obtained with seeded random generator --- test/make3dgrains_shape_edge.mx3 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/make3dgrains_shape_edge.mx3 b/test/make3dgrains_shape_edge.mx3 index 86f56c641..0d5fee1e3 100644 --- a/test/make3dgrains_shape_edge.mx3 +++ b/test/make3dgrains_shape_edge.mx3 @@ -23,10 +23,10 @@ for i:=2; i<254; i+=1 { //a cell in the sphere for which the nearest voronoi centre also lies in the sphere: nonzero region nr expected -expect("region", regions.getcell(15,15,15), 174, 0) +expect("region", regions.getcell(15,15,15), 191, 0) //a cell in the sphere for which the nearest voronoi centre lies outside the sphere: region nr 1 expected as it is not affected by make3Dgrains -expect("region", regions.getcell(19,19,15), 1, 0) +expect("region", regions.getcell(9,15,15), 1, 0) -//a cell outside of the sphere for which the neares voronoi centre lies inside the sphere: nonzero region nr expected as it was completed by make3Dgrains -expect("region", regions.getcell(15,6,15), 224, 0) +//a cell outside of the sphere for which the nearest voronoi centre lies inside the sphere: nonzero region nr expected as it was completed by make3Dgrains +expect("region", regions.getcell(16,4,15), 64, 0) From e16583c47d41926e8f1757de377985cef917860a Mon Sep 17 00:00:00 2001 From: Jonathan Maes Date: Fri, 4 Oct 2024 10:19:00 +0200 Subject: [PATCH 7/7] Go fmt ext_make3dgrains.go --- engine/ext_make3dgrains.go | 52 ++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/engine/ext_make3dgrains.go b/engine/ext_make3dgrains.go index b65745f4b..b34f868fc 100644 --- a/engine/ext_make3dgrains.go +++ b/engine/ext_make3dgrains.go @@ -7,7 +7,7 @@ import ( "math/rand" ) -var GrainCutShape = false // complete all voronoi grains whose centre lies within the shape. Warning, this also cuts away parts of the shape whose closest voronoi centre lies outside the shape +var GrainCutShape = false // complete all voronoi grains whose centre lies within the shape. Warning, this also cuts away parts of the shape whose closest voronoi centre lies outside the shape func init() { DeclFunc("ext_make3dgrains", Voronoi3d, "3D Voronoi tesselation over shape (grain size, starting region number, num regions, shape, seed)") @@ -87,8 +87,6 @@ func (t *tesselation3d) makeRandomCenters() { randRegion := t.startRegion + t.rnd.Intn(t.maxRegion) t.centers[p].region = byte(randRegion) } - - return } // Creates a slice of all cells which fall in the shape specified in the constructor. @@ -110,7 +108,7 @@ func (t *tesselation3d) tabulateCells() []cellLocs { y := cell.Y() z := cell.Z() - if (t.shape(x, y, z)|| GrainCutShape) { + if t.shape(x, y, z) || GrainCutShape { cells = append(cells, cellLocs{x, y, z}) } } @@ -123,30 +121,29 @@ func (t *tesselation3d) tabulateCells() []cellLocs { return cells } - func (t *tesselation3d) RegionOf(x, y, z float64) int { - // Check if the point is within the shape or if we're cutting the shape along the grains - if !(t.shape(x, y, z) || GrainCutShape) { - return -1 // Regions < 0 won't be rastered - } - - // Find the nearest center point to the (x, y, z) position - nearest := center3d{x, y, z, 0} - mindist := math.Inf(1) - for _, c := range t.centers { - dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z) - if dist < mindist { - nearest = c - mindist = dist - } - } - - // Check if the nearest point's region should be returned - if ((t.shape(x, y, z) && GrainCutShape==false)|| (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape)) { - return int(nearest.region) - } - - return -1 + // Check if the point is within the shape or if we're cutting the shape along the grains + if !(t.shape(x, y, z) || GrainCutShape) { + return -1 // Regions < 0 won't be rastered + } + + // Find the nearest center point to the (x, y, z) position + nearest := center3d{x, y, z, 0} + mindist := math.Inf(1) + for _, c := range t.centers { + dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z) + if dist < mindist { + nearest = c + mindist = dist + } + } + + // Check if the nearest point's region should be returned + if (t.shape(x, y, z) && !GrainCutShape) || (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape) { + return int(nearest.region) + } + + return -1 } // Generate normally distributed numbers; mean = lambda, variance = lambda. If generated number < 0, return 1. @@ -159,5 +156,4 @@ func (t *tesselation3d) truncNorm(lambda float64) int { } else { return int(ret + 0.5) } - }