def test01_create(variant_scalar_rgb): from mitsuba.core import xml, ScalarTransform4f s = xml.load_dict({ 'type': 'shapegroup', 'shape_01': { 'type': 'sphere', 'radius': 1.0, 'to_world': ScalarTransform4f.translate([-2, 0, 0]) }, 'shape_02': { 'type': 'sphere', 'radius': 1.0, 'to_world': ScalarTransform4f.translate([2, 0, 0]) } }) b = s.bbox() assert s is not None assert s.primitive_count() == 2 assert s.effective_primitive_count() == 0 assert s.surface_area() == 0 assert ek.allclose(b.center(), [0, 0, 0]) assert ek.allclose(b.min, [-3, -1, -1]) assert ek.allclose(b.max, [3, 1, 1])
def test03_ray_intersect_instance(variants_all_rgb): from mitsuba.core import xml, Ray3f, ScalarVector3f, ScalarTransform4f as T """Check that we can the correct instance pointer when tracing a ray""" scene = xml.load_dict({ 'type': 'scene', 'group_0': { 'type': 'shapegroup', 'shape': { 'type': 'rectangle' } }, 'instance_00': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([-0.5, -0.5, 0.0]) * T.scale(0.5) }, 'instance_01': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([-0.5, 0.5, 0.0]) * T.scale(0.5) }, 'instance_10': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': T.translate([0.5, -0.5, 0.0]) * T.scale(0.5) }, 'shape': { 'type': 'rectangle', 'to_world': T.translate([0.5, 0.5, 0.0]) * T.scale(0.5) } }) ray = Ray3f([-0.5, -0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, -0.5]' in str(pi) assert '[0, 0.5, 0, -0.5]' in str(pi) ray = Ray3f([-0.5, 0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, -0.5]' in str(pi) assert '[0, 0.5, 0, 0.5]' in str(pi) ray = Ray3f([0.5, -0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert '[0.5, 0, 0, 0.5]' in str(pi) assert '[0, 0.5, 0, -0.5]' in str(pi) ray = Ray3f([0.5, 0.5, -12], [0.0, 0.0, 1.0], 0.0, []) pi = scene.ray_intersect_preliminary(ray) assert 'instance = nullptr' in str(pi) or 'instance = [nullptr]' in str(pi)
def test01_create(variant_scalar_rgb): from mitsuba.core import xml, ScalarTransform4f s = xml.load_dict({"type": "sphere"}) assert s is not None assert s.primitive_count() == 1 assert ek.allclose(s.surface_area(), 4 * ek.pi) # Test transforms order in constructor rot = ScalarTransform4f.rotate([1.0, 0.0, 0.0], 35) s1 = xml.load_dict({ "type": "sphere", "radius": 2.0, "center": [1, 0, 0], "to_world": rot }) s2 = xml.load_dict({ "type": "sphere", "to_world": rot * ScalarTransform4f.translate([1, 0, 0]) * ScalarTransform4f.scale(2) }) assert str(s1) == str(s2)
def bsdfs(self, ctx: KernelDictContext) -> KernelDict: from mitsuba.core import ScalarTransform4f returndict = KernelDict({ f"bsdf_{self.id}": { "type": "blendbsdf", "inner_bsdf": onedict_value(self.central_patch.bsdfs(ctx=ctx)), "outer_bsdf": onedict_value(self.background_surface.bsdfs(ctx=ctx)), "weight": { "type": "bitmap", "filename": str( eradiate.path_resolver.resolve( "textures/rami4atm_experiment_surface_mask.bmp")), "filter_type": "nearest", "wrap_mode": "clamp", }, } }) scale = self._compute_scale_parameter(ctx=ctx) trafo = ScalarTransform4f.scale(scale) * ScalarTransform4f.translate(( -0.5 + (0.5 / scale), -0.5 + (0.5 / scale), 0, )) returndict[f"bsdf_{self.id}"]["weight"]["to_uv"] = trafo return returndict
def instances(self, ctx: KernelDictContext) -> t.Dict: """ Return instance plugin specifications. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- dict A dictionary suitable for merge with a :class:`~eradiate.scenes.core.KernelDict` containing instances. """ from mitsuba.core import ScalarTransform4f kernel_length = uck.get("length") return { f"{self.canopy_element.id}_instance_{i}": { "type": "instance", "group": { "type": "ref", "id": self.canopy_element.id }, "to_world": ScalarTransform4f.translate(position.m_as(kernel_length)), } for i, position in enumerate(self.instance_positions) }
def example_scene(shape, scale=1.0, translate=[0, 0, 0], angle=0.0): from mitsuba.core import xml, ScalarTransform4f as T to_world = T.translate(translate) * T.rotate([0, 1, 0], angle) * T.scale(scale) shape2 = shape.copy() shape2['to_world'] = to_world s = xml.load_dict({'type': 'scene', 'shape': shape2}) s_inst = xml.load_dict({ 'type': 'scene', 'group_0': { 'type': 'shapegroup', 'shape': shape }, 'instance': { 'type': 'instance', "group": { "type": "ref", "id": "group_0" }, 'to_world': to_world } }) return s, s_inst
def test_ramiatm_experiment_surface_adjustment(mode_mono): """Create a Rami4ATM experiment and assert the central patch surface is created with the correct parameters, according to the canopy and atmosphere.""" from mitsuba.core import ScalarTransform4f ctx = KernelDictContext() s = Rami4ATMExperiment( atmosphere=HomogeneousAtmosphere(width=ureg.Quantity(42.0, "km")), canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=0, ), surface=CentralPatchSurface(central_patch=LambertianSurface(), background_surface=LambertianSurface()), ) expected_trafo = ScalarTransform4f.scale( 1400) * ScalarTransform4f.translate((-0.499642857, -0.499642857, 0.0)) kernel_dict = s.kernel_dict(ctx=ctx) assert np.allclose(kernel_dict["bsdf_surface"]["weight"]["to_uv"].matrix, expected_trafo.matrix)
def velocity_transform(dr, dq, dt): linvel = dr * dt rotvel_z = ScalarTransform4f.rotate([0, 0, 1], dq[2] * dt) rotvel_y = ScalarTransform4f.rotate([0, 1, 0], dq[1] * dt) rotvel_x = ScalarTransform4f.rotate([1, 0, 0], dq[0] * dt) rotvel = rotvel_x * rotvel_y * rotvel_z return ScalarTransform4f.translate(linvel) * rotvel
def position_transform(r, q): pos = r rot_z = ScalarTransform4f.rotate([0, 0, 1], q[2]) rot_y = ScalarTransform4f.rotate([0, 1, 0], q[1]) rot_x = ScalarTransform4f.rotate([1, 0, 0], q[0]) rot = rot_x * rot_y * rot_z return ScalarTransform4f.translate(pos) * rot
def shapes(self, ctx: KernelDictContext) -> t.Dict: """ Return shape plugin specifications. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- dict A dictionary suitable for merge with a :class:`~eradiate.scenes.core.KernelDict` containing all the shapes in the abstract tree. """ from mitsuba.core import ScalarTransform4f kernel_length = uck.get("length") kernel_height = self.trunk_height.m_as(kernel_length) kernel_radius = self.trunk_radius.m_as(kernel_length) leaf_cloud = self.leaf_cloud.translated( [0.0, 0.0, kernel_height] * kernel_length + self.leaf_cloud_extra_offset.to(kernel_length)) if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx=ctx)[f"bsdf_{self.id}"] shapes_dict = leaf_cloud.shapes(ctx=ctx) shapes_dict[f"trunk_cyl_{self.id}"] = { "type": "cylinder", "bsdf": bsdf, "radius": kernel_radius, "p0": [0, 0, -0.1], "p1": [0, 0, kernel_height], } shapes_dict[f"trunk_cap_{self.id}"] = { "type": "disk", "bsdf": bsdf, "to_world": ScalarTransform4f.scale(kernel_radius) * ScalarTransform4f.translate(((0, 0, kernel_height))), } return shapes_dict
def test_centralpatch_scale_kernel_dict(mode_mono): from mitsuba.core import ScalarTransform4f cs = CentralPatchSurface( width=3000.0 * ureg.km, central_patch=LambertianSurface(width=100 * ureg.km), id="surface", ) ctx = KernelDictContext() kernel_dict = cs.bsdfs(ctx=ctx) assert np.allclose( kernel_dict["bsdf_surface"]["weight"]["to_uv"].matrix, (ScalarTransform4f.scale(10) * ScalarTransform4f.translate( (-0.45, -0.45, 0))).matrix, )
def kernel_item(self) -> t.Dict: """Return kernel item.""" from mitsuba.core import ScalarTransform4f xmin = self.xmin.m_as(uck.get("length")) xmax = self.xmax.m_as(uck.get("length")) ymin = self.ymin.m_as(uck.get("length")) ymax = self.ymax.m_as(uck.get("length")) z = self.z.m_as(uck.get("length")) dx = xmax - xmin dy = ymax - ymin to_world = ScalarTransform4f.translate([ 0.5 * dx + xmin, 0.5 * dy + ymin, z ]) * ScalarTransform4f.scale([0.5 * dx, 0.5 * dy, 1.0]) return {"type": "rectangle", "to_world": to_world}
def map_cube(xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float) -> "mitsuba.core.ScalarTransform4f": r""" Map the cube :math:`[-1, 1]^3` to :math:`[x_\mathrm{min}, x_\mathrm{max}] \times [y_\mathrm{min}, y_\mathrm{max}] \times [z_\mathrm{min}, z_\mathrm{max}]`. Parameters ---------- xmin : float Minimum X value. xmax : float Maximum X value. ymin : float Minimum Y value. ymax : float Maximum Y value. zmin : float Minimum Z value. zmax : float Maximum Z value. Returns ------- :class:`mitsuba.core.ScalarTransform4f` Computed transform matrix. Warnings -------- You must select a Mitsuba variant before calling this function. """ from mitsuba.core import ScalarTransform4f half_dx = (xmax - xmin) * 0.5 half_dy = (ymax - ymin) * 0.5 half_dz = (zmax - zmin) * 0.5 scale_trafo = ScalarTransform4f.scale([half_dx, half_dy, half_dz]) translate_trafo = ScalarTransform4f.translate( [xmin + half_dx, ymin + half_dy, half_dz + zmin]) return translate_trafo * scale_trafo
def sensor_shape_dict(radius, center): from mitsuba.core import ScalarTransform4f d = { "type": "sphere", "radius": radius, "to_world": ScalarTransform4f.translate(center), "sensor": { "type": "irradiancemeter", "film": { "type": "hdrfilm", "width": 1, "height": 1, "rfilter": {"type": "box"} }, } } return d
def shapes(self, ctx: KernelDictContext) -> KernelDict: """ Return shape plugin specifications only. Parameters ---------- ctx : :class:`.KernelDictContext` A context data structure containing parameters relevant for kernel dictionary generation. Returns ------- :class:`.KernelDict` A kernel dictionary containing all the shapes attached to the surface. """ from mitsuba.core import ScalarTransform4f, ScalarVector3f if ctx.ref: bsdf = {"type": "ref", "id": f"bsdf_{self.id}"} else: bsdf = self.bsdfs(ctx)[f"bsdf_{self.id}"] w = self.kernel_width(ctx).m_as(uck.get("length")) z = self.altitude.m_as(uck.get("length")) translate_trafo = ScalarTransform4f.translate( ScalarVector3f(0.0, 0.0, z)) scale_trafo = ScalarTransform4f.scale( ScalarVector3f(w / 2.0, w / 2.0, 1.0)) trafo = translate_trafo * scale_trafo return KernelDict({ f"shape_{self.id}": { "type": "rectangle", "to_world": trafo, "bsdf": bsdf, } })
def add_object(self, scene_dict): """ Add registered meshes to given scene file format """ if len(self.bssrdf) != len(self.mesh): exit("The number of registerd mesh and bssrdf are different!") num_mesh = len(self.bssrdf) num_obj = len(self.bssrdf_obj) for i in range(num_obj): i += 1 scene_dict["obj_" + str(i)] = { "type": "shapegroup" } for i in range(num_mesh): i += 1 bssrdf = self.bssrdf[i] mesh = self.mesh[i] axis = None if(mesh["rotate"]["axis"] == "x"): axis = [1, 0, 0] elif(mesh["rotate"]["axis"] == "y"): axis = [0, 1, 0] elif(mesh["rotate"]["axis"] == "z"): axis = [0, 0, 1] angle = mesh["rotate"]["angle"] if mesh["type"] == "rectangle": shape = { "type": mesh["type"], "to_world": ScalarTransform4f.translate(mesh["translate"]) * ScalarTransform4f.rotate(axis, angle) * ScalarTransform4f.scale(mesh["scale"]), } else: shape = { "type": mesh["type"], "filename": mesh["filename"], "to_world": ScalarTransform4f.translate(mesh["translate"]) * ScalarTransform4f.rotate(axis, angle) * ScalarTransform4f.scale(mesh["scale"]), } bssrdf["trans"] = mesh["translate"] if(mesh["rotate"]["axis"] == "x"): bssrdf["rotate_x"] = angle elif(mesh["rotate"]["axis"] == "y"): bssrdf["rotate_y"] = angle elif(mesh["rotate"]["axis"] == "z"): bssrdf["rotate_z"] = angle bssrdf["height_max"] = mesh["height_max"] bsdf = { "bsdf_" + str(i): bssrdf } shape.update(bsdf) for j in range(num_obj): j += 1 if i in self.bssrdf_obj[j]: scene_dict["obj_" + str(j)][str(i)] = shape for i in range(num_obj): i += 1 scene_dict["instance_" + str(i)] = { "type": "instance", "group":{ "type": "ref", "id": "obj_" + str(i) } } return scene_dict