Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(modifier): Add from_reflected_specularity methods to opaque classes #844

Merged
merged 2 commits into from Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions honeybee_radiance/dynamic/group.py
Expand Up @@ -125,7 +125,7 @@ def states_by_index(self, state_index):
one state object per dynamic object.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
"""
# make sure that the state_index is valid
Expand Down Expand Up @@ -153,7 +153,7 @@ def to_radiance(self, state_index, direct=False, minimal=False):
to simulate the state, including all geometry and modifiers.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
direct: Boolean to note whether to write the "direct" version of the
state. (Default: False)
Expand Down Expand Up @@ -345,7 +345,7 @@ def states_by_index(self, state_index):
one state object per dynamic object.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
"""
# make sure that the state_index is valid
Expand Down Expand Up @@ -404,7 +404,7 @@ def tmxt_bsdf(self, state_index):
simulation.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
"""
bsdf_mods = []
Expand All @@ -424,7 +424,7 @@ def vmtx_to_radiance(self, state_index, minimal=False):
The resulting string has all geometry geometry and the white_glow modifier.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
minimal: Boolean to note whether the radiance string should be written
in a minimal format (with spaces instead of line breaks). Default: False.
Expand All @@ -451,7 +451,7 @@ def dmtx_to_radiance(self, state_index, minimal=False):
The resulting string has all geometry geometry and the white_glow modifier.

Args:
state_index: An interger from 0 up to the state_count - 1 , which notes
state_index: An integer from 0 up to the state_count - 1 , which notes
the state of the group for which a rad string will be produced.
minimal: Boolean to note whether the radiance string should be written
in a minimal format (with spaces instead of line breaks). Default: False.
Expand Down
62 changes: 62 additions & 0 deletions honeybee_radiance/modifier/material/metal.py
Expand Up @@ -103,3 +103,65 @@ def from_single_reflectance(
g_reflectance=rgb_reflectance, b_reflectance=rgb_reflectance,
specularity=specularity, roughness=roughness,
modifier=modifier, dependencies=dependencies)

@classmethod
def from_reflected_specularity(
cls, identifier, r_reflectance=0.0, g_reflectance=0.0, b_reflectance=0.0,
reflected_specularity=0.9, roughness=0.0, modifier=None, dependencies=None):
"""Create metal 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)
64 changes: 63 additions & 1 deletion honeybee_radiance/modifier/material/plastic.py
Expand Up @@ -22,7 +22,7 @@ class Plastic(Material):
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).
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
Expand Down Expand Up @@ -190,6 +190,68 @@ def from_single_reflectance(
specularity=specularity, roughness=roughness,
modifier=modifier, dependencies=dependencies)

@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)

@classmethod
def from_primitive_dict(cls, primitive_dict):
"""Initialize material from a primitive dict.
Expand Down
18 changes: 15 additions & 3 deletions honeybee_radiance/modifier/material/trans.py
Expand Up @@ -234,6 +234,11 @@ def from_reflected_specularity(
transmitted_spec=0.0, modifier=None, dependencies=None):
"""Create trans material from reflected specularity.

This method assumes that all of the input fractions for reflectance and
transmittance are absolute fractions of the total amount of light hitting
the modifier. This is different than how Radiance natively interprets
the properties.

Note:
https://radiance-online.org//community/workshops/2010-freiburg/PDF/DavidMead.pdf

Expand Down Expand Up @@ -294,9 +299,16 @@ def from_reflected_specularity(
a1 = cr / ((1 - rs) * (1 - a6))

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 is physically impossible to create!\n'
'You need to adjust the inputs for diffuse reflectance values.')
'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, a4, a5, a6, a7, modifier,
dependencies=dependencies)
Expand Down Expand Up @@ -358,7 +370,7 @@ def from_average_properties(
should be between 0 and 1 (Default: 0).
is_specular: Boolean to note if the reflected component is specular (True)
or diffuse (False). (Default: False).
is_diffusing: Boolean to note if the tranmitted component is diffused (True)
is_diffusing: Boolean to note if the transmitted component is diffused (True)
instead of specular like glass (False). (Default: True).
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
Expand Down