"""Radiance Plastic Material.
http://radsite.lbl.gov/radiance/refer/ray.html#Plastic
"""
from __future__ import division
from .materialbase import Material
import honeybee.typing as typing
[docs]
class Plastic(Material):
"""Radiance plastic material.
Args:
identifier: Text string for a unique Material ID. Must not contain spaces
or special characters. This will be used to identify the object across
a model and in the exported Radiance files.
r_reflectance: Reflectance for red. The value should be between 0 and 1
(Default: 0).
g_reflectance: Reflectance for green. The value should be between 0 and 1
(Default: 0).
b_reflectance: Reflectance for blue. The value should be between 0 and 1
(Default: 0).
specularity: Fraction of specularity. Specularity fractions greater than 0.1
are not common in non-metallic materials. (Default: 0).
roughness: Roughness is specified as the rms slope of surface facets. A
value of 0 corresponds to a perfectly smooth surface, and a value of 1
would be a very rough surface. Roughness values greater than 0.2 are not
very realistic. (Default: 0).
modifier: Material modifier (Default: None).
dependencies: A list of primitives that this primitive depends on. This
argument is only useful for defining advanced primitives where the
primitive is defined based on other primitives. (Default: None).
Properties:
* identifier
* display_name
* r_reflectance
* g_reflectance
* b_reflectance
* specularity
* roughness
* average_reflectance
* values
* modifier
* dependencies
* is_modifier
* is_material
Usage:
.. code-block:: python
wall_material = Plastic("generic_wall", .55, .65, .75)
print(wall_material)
"""
__slots__ = ('_r_reflectance', '_g_reflectance', '_b_reflectance',
'_specularity', '_roughness')
def __init__(self, identifier, r_reflectance=0.0, g_reflectance=0.0, b_reflectance=0.0,
specularity=0.0, roughness=0.0, modifier=None, dependencies=None):
"""Create plastic material."""
Material.__init__(self, identifier, modifier=modifier, dependencies=dependencies)
self.r_reflectance = r_reflectance
self.g_reflectance = g_reflectance
self.b_reflectance = b_reflectance
self.specularity = specularity
self.roughness = roughness
self._update_values()
def _update_values(self):
"update value dictionaries."
self._values[2] = [
self.r_reflectance, self.g_reflectance, self.b_reflectance,
self.specularity, self.roughness
]
if self.specularity > 0.1:
print("Warning: Specularity values above .1 is uncommon for plastic.")
if self.roughness > 0.2:
print("Warning: Roughness values above .2 is uncommon.")
@property
def r_reflectance(self):
"""Get or set the reflectance for red channel.
The value should be between 0 and 1 (Default: 0).
"""
return self._r_reflectance
@r_reflectance.setter
def r_reflectance(self, reflectance):
self._r_reflectance = \
typing.float_in_range(reflectance, 0, 1, 'red reflectance')
@property
def g_reflectance(self):
"""Get or set the reflectance for green channel.
The value should be between 0 and 1 (Default: 0).
"""
return self._g_reflectance
@g_reflectance.setter
def g_reflectance(self, reflectance):
self._g_reflectance = \
typing.float_in_range(reflectance, 0, 1, 'green reflectance')
@property
def b_reflectance(self):
"""Get or set the reflectance for blue channel.
The value should be between 0 and 1 (Default: 0).
"""
return self._b_reflectance
@b_reflectance.setter
def b_reflectance(self, reflectance):
self._b_reflectance = \
typing.float_in_range(reflectance, 0, 1, 'blue reflectance')
@property
def specularity(self):
"""Get or set the fraction of specularity.
Specularity fractions greater than 0.1 are not common for non-metallic
materials. Specularity fractions smaller than 0.9 are not common for
metallic materials.
"""
return self._specularity
@specularity.setter
def specularity(self, spec_value):
self._specularity = typing.float_in_range(spec_value, 0, 1, 'specularity')
@property
def roughness(self):
"""Get or set the roughness as the rms slope of surface facets.
A value of 0 corresponds to a perfectly smooth surface, and a value of 1
would be a very rough surface. Roughness values greater than 0.2 are not
very realistic. (Default: 0).
"""
return self._roughness
@roughness.setter
def roughness(self, roughness_values):
self._roughness = typing.float_in_range(roughness_values, 0, 1, 'roughness')
@property
def average_reflectance(self):
"""Get the average reflectance of the material."""
return (0.265 * self.r_reflectance + 0.670 * self.g_reflectance +
0.065 * self.b_reflectance) * (1 - self.specularity) + self.specularity
[docs]
@classmethod
def from_single_reflectance(
cls, identifier, rgb_reflectance=0.0, specularity=0.0, roughness=0.0,
modifier=None, dependencies=None):
"""Create Plastic material with single reflectance value.
Args:
identifier: Text string for a unique Material ID. Must not contain spaces
or special characters. This will be used to identify the object across
a model and in the exported Radiance files.
rgb_reflectance: Reflectance for red, green and blue. The value should be
between 0 and 1 (Default: 0).
specularity: Fraction of specularity. Specularity fractions greater than 0.1
are not common in non-metallic materials (Default: 0).
roughness: Roughness is specified as the rms slope of surface facets. A value
of 0 corresponds to a perfectly smooth surface, and a value of 1 would be
a very rough surface. Roughness values greater than 0.2 are not very
realistic. (Default: 0).
modifier: Material modifier (Default: None).
dependencies: A list of primitives that this primitive depends on. This
argument is only useful for defining advanced primitives where the
primitive is defined based on other primitives. (Default: None).
Usage:
.. code-block:: python
wall_material = Plastic.from_single_reflectance("generic_wall", .55)
print(wall_material)
"""
return cls(identifier, r_reflectance=rgb_reflectance,
g_reflectance=rgb_reflectance, b_reflectance=rgb_reflectance,
specularity=specularity, roughness=roughness,
modifier=modifier, dependencies=dependencies)
[docs]
@classmethod
def from_reflected_specularity(
cls, identifier, r_reflectance=0.0, g_reflectance=0.0, b_reflectance=0.0,
reflected_specularity=0.0, roughness=0.0, modifier=None, dependencies=None):
"""Create plastic material from reflected specularity.
This method assumes that all of the input fractions for reflectance are
absolute fractions of the total amount of light hitting the modifier.
This is different than how Radiance natively interprets the properties.
Args:
identifier: Text string for a unique Material ID. Must not contain spaces
or special characters. This will be used to identify the object across
a model and in the exported Radiance files.
r_reflectance: Reflectance for red. The value should be between 0 and 1
(Default: 0).
g_reflectance: Reflectance for green. The value should be between 0 and 1
(Default: 0).
b_reflectance: Reflectance for blue. The value should be between 0 and 1
(Default: 0).
reflected_specularity: Fraction of reflected specular. Specularity
fractions greater than 0.1 are not common in non-metallic
materials. (Default: 0).
roughness: Roughness is specified as the rms slope of surface facets. A
value of 0 corresponds to a perfectly smooth surface, and a value of 1
would be a very rough surface. Roughness values greater than 0.2 are not
very realistic. (Default: 0).
modifier: Material modifier (Default: None).
dependencies: A list of primitives that this primitive depends on. This
argument is only useful for defining advanced primitives where the
primitive is defined based on other primitives. (Default: [])
"""
cr, cg, cb, rs = \
r_reflectance, g_reflectance, b_reflectance, reflected_specularity
rd = (0.265 * cr + 0.670 * cg + 0.065 * cb)
absorb = 1 - rd - rs
if absorb < 0:
summ = rd + rs
msg = 'Sum of Diffuse Reflection (%.3f) and Specular Reflection (%.3f) ' \
'cannot be more than 1 (%.3f).' % (rd, rs, summ)
raise ValueError(msg)
a1 = cr / ((1 - rs))
a2 = cg / ((1 - rs))
a3 = cb / ((1 - rs))
if a3 > 1 or a2 > 1 or a1 > 1:
if a1 > 1:
channel, val = 'Red', a1
elif a2 > 1:
channel, val = 'Green', a2
else:
channel, val = 'Blue', a3
raise ValueError(
'This material has a physically impossible reflectance value for '
'the {} channel\nwhen specular and diffuse fractions are added '
'({}).'.format(channel, val))
return cls(identifier, a1, a2, a3, rs, roughness,
modifier=modifier, dependencies=dependencies)
[docs]
@classmethod
def from_primitive_dict(cls, primitive_dict):
"""Initialize material from a primitive dict.
Args:
data: A dictionary in the format below.
.. code-block:: python
{
"modifier": {}, # primitive modifier (Default: None)
"type": "", # primitive type
"identifier": "", # primitive identifier
"display_name": "", # primitive display name
"values": [], # values
"dependencies": []
}
"""
cls._dict_type_check(cls.__name__, primitive_dict)
modifier, dependencies = cls.filter_dict_input(primitive_dict)
values = primitive_dict['values'][2]
cls_ = cls(
identifier=primitive_dict["identifier"],
r_reflectance=values[0],
g_reflectance=values[1],
b_reflectance=values[2],
specularity=values[3],
roughness=values[4],
modifier=modifier,
dependencies=dependencies
)
if 'display_name' in primitive_dict \
and primitive_dict['display_name'] is not None:
cls_.display_name = primitive_dict['display_name']
# this might look redundant but it is NOT. see glass for explanation.
cls_.values = primitive_dict['values']
return cls_
[docs]
@classmethod
def from_dict(cls, data):
"""Initialize material from a dictionary.
Args:
data: A dictionary in the format below.
.. code-block:: python
{
"type": "", # Material type
"identifier": "", # Material identifier
"display_name": "", # Material display name
"r_reflectance": float, # Reflectance for red
"g_reflectance": float, # Reflectance for green
"b_reflectance": float, # Reflectance for blue
"specularity": float, # Material specularity
"roughness": float, # Material roughness
"modifier": {}, # Material modifier (Default: None)
"dependencies": []
}
"""
cls._dict_type_check(cls.__name__, data)
modifier, dependencies = cls.filter_dict_input(data)
new_obj = cls(identifier=data["identifier"],
r_reflectance=data["r_reflectance"],
g_reflectance=data["g_reflectance"],
b_reflectance=data["b_reflectance"],
specularity=data["specularity"],
roughness=data["roughness"],
modifier=modifier,
dependencies=dependencies)
if 'display_name' in data and data['display_name'] is not None:
new_obj.display_name = data['display_name']
return new_obj
[docs]
def to_dict(self):
"""Translate this object to a dictionary."""
base = {
'modifier': self.modifier.to_dict(),
'type': self.__class__.__name__,
'identifier': self.identifier,
'r_reflectance': self.r_reflectance,
'g_reflectance': self.g_reflectance,
'b_reflectance': self.b_reflectance,
'specularity': self.specularity,
'roughness': self.roughness,
'dependencies': [dp.to_dict() for dp in self.dependencies]
}
if self._display_name is not None:
base['display_name'] = self.display_name
return base
def __copy__(self):
mod, depend = self._dup_mod_and_depend()
new_obj = self.__class__(
self.identifier, self.r_reflectance, self.g_reflectance, self.b_reflectance,
self.specularity, self.roughness, mod, depend)
new_obj._display_name = self._display_name
return new_obj