def test_dict_to_object_view(): """Test the dict_to_object method with View objects.""" v_obj = View('test_view', (0, 0, 10), (0, 1, 0), (0, 0, 1), 'l', 240, 300, -10, -25) v_dict = v_obj.to_dict() new_v = dict_to_object(v_dict) assert isinstance(new_v, View)
def test_to_from_dict(): view = View('test_view', (0, 0, 10), (0, 1, 0), (0, 0, 1), 'l', 240, 300, -10, -25) view.room_identifier = 'Test_room' view_dict = view.to_dict() new_view = View.from_dict(view_dict) assert new_view == view assert view_dict == new_view.to_dict()
def test_rotate(): v = View('test_view', (0, 0, 0), (1, 0, 0), (0, 0, 1)) v.rotate(90, pv.Vector3D(0, 1, 1), pv.Point3D(0, 0, 20)) assert round(v.position[0], 3) == -14.142 assert round(v.position[1]) == -10 assert round(v.position[2]) == 10 assert round(v.up_vector[0], 3) == 0.707 assert round(v.up_vector[1], 1) == 0.5 assert round(v.up_vector[2], 1) == 0.5 assert round(v.direction[0], 1) == 0.0 assert round(v.direction[1], 3) == 0.707 assert round(v.direction[2], 3) == -0.707
def test_scale(): v = View('test_view', (1, 0, 2), (2, 0, 0), (0, 0, 3)) v.scale(2) assert round(v.position[0]) == 2 assert round(v.position[1]) == 0 assert round(v.position[2]) == 4 assert round(v.up_vector[0]) == 0 assert round(v.up_vector[1]) == 0 assert round(v.up_vector[2]) == 6 assert round(v.direction[0]) == 4 assert round(v.direction[1]) == 0 assert round(v.direction[2]) == 0
def test_rotate_xy(): v = View('test_view', (1, 0, 2), (2, 0, 0), (0, 0, 3)) v.rotate_xy(90, pv.Point3D(0, 0, 0)) assert round(v.position[0]) == 0 assert round(v.position[1]) == 1 assert round(v.position[2]) == 2 assert round(v.up_vector[0]) == 0 assert round(v.up_vector[1]) == 0 assert round(v.up_vector[2]) == 3 assert round(v.direction[0]) == 0 assert round(v.direction[1]) == 2 assert round(v.direction[2]) == 0
def test_reflect(): v = View('test_view', (1, 0, 2), (2, 0, 0), (0, 0, 3)) v.reflect(Plane(pv.Vector3D(1, 0, 0), pv.Point3D(0, 0, 0))) assert round(v.position[0]) == -1 assert round(v.position[1]) == 0 assert round(v.position[2]) == 2 assert round(v.up_vector[0]) == 0 assert round(v.up_vector[1]) == 0 assert round(v.up_vector[2]) == 3 assert round(v.direction[0]) == -2 assert round(v.direction[1]) == 0 assert round(v.direction[2]) == 0
def test_value_assignment(): v = View('test_view', (0, 0, 10), (0, 1, 0), (0, 0, 1), 'l', 240, 300, -10, -25) assert v.position == (0, 0, 10) assert v.direction == (0, 1, 0) assert v.up_vector == (0, 0, 1) assert v.type == 'l' assert v.h_size == 240 assert v.v_size == 300 assert v.shift == -10 assert v.lift == -25 assert v.vo == '' assert v.to_radiance() == '-vtl -vp 0.0 0.0 10.0 -vd 0.0 1.0 0.0' \ ' -vu 0.0 0.0 1.0 -vh 240.0 -vv 300.0 -vs -10.0 -vl -25.0'
def test_default_values(): v = View('test_view') str(v) # test string representation hash(v) # test hashability assert v.position == (0, 0, 0) assert v.direction == (0, 0, 1) assert v.up_vector == (0, 1, 0) assert v.type == 'v' assert v.h_size == 60 assert v.v_size == 60 assert v.vo == '' assert v.va == '' assert v.to_radiance() == '-vtv -vp 0.0 0.0 0.0 -vd 0.0 0.0 1.0' \ ' -vu 0.0 1.0 0.0 -vh 60.0 -vv 60.0'
def test_dimensions(): view_string = 'rvu -vtv -vp 1.000 8.000 2.000 -vd 3.000 -2.000 0.000' \ ' -vu 0.000 0.000 1.000 -vh 120.000 -vv 45.000' view = View.from_string('test_view', view_string) x, y = view.dimension_x_y(512, 512) assert x == 512 assert y == 122 assert view.dimension(512, 512) == '-x 512 -y 122 -ld-'
def test_from_grid_unf(): v = View('test_vta') v.position = (3, 7, 1.8) v.direction = (0, 1, 0) v.up_vector = (0, 0, 1) v.type = 'a' v.h_size = 360 v.v_size = 360 grid = [ './tests/assets/view/view_0000.unf', './tests/assets/view/view_0001.unf', './tests/assets/view/view_0002.unf', './tests/assets/view/view_0003.unf', './tests/assets/view/view_0004.unf' ] from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance()
def test_from_file(): vw = View.from_file('./tests/assets/view.vf') assert vw.identifier == 'view' assert list(vw.position) == [0, 0, 0] assert list(vw.direction) == [0, 0, 1] assert list(vw.up_vector) == [0, 1, 0] assert vw.h_size == 29.341 assert vw.v_size == 32.204 assert vw.shift == -0.500 assert vw.lift == -0.500 assert vw.fore_clip == 100 assert vw.aft_clip == 200
def test_from_string(): view = 'rvu -vtv -vp 0.000 0.000 0.000 -vd 0.000 0.000 1.000 ' \ '-vu 0.000 1.000 0.000 -vh 29.341 -vv 32.204 ' \ '-vs -0.500 -vl -0.500 -vo 100.000 -va 200.000' vw = View.from_string('test_view', view) assert list(vw.position) == [0, 0, 0] assert list(vw.direction) == [0, 0, 1] assert list(vw.up_vector) == [0, 1, 0] assert vw.h_size == 29.341 assert vw.v_size == 32.204 assert vw.shift == -0.500 assert vw.lift == -0.500 assert vw.fore_clip == 100 assert vw.aft_clip == 200
def from_view_file(cls: Camera, file_path: str) -> Camera: """Create a Camera object from a radiance view file. Args: file_path: A valid path to a radiance view file with .vf extension. Returns: A Camera object. """ view_file = Path(file_path) if view_file.is_file() and view_file.as_posix()[-3:] == '.vf': return Camera.from_view(view=View.from_file(view_file.as_posix())) else: raise FileNotFoundError('Radiance view file not found.')
def view_perspective(directory): vw = View('test_view_perspective', (0, 0, 10), (0, 1, 0), (0, 0, 1)) dest_file = os.path.join(directory, 'view_perspective.json') with open(dest_file, 'w') as fp: json.dump(vw.to_dict(), fp, indent=4)
ghenv.Component.AdditionalHelpFromDocStrings = '4' try: # import the core honeybee dependencies from honeybee.typing import clean_and_id_rad_string except ImportError as e: raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) try: from honeybee_radiance.view import View except ImportError as e: raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) try: # import ladybug_rhino dependencies from ladybug_rhino.grasshopper import all_required_inputs from ladybug_rhino.viewport import viewport_by_name, viewport_properties except ImportError as e: raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) VIEW_TYPES = ('v', 'h', 'l', 'c', 'a') # set the default values _name_ = 'RadianceView' if _name_ is None else _name_ viewp = viewport_by_name(_viewport_) v_props = viewport_properties(viewp, _view_type_) view = View(clean_and_id_rad_string(_name_), v_props['position'], v_props['direction'], v_props['up_vector'], VIEW_TYPES[v_props['view_type']], v_props['h_angle'], v_props['v_angle']) view.display_name = _name_
def test_from_grid_vth(): v = View('test_vth') v.position = (0, 0, 0) v.direction = (0, 1, 0) v.up_vector = (0, 0, 1) v.type = 'h' v.h_size = 180 v.v_size = 180 grid = v.grid(x_div_count=2, y_div_count=5) from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance()
def test_move(): v = View('test_view') v.move(pv.Vector3D(10, 20, 30)) assert v.position == (10, 20, 30) assert v.up_vector == (0, 1, 0)
def main(): argc = len(sys.argv) if argc < 2: print("Error: .rad file not specified, usage: python3 genParallelViews.py <file.rad>") return -1 filePath = sys.argv[1] if not filePath.endswith(".rad"): print("Error: .rad file not specified, usage: python3 genParallelViews.py <file.rad>") return -1 print("Scene up direction: [{0}, {1}, {2}]".format(SCENE_UP[0], SCENE_UP[1], SCENE_UP[2])) # Read in the RAD file stringObjects = reader.parse_from_file(filePath) polygons = [] materials = [] currentModifier = None for stringObject in stringObjects: if not "polygon" in stringObject: validMaterial = False for material in VALID_MATERIALS: if material in stringObject: validMaterial = True break # This is a bit hacky right now. We get an exception if we try and parse a non-material or non-polygon if not validMaterial: print("Error: Can't parse '{0}' from RAD file. If this is a material try manually adding it to the script, else ignore.".format(stringObject)) continue primitiveDict = reader.string_to_dict(stringObject) if primitiveDict["type"] == "polygon": primitiveDict["modifier"] = None polygon = Polygon.from_primitive_dict(primitiveDict) polygon.modifier = currentModifier polygons.append(polygon) elif primitiveDict["type"] == "plastic": plastic = Plastic.from_primitive_dict(primitiveDict) currentModifier = plastic materials.append(plastic) elif primitiveDict["type"] == "metal": metal = Metal.from_primitive_dict(primitiveDict) currentModifier = metal materials.append(metal) elif primitiveDict["type"] == "glass": glass = Glass.from_primitive_dict(primitiveDict) currentModifier = glass materials.append(glass) else: print("Error: Unable to assign material from '{0}'.".format(stringObject)) # Loop through all the polygons read in from the RAD file and classify them as triangles or quads triangles = [] quads = [] for polygon in polygons: if len(polygon.vertices) == 3: triangles.append(polygon) elif len(polygon.vertices) == 4: quads.append(polygon) # Loop through all the triangles read in from the RAD file and attempt to form quads from them trianglesMissed = [] i = 0 while True: if i >= len(triangles) - 1: break triangleA = triangles[i] triangleB = triangles[i+1] if formsQuad(triangleA, triangleB): quad = formQuad(triangleA, triangleB) quads.append(quad) i += 2 else: trianglesMissed.append(triangleA) i += 1 if len(trianglesMissed) != 0: print("The following triangles from the RAD file couldn't be formed into quads: ", end="") for triangle in trianglesMissed: print("{0}".format(triangle.identifier), end=" ") print() # Loop through all the quads and generate a Radiance parallel projection view for it viewDict = {} for quad in quads: # type 'l' defines this view as a parallel projection view = View(quad.identifier, type='l') # Get the dimensions of the quad. # One of these should be approximately 0.0 because a quad is two dimensional dimensions = [0, 0, 0] for i in range(3): dimensions[i] = getDimensionLength(quad, i) # Set the view's horizontal and vertical size based on the dimensions of the quad # The projection will contain the entire quad horizontalSet = False verticalSet = False for i in range(3): if dimensions[i] > SIGMA: if not horizontalSet: view.h_size = dimensions[i] horizontalSet = True else: view.v_size = dimensions[i] verticalSet = True break if not horizontalSet or not verticalSet: print("Error: " + view.identifier + " vh and/or vv not set") continue # Set view direction normal = getQuadNormal(quad) if len(normal) == 0: print("Error: " + view.identifier + " vn not set") continue direction = (-normal[0], -normal[1], -normal[2]) view.direction = direction # Set view position position = getViewPosition(quad, dimensions, normal) view.position = position # Set view up view.up_vector = SCENE_UP if listsSame(SCENE_UP, direction) or listsSame(SCENE_UP, normal): if not listsSame(SCENE_UP, [0.0, 0.0, 1.0]): view.up_vector = [0.0, 0.0, 1.0] elif not listsSame(SCENE_UP, [0.0, 1.0, 0.0]): view.up_vector = [0.0, 1.0, 0.0] else: view.up_vector = [1.0, 0.0, 0.0] viewDict[quad.identifier] = view print("\n-----Radiance Parallel Views-----") for view in viewDict.values(): print("view=" + view.identifier + " " + view.to_radiance()) print("----------\n\nTotal view count: {0}, Total quad count: {1}".format(len(viewDict.values()), len(quads))) writeOBJFile(BASE_FILE_NAME, quads, viewDict) writeMTLFile(BASE_FILE_NAME, quads) return 0
try: from honeybee_radiance.view import View except ImportError as e: raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) try: # import ladybug_rhino dependencies from ladybug_rhino.grasshopper import all_required_inputs except ImportError as e: raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) VIEW_TYPES = ('v', 'h', 'l', 'c', 'a') if all_required_inputs(ghenv.Component): # process the points/vectors into tuples _pos = (_position.X, _position.Y, _position.Z) _dir = (_direction.X, _direction.Y, _direction.Z) # set the default values name = clean_and_id_rad_string('View') if _name_ is None else _name_ _up_vec = (_up_vector_.X, _up_vector_.Y, _up_vector_.Z) if _up_vector_ \ is not None else (0, 0, 1) _type_ = 'v' if _view_type_ is None else VIEW_TYPES[_view_type_] _h_angle_ = 60 if _h_angle_ is None else _h_angle_ _v_angle_ = 60 if _v_angle_ is None else _v_angle_ view = View(clean_rad_string(name), _pos, _dir, _up_vec, _type_, _h_angle_, _v_angle_) if _name_ is not None: view.display_name = _name_
def test_value_update(): v = View('__') v.position = (0, 0, 10) v.direction = (0, 1, 0) v.up_vector = (0, 0, 1) v.type = 'l' v.h_size = 240 v.v_size = 300 v.shift = -10 v.lift = -25 v.fore_clip = 30 v.aft_clip = 50 assert v.position == (0, 0, 10) assert v.direction == (0, 1, 0) assert v.up_vector == (0, 0, 1) assert v.type == 'l' assert v.h_size == 240 assert v.v_size == 300 assert v.shift == -10 assert v.lift == -25 assert v.fore_clip == 30 assert v.aft_clip == 50 assert v.to_radiance() == '-vtl -vp 0.0 0.0 10.0 -vd 0.0 1.0 0.0' \ ' -vu 0.0 0.0 1.0 -vh 240.0 -vv 300.0 -vs -10.0 -vl -25.0 ' \ '-vo 30.0 -va 50.0'
def merge_view(input_folder, base_name, extension, scale_factor, folder, name): """Merge several radiance HDR image files into a single file. This command will also perform an anti-aliasing operation on the output and replace the view information in the header of the merged file if a single .vf file is found within the root of the input-folder. \b Args: input_folder: Input folder. base_name: File base name. All of the files must start with base name and continue with _ and an integer values. extension: File extention. [Default: .unf] """ try: # get all of the files in the folder with the given extension pattern = r'{}_\d+{}'.format(base_name, extension) images = sorted(f for f in os.listdir(input_folder) if re.match(pattern, f)) if len(images) == 0: raise ValueError('Found no files to merge.') name = name or base_name # get the new dir name as view name might be group/name dirname = os.path.dirname(os.path.normpath(os.path.join(folder, name))) if dirname and not os.path.exists(dirname): os.makedirs(dirname) temp_output = os.path.join(dirname, name + '_temp.HDR') output_file = os.path.join(dirname, name + '.HDR') # set up the pcompos command in_dirname = os.path.normpath(input_folder) pcompos = Pcompos(output=temp_output) pcompos.input = [os.path.join(in_dirname, img) for img in images] pcompos.options.a = 1 # setup the pfilt command to perform anti-aliasing on the output pfilt = Pfilt(input=temp_output) pfilt.options.r = 0.6 if scale_factor != 1: pfilt.options.x = '/{}'.format(scale_factor) pfilt.options.y = '/{}'.format(scale_factor) # search for a single .vf in the folder and, if it's found, grab the info views = sorted(f for f in os.listdir(input_folder) if f.endswith('.vf')) if len(views) == 1: # replace the header with the info in the view view_obj = View.from_file(os.path.join(input_folder, views[0])) getinfo = Getinfo(output=output_file) getinfo.options.a = 'VIEW= {}'.format(view_obj) pfilt.pipe_to = getinfo else: # just let the output of pfilt be the final output pfilt.output = output_file # run the commands in series env = None if folders.env != {}: env = folders.env env = dict(os.environ, **env) if env else None for r_cmd in (pcompos, pfilt): r_cmd.run(env) except Exception: _logger.exception('Failed to merge image files.') sys.exit(1) else: sys.exit(0)
def test_grid_dimension(): v = View('test_vtv') v.position = (0, 0, 0) v.direction = (0, 1, 0) v.up_vector = (0, 0, 1) v.type = 'v' v.h_size = 60 v.v_size = 60 grid = v.grid(x_div_count=1, y_div_count=1) from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance() grid = v.grid(x_div_count=2, y_div_count=4) from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance() grid = v.grid(x_div_count=1, y_div_count=10) from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance() grid = v.grid(x_div_count=10, y_div_count=100) from_grid = View.from_grid(grid) assert v.to_radiance() == from_grid.to_radiance()
def view_parallel(directory): vw = View('test_view', (0, 0, 10), (0, 1, 0), (0, 0, 1), 'l', 240, 300, -10, -25) dest_file = os.path.join(directory, 'view_parallel.json') with open(dest_file, 'w') as fp: json.dump(vw.to_dict(), fp, indent=4)
def split_view(view, count, skip_overture, octree, rad_params, folder, log_file): """Split a radiance view file into smaller views based on count. \b Args: view: Full path to input sensor view file. count: Maximum number of sensors in new files. The number will be rounded to closest round number for each file. For example if the input file has 21 sensors and input count is set to 5 this command will generate 4 files where the first three files will have 5 sensors and the last file will have 6. """ try: # split the view into smaller views view_obj = View.from_file(view) views = view_obj.grid(y_div_count=count) views_info = [] for c, v in enumerate(views): name = '%s_%04d' % (view_obj.identifier, c) path = '%s.vf' % name full_path = os.path.join(folder, path) v.to_file(folder, path, mkdir=True) views_info.append({ 'name': name, 'path': path, 'full_path': full_path }) # create the ambient cache file if specified amb_file = os.path.basename(view).replace('.vf', '.amb') if not skip_overture: options = RpictOptions() if rad_params: options.update_from_string(rad_params.strip()) # overwrite default image size to be small for the ambient cache (64 x 64) options.x = 64 options.y = 64 options.af = amb_file # create command and run it to get the .amb file assert octree is not None, \ 'Octree must be specified for an overture calculation.' out_file = os.path.basename(view).replace('.vf', '.unf') rpict = Rpict(options=options, output=out_file, octree=octree, view=view) env = None if folders.env != {}: env = folders.env env = dict(os.environ, **env) if env else None rpict.run(env=env, cwd=folder) os.remove(os.path.join(folder, out_file)) else: # write a dummy ambient file so that queenbee does not crash write_to_file_by_name(folder, amb_file, '') # record all of the view files that were generated log_file.write(json.dumps(views_info)) except Exception: _logger.exception('Failed to split view file.') sys.exit(1) else: sys.exit(0)