1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
package engine
import (
"image"
_ "image/jpeg"
_ "image/png"
"math"
"github.com/mumax/3/data"
"github.com/mumax/3/httpfs"
"github.com/mumax/3/util"
)
func init() {
DeclFunc("Ellipsoid", Ellipsoid, "3D Ellipsoid with axes in meter")
DeclFunc("Ellipse", Ellipse, "2D Ellipse with axes in meter")
DeclFunc("Cone", Cone, "3D Cone with diameter and height in meter. The base is at z=0. If the height is positive, the tip points in the +z direction.")
DeclFunc("Cylinder", Cylinder, "3D Cylinder with diameter and height in meter")
DeclFunc("Circle", Circle, "2D Circle with diameter in meter")
DeclFunc("Cuboid", Cuboid, "Cuboid with sides in meter")
DeclFunc("Rect", Rect, "2D rectangle with size in meter")
DeclFunc("Square", Square, "2D square with size in meter")
DeclFunc("Triangle", Triangle, "2D triangle with vertices (x0, y0), (x1, y1) and (x2, y2)")
DeclFunc("XRange", XRange, "Part of space between x1 (inclusive) and x2 (exclusive), in meter")
DeclFunc("YRange", YRange, "Part of space between y1 (inclusive) and y2 (exclusive), in meter")
DeclFunc("ZRange", ZRange, "Part of space between z1 (inclusive) and z2 (exclusive), in meter")
DeclFunc("Layers", Layers, "Part of space between cell layer1 (inclusive) and layer2 (exclusive), in integer indices")
DeclFunc("Layer", Layer, "Single layer (along z), by integer index starting from 0")
DeclFunc("Universe", Universe, "Entire space")
DeclFunc("Cell", Cell, "Single cell with given integer index (i, j, k)")
DeclFunc("ImageShape", ImageShape, "Use black/white image as shape")
DeclFunc("GrainRoughness", GrainRoughness, "Grainy surface with different heights per grain "+
"with a typical grain size (first argument), minimal height (second argument), and maximal "+
"height (third argument). The last argument is a seed for the random number generator.")
}
// geometrical shape for setting sample geometry
type Shape func(x, y, z float64) bool
// Ellipsoid with given diameters
func Ellipsoid(diamx, diamy, diamz float64) Shape {
return func(x, y, z float64) bool {
return sqr64(x/diamx)+sqr64(y/diamy)+sqr64(z/diamz) <= 0.25
}
}
func Ellipse(diamx, diamy float64) Shape {
return Ellipsoid(diamx, diamy, math.Inf(1))
}
// 3D Cone with base at z=0 and vertex at z=height.
func Cone(diam, height float64) Shape {
return func(x, y, z float64) bool {
return (height-z)*z >= 0 && sqr64(x/diam)+sqr64(y/diam) <= 0.25*sqr64(1-z/height)
}
}
func Circle(diam float64) Shape {
return Cylinder(diam, math.Inf(1))
}
// cylinder along z.
func Cylinder(diam, height float64) Shape {
return func(x, y, z float64) bool {
return z <= height/2 && z >= -height/2 &&
sqr64(x/diam)+sqr64(y/diam) <= 0.25
}
}
// 3D Rectangular slab with given sides.
func Cuboid(sidex, sidey, sidez float64) Shape {
return func(x, y, z float64) bool {
rx, ry, rz := sidex/2, sidey/2, sidez/2
return x < rx && x > -rx && y < ry && y > -ry && z < rz && z > -rz
}
}
// 2D Rectangle with given sides.
func Rect(sidex, sidey float64) Shape {
return func(x, y, z float64) bool {
rx, ry := sidex/2, sidey/2
return x < rx && x > -rx && y < ry && y > -ry
}
}
// 2D square with given side.
func Square(side float64) Shape {
return Rect(side, side)
}
// 2D triangle with given vertices.
func Triangle(x0, y0, x1, y1, x2, y2 float64) Shape {
denom := x0*(y1-y2) + x1*(y2-y0) + x2*(y0-y1) // 2 * area
if denom == 0 {
return func(x, y, z float64) bool { return false }
}
A2m1 := 1 / denom
Sc := A2m1 * (y0*x2 - x0*y2)
Sx := A2m1 * (y2 - y0)
Sy := A2m1 * (x0 - x2)
Tc := A2m1 * (x0*y1 - y0*x1)
Tx := A2m1 * (y0 - y1)
Ty := A2m1 * (x1 - x0)
return func(x, y, z float64) bool {
// barycentric coordinates
s := Sc + Sx*x + Sy*y
t := Tc + Tx*x + Ty*y
return ((0 <= s) && (0 <= t) && (s+t <= 1))
}
}
// All cells with x-coordinate between a and b
func XRange(a, b float64) Shape {
return func(x, y, z float64) bool {
return x >= a && x < b
}
}
// All cells with y-coordinate between a and b
func YRange(a, b float64) Shape {
return func(x, y, z float64) bool {
return y >= a && y < b
}
}
// All cells with z-coordinate between a and b
func ZRange(a, b float64) Shape {
return func(x, y, z float64) bool {
return z >= a && z < b
}
}
// Cell layers #a (inclusive) up to #b (exclusive).
func Layers(a, b int) Shape {
Nz := Mesh().Size()[Z]
if a < 0 || a > Nz || b < 0 || b < a {
util.Fatal("layers ", a, ":", b, " out of bounds (0 - ", Nz, ")")
}
c := Mesh().CellSize()[Z]
z1 := Index2Coord(0, 0, a)[Z] - c/2
z2 := Index2Coord(0, 0, b)[Z] - c/2
return ZRange(z1, z2)
}
func Layer(index int) Shape {
return Layers(index, index+1)
}
// Single cell with given index
func Cell(ix, iy, iz int) Shape {
c := Mesh().CellSize()
pos := Index2Coord(ix, iy, iz)
x1 := pos[X] - c[X]/2
y1 := pos[Y] - c[Y]/2
z1 := pos[Z] - c[Z]/2
x2 := pos[X] + c[X]/2
y2 := pos[Y] + c[Y]/2
z2 := pos[Z] + c[Z]/2
return func(x, y, z float64) bool {
return x > x1 && x < x2 &&
y > y1 && y < y2 &&
z > z1 && z < z2
}
}
func Universe() Shape {
return universe
}
// The entire space.
func universe(x, y, z float64) bool {
return true
}
func ImageShape(fname string) Shape {
r, err1 := httpfs.Open(fname)
CheckRecoverable(err1)
defer r.Close()
img, _, err2 := image.Decode(r)
CheckRecoverable(err2)
width := img.Bounds().Max.X
height := img.Bounds().Max.Y
// decode image into bool matrix for fast pixel lookup
inside := make([][]bool, height)
for iy := range inside {
inside[iy] = make([]bool, width)
}
for iy := 0; iy < height; iy++ {
for ix := 0; ix < width; ix++ {
r, g, b, a := img.At(ix, height-1-iy).RGBA()
if a > 128 && r+g+b < (0xFFFF*3)/2 {
inside[iy][ix] = true
}
}
}
// stretch the image onto the gridsize
c := Mesh().CellSize()
cx, cy := c[X], c[Y]
N := Mesh().Size()
nx, ny := float64(N[X]), float64(N[Y])
w, h := float64(width), float64(height)
return func(x, y, z float64) bool {
ix := int((w/nx)*(x/cx) + 0.5*w)
iy := int((h/ny)*(y/cy) + 0.5*h)
if ix < 0 || ix >= width || iy < 0 || iy >= height {
return false
} else {
return inside[iy][ix]
}
}
}
func VoxelShape(voxels *data.Slice, a, b, c float64) Shape {
//component dimension check, expect 1D points
if voxels.NComp() != 1 {
util.Fatal("Voxel array fed has a wrong value dimension: ", voxels.NComp(), ", Aborting!")
}
//cut FP array into bool array
arrSize := voxels.Size()
voxelArr := make([]bool, arrSize[0]*arrSize[1]*arrSize[2])
for ix := 0; ix < arrSize[0]; ix++ {
for iy := 0; iy < arrSize[1]; iy++ {
for iz := 0; iz < arrSize[2]; iz++ {
voxelArr[iz*arrSize[0]*arrSize[1]+iy*arrSize[0]+ix] = voxels.Get(0, ix, iy, iz) > 0.5
}
}
}
//the predicate
voxelSize := [3]float64{a, b, c}
return func(x, y, z float64) bool {
var ind [3]int
coord := [3]float64{x, y, z}
for c := 0; c < 3; c++ {
//truncation applies floor by default
ind[c] = int(coord[c]/voxelSize[c] + float64(arrSize[c])/2)
if ind[c] < 0 || ind[c] >= arrSize[c] {
//there is no geometry outside of the imported array
return false
}
}
//if not fallen through check against the previous array
return voxelArr[ind[2]*arrSize[0]*arrSize[1]+ind[1]*arrSize[0]+ind[0]]
}
}
func GrainRoughness(grainsize, zmin, zmax float64, seed int) Shape {
t := newTesselation(grainsize, 256, int64(seed))
return func(x, y, z float64) bool {
if z <= zmin {
return true
}
if z >= zmax {
return false
}
r := t.RegionOf(x, y, z)
return (z-zmin)/(zmax-zmin) < (float64(r) / 256)
}
}
// Transl returns a translated copy of the shape.
func (s Shape) Transl(dx, dy, dz float64) Shape {
return func(x, y, z float64) bool {
return s(x-dx, y-dy, z-dz)
}
}
// Infinitely repeats the shape with given period in x, y, z.
// A period of 0 or infinity means no repetition.
func (s Shape) Repeat(periodX, periodY, periodZ float64) Shape {
return func(x, y, z float64) bool {
return s(fmod(x, periodX), fmod(y, periodY), fmod(z, periodZ))
}
}
func fmod(a, b float64) float64 {
if b == 0 || math.IsInf(b, 1) {
return a
}
if math.Abs(a) > b/2 {
return sign(a) * (math.Mod(math.Abs(a+b/2), b) - b/2)
} else {
return a
}
}
// Scale returns a scaled copy of the shape.
func (s Shape) Scale(sx, sy, sz float64) Shape {
return func(x, y, z float64) bool {
return s(x/sx, y/sy, z/sz)
}
}
// Rotates the shape around the Z-axis, over θ radians.
func (s Shape) RotZ(θ float64) Shape {
cos := math.Cos(θ)
sin := math.Sin(θ)
return func(x, y, z float64) bool {
x_ := x*cos + y*sin
y_ := -x*sin + y*cos
return s(x_, y_, z)
}
}
// Rotates the shape around the Y-axis, over θ radians.
func (s Shape) RotY(θ float64) Shape {
cos := math.Cos(θ)
sin := math.Sin(θ)
return func(x, y, z float64) bool {
x_ := x*cos - z*sin
z_ := x*sin + z*cos
return s(x_, y, z_)
}
}
// Rotates the shape around the X-axis, over θ radians.
func (s Shape) RotX(θ float64) Shape {
cos := math.Cos(θ)
sin := math.Sin(θ)
return func(x, y, z float64) bool {
y_ := y*cos + z*sin
z_ := -y*sin + z*cos
return s(x, y_, z_)
}
}
// Union of shapes a and b (logical OR).
func (a Shape) Add(b Shape) Shape {
return func(x, y, z float64) bool {
return a(x, y, z) || b(x, y, z)
}
}
// Intersection of shapes a and b (logical AND).
func (a Shape) Intersect(b Shape) Shape {
return func(x, y, z float64) bool {
return a(x, y, z) && b(x, y, z)
}
}
// Inverse (outside) of shape (logical NOT).
func (s Shape) Inverse() Shape {
return func(x, y, z float64) bool {
return !s(x, y, z)
}
}
// Removes b from a (logical a AND NOT b)
func (a Shape) Sub(b Shape) Shape {
return func(x, y, z float64) bool {
return a(x, y, z) && !b(x, y, z)
}
}
// Logical XOR of shapes a and b
func (a Shape) Xor(b Shape) Shape {
return func(x, y, z float64) bool {
A, B := a(x, y, z), b(x, y, z)
return (A || B) && !(A && B)
}
}
func sqr64(x float64) float64 { return x * x }
|