def test_assign_values():
    geo = Polygon('test_polygon', vertices)
    geo.identifier = 'new_polygon'
    geo.vertices = tuple(tuple(pt) for pt in alt_vertices)
    assert geo.to_radiance(minimal=True) == \
        'void polygon new_polygon 0 0 12 6.0 -10.0 20.0 6.0 -5.0 20.0 ' \
        '6.0 -5.0 10.0 6.0 -10.0 10.0'
def test_polygon():
    geo = Polygon('test_polygon', vertices)
    assert geo.identifier == 'test_polygon'
    assert geo.vertices == tuple(tuple(pt) for pt in vertices)
    assert geo.to_radiance(
        minimal=True) == \
            'void polygon test_polygon 0 0 12 3.0 -7.0 15.0 3.0 -1.0 15.0 ' \
            '3.0 -1.0 0.0 3.0 -7.0 0.0'
예제 #3
0
 def _add_geo_and_modifier(hb_obj):
     """Add a honeybee object to the geometry and modifier strings."""
     mod_name = '%s_mod' % hb_obj.identifier
     mod_names.append(mod_name)
     white_plastic.identifier = mod_name
     rad_poly = Polygon(hb_obj.identifier, hb_obj.vertices,
                        white_plastic)
     geo_strs.append(rad_poly.to_radiance(False, False, False))
     mod_strs.append(white_plastic.to_radiance(True, False, False))
def test_from_and_to_dict():
    modifier = Plastic('polygon_material')
    polygon = Polygon('default_polygon', vertices, modifier=modifier)
    polygon_dict = polygon.to_dict()

    # check values in dictionary
    assert polygon_dict['identifier'] == 'default_polygon'
    assert polygon_dict['modifier'] == modifier.to_dict()
    assert polygon_dict['vertices'] == tuple(tuple(pt) for pt in vertices)

    polygon_from_dict = Polygon.from_dict(polygon_dict)

    assert polygon_from_dict.to_radiance() == polygon.to_radiance()
def test_from_string():
    geometry_str = metal_polygon
    geo = Polygon.from_string(geometry_str)
    assert geo.identifier == 'polygon_one'
    assert geo.vertices == tuple(tuple(pt) for pt in vertices)
    assert ' '.join(geo.to_radiance(minimal=True).split()) == \
        ' '.join(geometry_str.split())
예제 #6
0
def formQuad(triangleA : Polygon, triangleB : Polygon) -> Polygon:
    """
    Forming a quad out of two complementary triangles
    """
    vertices = [[], [], [], []]
    vertices[0] = triangleA.vertices[0]
    vertices[1] = triangleA.vertices[1]
    vertices[3] = triangleA.vertices[2]

    # First we search for the unique vertex in triangle B
    for i in range(len(triangleB.vertices)):
        isDuplicate = False
        vertexB = triangleB.vertices[i]
        for j in range(len(triangleA.vertices)):
            vertexA = triangleA.vertices[j]
            if listsSame(vertexB, vertexA):
                isDuplicate = True
                break

        # Once we find the vertex unique to the second triangle, we assign it and break out
        if not isDuplicate:
            vertices[2] = vertexB
            break
    
    # Next we check if the vertex ordering needs to be switched
    # This is necessary when two or more elements switch from one vertex to the next
    for i in range(3):
        differentElements = 0
        for j in range(3):
            if vertices[i][j] < vertices[i+1][j] - SIGMA or vertices[i][j] > vertices[i+1][j] + SIGMA:
                differentElements += 1

        # Swap
        if differentElements > 1:
            # Note that this could throw an index error for the first and last vertices, but during my testing this has been a sufficient workaround
            tmp = vertices[i-1]
            vertices[i-1] = vertices[i]
            vertices[i] = tmp
            break

    quad = Polygon(triangleA.identifier, vertices)
    quad.modifier = triangleA.modifier
    return quad
예제 #7
0
def create_view_factor_modifiers(model_file, exclude_sky, exclude_ground,
                                 individual_shades, triangulate, folder, name):
    """Translate a Model into an Octree and corresponding modifier list for view factors.

    \b
    Args:
        model_file: Full path to a Model JSON file (HBJSON) or a Model pkl (HBpkl) file.
    """
    try:
        # create the directory if it's not there
        if not os.path.isdir(folder):
            preparedir(folder)

        # load the model and ensure the properties align with the energy model
        model = Model.from_file(model_file)
        original_units = None
        if model.units != 'Meters':
            original_units = model.units
            model.convert_to_units('Meters')
        for room in model.rooms:
            room.remove_colinear_vertices_envelope(tolerance=0.01,
                                                   delete_degenerate=True)
        if original_units is not None:
            model.convert_to_units(original_units)

        # triangulate the sub-faces if requested
        if triangulate:
            apertures, parents_to_edit = model.triangulated_apertures()
            for tri_aps, edit_infos in zip(apertures, parents_to_edit):
                if len(edit_infos) == 3:
                    for room in model._rooms:
                        if room.identifier == edit_infos[2]:
                            break
                    for face in room._faces:
                        if face.identifier == edit_infos[1]:
                            break
                    for i, ap in enumerate(face._apertures):
                        if ap.identifier == edit_infos[0]:
                            break
                    face._apertures.pop(i)  # remove the aperture to replace
                    face._apertures.extend(tri_aps)
            doors, parents_to_edit = model.triangulated_doors()
            for tri_drs, edit_infos in zip(doors, parents_to_edit):
                if len(edit_infos) == 3:
                    for room in model._rooms:
                        if room.identifier == edit_infos[2]:
                            break
                    for face in room._faces:
                        if face.identifier == edit_infos[1]:
                            break
                    for i, dr in enumerate(face._doors):
                        if dr.identifier == edit_infos[0]:
                            break
                    face._doors.pop(i)  # remove the doors to replace
                    face._doors.extend(tri_drs)

        # set values to be used throughout the modifier assignment
        offset = model.tolerance * -1
        white_plastic = Plastic('white_plastic', 1, 1, 1)
        geo_strs, mod_strs, mod_names = [], [], []

        def _add_geo_and_modifier(hb_obj):
            """Add a honeybee object to the geometry and modifier strings."""
            mod_name = '%s_mod' % hb_obj.identifier
            mod_names.append(mod_name)
            white_plastic.identifier = mod_name
            rad_poly = Polygon(hb_obj.identifier, hb_obj.vertices,
                               white_plastic)
            geo_strs.append(rad_poly.to_radiance(False, False, False))
            mod_strs.append(white_plastic.to_radiance(True, False, False))

        # loop through all geometry in the model and get radiance strings
        for room in model.rooms:
            for face in room.faces:
                if not isinstance(face.type, AirBoundary):
                    if isinstance(face.boundary_condition, Surface):
                        face.move(face.normal * offset)
                    _add_geo_and_modifier(face)
                for ap in face.apertures:
                    _add_geo_and_modifier(ap)
                for dr in face.doors:
                    _add_geo_and_modifier(dr)
        all_shades = model.shades + model._orphaned_faces + \
            model._orphaned_apertures + model._orphaned_doors
        if individual_shades:
            for shade in all_shades:
                _add_geo_and_modifier(shade)
        else:
            white_plastic.identifier = 'shade_plastic_mod'
            mod_names.append(white_plastic.identifier)
            mod_strs.append(white_plastic.to_radiance(True, False, False))
            for shade in all_shades:
                rad_poly = Polygon(shade.identifier, shade.vertices,
                                   white_plastic)
                geo_strs.append(rad_poly.to_radiance(False, False, False))

        # add the ground and sky domes if requested
        if not exclude_sky:
            mod_names.append('sky_glow_mod')
            mod_strs.append('void glow sky_glow_mod 0 0 4 1 1 1 0')
            geo_strs.append('sky_glow_mod source sky_dome 0 0 4 0 0 1 180')
        if not exclude_ground:
            mod_names.append('ground_glow_mod')
            mod_strs.append('void glow ground_glow_mod 0 0 4 1 1 1 0')
            geo_strs.append(
                'ground_glow_mod source ground_dome 0 0 4 0 0 -1 180')

        # write the radiance strings to the output folder
        geo_file = os.path.join(folder, '{}.rad'.format(name))
        mod_file = os.path.join(folder, '{}.mod'.format(name))
        oct_file = os.path.join(folder, '{}.oct'.format(name))
        with open(geo_file, 'w') as gf:
            gf.write('\n\n'.join(mod_strs + geo_strs))
        with open(mod_file, 'w') as mf:
            mf.write('\n'.join(mod_names))

        # use the radiance files to create an octree
        cmd = Oconv(output=oct_file, inputs=[geo_file])
        cmd.options.f = True
        run_command(cmd.to_radiance(), env=folders.env)
    except Exception as e:
        _logger.exception('Model translation failed.\n{}'.format(e))
        sys.exit(1)
    else:
        sys.exit(0)
예제 #8
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