-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmeep_csg.py
155 lines (119 loc) · 4.45 KB
/
meep_csg.py
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
"""
VRep implementations for basic shapes
"""
import meep as mp
class GeometryDecoderError(Exception):
pass
def decode_json(data: dict):
try:
if "type" in data:
# Look for the type in the module dict instead of having to deal with a registry system
return globals()[data["type"]].decode(data)
else:
raise GeometryDecoderError
except KeyError:
print("Unable to decode object of type {}".format(data["type"]))
except GeometryDecoderError:
print("There was an error decoding object of type {}".format(data["type"]))
class VolumeRepresentation:
"""
Abstract class for a volume of space
"""
def is_inside(self, pos: mp.Vector3) -> bool:
"""
Abstract method for determining if a point is inside the object
"""
raise NotImplementedError
def intersect(self, *others):
return Intersection(self, *others)
def union(self, *others):
return Union(self, *others)
def subtract(self, *others):
return Subtraction(self, *others)
def material_function(
sdf: VolumeRepresentation, medium: mp.Medium, background: mp.Medium = mp.air
):
return lambda x: medium if sdf.is_inside(x) else background
# ----------------------------------------------------------------
# CSG Operations
# ----------------------------------------------------------------
class VolumeOperation(VolumeRepresentation):
def __init__(self, *children: list[VolumeRepresentation]):
self.children = children
def encode(self):
return {
"type": self.__class__.__name__,
"children": [c.encode() for c in self.children],
}
@classmethod
def decode(cls, data):
if "children" in data:
return cls(*[decode_json(c) for c in data["children"]])
else:
raise GeometryDecoderError
class Union(VolumeOperation):
"""
Geometric union of two objects
"""
def is_inside(self, pos: mp.Vector3) -> bool:
return any([c.is_inside(pos) for c in self.children])
class Intersection(VolumeOperation):
"""
Geometric intersection of two objects
"""
def is_inside(self, pos: mp.Vector3) -> bool:
return all([c.is_inside(pos) for c in self.children])
class Subtraction(VolumeOperation):
"""
Geometric subtraction of one object from another
"""
def is_inside(self, pos: mp.Vector3) -> bool:
return self.children[0].is_inside(pos) and not any(
[c.is_inside(pos) for c in self.children[1:]]
)
# ----------------------------------------------------------------
# Geometry Primitives
# ----------------------------------------------------------------
class Sphere(VolumeRepresentation):
def __init__(self, center: mp.Vector3, radius: float) -> None:
self.center = center
self.radius = radius
def is_inside(self, pos: mp.Vector3) -> bool:
return True if (pos - self.center).norm() - self.radius <= 0 else False
def encode(self) -> dict:
return {
"type": self.__class__.__name__,
"center": [self.center.x, self.center.y, self.center.z],
"radius": self.radius,
}
@staticmethod
def decode(data: dict) -> VolumeRepresentation:
if "center" in data and "radius" in data:
return Sphere(mp.Vector3(*data["center"]), data["radius"])
else:
raise GeometryDecoderError
class Box(VolumeRepresentation):
def __init__(self, axis: mp.Vector3, center: mp.Vector3) -> None:
self.axis = axis
self.center = center
def is_inside(self, pos: mp.Vector3) -> bool:
is_in_x = abs(pos.x - self.center.x) - self.axis.x / 2 < 0
is_in_y = abs(pos.y - self.center.y) - self.axis.y / 2 < 0
is_in_z = abs(pos.z - self.center.z) - self.axis.z / 2 < 0
return is_in_x and is_in_y and is_in_z
def encode(self) -> dict:
return {
"type": self.__class__.__name__,
"axis": [self.axis.x, self.axis.y, self.axis.z],
"center": [self.center.x, self.center.y, self.center.z],
}
@staticmethod
def decode(data: dict) -> VolumeRepresentation:
if "axis" in data and "center" in data:
axis = mp.Vector3(*data["axis"])
if axis.z < 0:
axis.z = mp.inf
center = mp.Vector3(*data["center"])
return Box(axis, center)
else:
raise GeometryDecoderError