コード例 #1
0
def _pyramidal_top(ac_object: ac.Object, x_centre: float, y_centre: float, top: float,
                   roof_texture, roof_mat_idx: int, pyramid_height: float,
                   ring: List[List[float]], object_node_index: int) -> None:
    """Adds the pyramidal top by adding the centre node and the necessary faces.

    ring is a list of x,y coordinates for the current top points of the roof.
    """
    # add node for the middle of the roof
    ac_object.node(-1 * y_centre, top, -1 * x_centre)

    roof_texture_size = roof_texture.h_size_meters

    # loop on sides of the building
    n_pts = len(ring)  # number of points
    for i in range(0, n_pts):
        dist_inwards = coord.calc_distance_local(ring[i][0], ring[i][1], x_centre, y_centre)
        j = (i+1) % n_pts
        dist_edge = coord.calc_distance_local(ring[i][0], ring[i][1], ring[j][0], ring[j][1])
        len_roof_hypo = (dist_inwards ** 2 + pyramid_height ** 2) ** 0.5
        repeat_x = dist_edge/roof_texture_size
        repeat_y = len_roof_hypo/roof_texture_size
        ac_object.face([(object_node_index + i, roof_texture.x(0), roof_texture.y(0)),
                        (object_node_index + j, roof_texture.x(repeat_x), roof_texture.y(0)),
                        (object_node_index + n_pts, roof_texture.x(0.5*repeat_x), roof_texture.y(repeat_y))],
                       mat_idx=roof_mat_idx)
コード例 #2
0
ファイル: linear.py プロジェクト: Joako360/Osm2city
 def _write_quads(self,
                  obj: ac3d.Object,
                  left_nodes_list,
                  right_nodes_list,
                  tex_y0,
                  tex_y1,
                  check_lit: bool = False) -> None:
     """Write a series of quads bound by left and right.
     Left/right are lists of node indices which will be used to form a series of quads.
     Material index tells whether it is lit or not.
     """
     n_nodes = len(left_nodes_list)
     assert (len(left_nodes_list) == len(right_nodes_list))
     for i in range(n_nodes - 1):
         mat_idx = mat.Material.unlit
         if check_lit:
             if self.lighting[i] or self.lighting[i + 1]:
                 mat_idx = mat.Material.lit
         xl = self.dist[i] / road.LENGTH
         xr = self.dist[i + 1] / road.LENGTH
         face = [(left_nodes_list[i], xl, tex_y0),
                 (left_nodes_list[i + 1], xr, tex_y0),
                 (right_nodes_list[i + 1], xr, tex_y1),
                 (right_nodes_list[i], xl, tex_y1)]
         obj.face(face[::-1], mat_idx=mat_idx.value)
コード例 #3
0
def separate_skillion(ac_object: ac.Object, b, roof_mat_idx: int):
    """skillion roof, n nodes, separate model. Inward_"""
    # - handle square skillion roof
    #   it's assumed that the first 2 nodes are at building:height-roof:height
    #                     the last  2 nodes are at building:height
    # -- 4 corners
    object_node_index = ac_object.next_node_index()
    for x in b.pts_all:
        ac_object.node(-x[1], b.beginning_of_roof_above_sea_level, -x[0])

    # We don't want the hipped part to be greater than the height, which is 45 deg

    # FLAT PART
    i = 0
    for pt in b.pts_all:
        ac_object.node(-pt[1], b.beginning_of_roof_above_sea_level + b.roof_height_pts[i], -pt[0])
        i += 1

    if b.polygon.interiors:
        print(" len(b.polygon.interiors)")
        outer_closest = copy.copy(b.outer_nodes_closest)
        print(("outer_closest = copy.copy(b.outer_nodes_closest)", outer_closest))
        i = b.pts_outer_count
        inner_ring = 0
        nodes = []
        for object_node_index in range(b.pts_outer_count):
            nodes.append(object_node_index)
            if outer_closest and object_node_index == outer_closest[0]:
                len_ring = len(b.polygon.interiors[inner_ring].coords) - 1
                a = np.arange(len_ring) + i
                for x in a:
                    nodes.append(x)
                nodes.append(a[0])  # -- close inner ring
                i += len_ring
                inner_ring += 1
                outer_closest.pop(0)
                nodes.append(object_node_index)  # -- go back to outer ring
    else:
        nodes = list(range(b.pts_outer_count))

    uv = face_uv(nodes, b.pts_all, b.roof_texture, angle=None)

    assert(len(nodes) == b.pts_all_count + 2 * len(b.polygon.interiors))

    nodes_uv_list = []
    object_node_index = ac_object.next_node_index()

    # create nodes for/ and roof
    for i, node in enumerate(nodes):
        # new nodes
        ac_object.node(-b.pts_all[node][1], b.beginning_of_roof_above_sea_level + b.roof_height_pts[node],
                       -b.pts_all[node][0])
        nodes_uv_list.append((object_node_index + node, uv[i][0], uv[i][1]))
    ac_object.face(nodes_uv_list, mat_idx=roof_mat_idx)
    return
コード例 #4
0
def flat(ac_object: ac.Object, index_first_node_in_ac_obj: int, b, roof_mgr: RoofManager, roof_mat_idx: int,
         stats: Stats) -> None:
    """Flat roof. Also works for relations."""
    #   3-----------------2  Outer is CCW: 0 1 2 3
    #   |                /|  Inner[0] is CW: 4 5 6 7
    #   |         11----8 |  Inner[1] is CW: 8 9 10 11
    #   | 5----6  |     | |  Inner rings are rolled such that their first nodes
    #   | |    |  10-<<-9 |  are closest to an outer node. (see b.roll_inner_nodes)
    #   | 4-<<-7          |
    #   |/                |  draw 0 - 4 5 6 7 4 - 0 1 2 - 8 9 10 11 8 - 2 3
    #   0---->>-----------1
    if b.roof_texture is None:
        raise ValueError("Roof texture None")

    if "compat:roof-flat" not in b.roof_requires:
        # flat roof might have gotten required later in process, so we must find a new roof texture
        logging.debug("Replacing texture for flat roof despite " + str(b.roof_requires))
        if "compat:roof-pitched" in b.roof_requires:
            b.roof_requires.remove("compat:roof-pitched")
        b.roof_requires.append("compat:roof-flat")
        b.roof_texture = roof_mgr.find_matching_roof(b.roof_requires, b.longest_edge_length, stats)

    if b.polygon.interiors:
        outer_closest = copy.copy(b.outer_nodes_closest)
        i = b.pts_outer_count
        inner_ring = 0
        nodes = []
        for o in range(b.pts_outer_count):
            nodes.append(o)
            if outer_closest and o == outer_closest[0]:
                len_ring = len(b.polygon.interiors[inner_ring].coords) - 1
                a = np.arange(len_ring) + i
                for x in a:
                    nodes.append(x)
                nodes.append(a[0])  # -- close inner ring
                i += len_ring
                inner_ring += 1
                outer_closest.pop(0)
                nodes.append(o)  # -- go back to outer ring
    else:
        nodes = list(range(b.pts_outer_count))

    uv = _face_uv_flat_roof(nodes, b.pts_all, b.roof_texture)
    nodes = np.array(nodes) + b.pts_all_count

    assert(len(nodes) == b.pts_all_count + 2 * len(b.polygon.interiors))

    nodes_uv_list = []
    for i, node in enumerate(nodes):
        nodes_uv_list.append((node + index_first_node_in_ac_obj, uv[i][0], uv[i][1]))
    ac_object.face(nodes_uv_list, mat_idx=roof_mat_idx)
コード例 #5
0
 def _write_pier_area(self, obj: ac3d.Object, offset: co.Vec2d) -> None:
     """Writes a Pier mapped as an area"""
     if len(self.nodes) < 3:
         logging.debug(
             'ERROR: platform with osm_id=%d cannot created due to less then 3 nodes',
             self.osm_id)
         return
     linear_ring = shg.LinearRing(self.nodes)
     # TODO shg.LinearRing().is_ccw
     o = obj.next_node_index()
     if linear_ring.is_ccw:
         logging.info('CounterClockWise')
     else:
         # normalize to CCW
         logging.info("Clockwise")
         self.nodes = self.nodes[::-1]
     # top ring
     e = self.elevation + 1
     for p in self.nodes:
         obj.node(-p[1] + offset.y, e, -p[0] + offset.x)
     top_nodes = np.arange(len(self.nodes))
     self.segment_len = np.array([0] + [
         co.Vec2d(coord).distance_to(co.Vec2d(linear_ring.coords[i]))
         for i, coord in enumerate(linear_ring.coords[1:])
     ])
     rd_len = len(linear_ring.coords)
     self.dist = np.zeros((rd_len))
     for i in range(1, rd_len):
         self.dist[i] = self.dist[i - 1] + self.segment_len[i]
     face = []
     x = 0.
     # reversed(list(enumerate(a)))
     # Top Face
     for i, n in enumerate(top_nodes):
         face.append((n + o, x, 0.5))
     obj.face(face)
     # Build bottom ring
     e = self.elevation - 5
     for p in self.nodes:
         obj.node(-p[1] + offset.y, e, -p[0] + offset.x)
     # Build Sides
     for i, n in enumerate(top_nodes[1:]):
         sideface = list()
         sideface.append((n + o + rd_len - 1, x, 0.5))
         sideface.append((n + o + rd_len, x, 0.5))
         sideface.append((n + o, x, 0.5))
         sideface.append((n + o - 1, x, 0.5))
         obj.face(sideface)
コード例 #6
0
ファイル: linear.py プロジェクト: Joako360/Osm2city
    def _write_nodes(self,
                     obj: ac3d.Object,
                     line_string: shg.LineString,
                     z,
                     cluster_elev: float,
                     offset: Optional[co.Vec2d] = None,
                     join: bool = False,
                     is_left: bool = False) -> List[int]:
        """given a LineString and z, write nodes to .ac.
           Return nodes_list
        """
        to_write = copy.copy(line_string.coords)
        nodes_list = []
        assert (self.cluster_ref is not None)
        if not join:
            nodes_list += list(obj.next_node_index() +
                               np.arange(len(to_write)))
        else:
            if len(self.junction0) == 2:
                try:
                    # if other node already exists, do not write a new one
                    other_node = self.junction0.get_other_node(
                        self, is_left,
                        self.cluster_ref)  # other nodes already written:
                    to_write = to_write[1:]
                    z = z[1:]
                    nodes_list.append(other_node)
                except KeyError:
                    self.junction0.set_other_node(self, is_left,
                                                  obj.next_node_index(),
                                                  self.cluster_ref)

            # -- make list with all but last node -- we might add last node later
            nodes_list += list(obj.next_node_index() +
                               np.arange(len(to_write) - 1))
            last_node = obj.next_node_index() + len(to_write) - 1

            if len(self.junction1) == 2:
                try:
                    # if other node already exists, do not write a new one
                    other_node = self.junction1.get_other_node(
                        self, is_left,
                        self.cluster_ref)  # other nodes already written:
                    to_write = to_write[:-1]
                    z = z[:-1]
                    nodes_list.append(other_node)
                except KeyError:
                    self.junction1.set_other_node(self, is_left, last_node,
                                                  self.cluster_ref)
                    nodes_list.append(last_node)
            else:
                nodes_list.append(last_node)

        for i, the_node in enumerate(to_write):
            e = z[i] - cluster_elev
            obj.node(-(the_node[1] - offset.y), e, -(the_node[0] - offset.x))

        return nodes_list
コード例 #7
0
    def _write_area(self, fg_elev: utilities.FGElev, obj: ac3d.Object, offset: co.Vec2d) -> None:
        """Writes a platform mapped as an area"""
        if len(self.nodes) < 3:
            logging.debug('ERROR: platform with osm_id=%d cannot created due to less then 3 nodes', self.osm_id)
            return
        linear_ring = shg.LinearRing(self.nodes)

        o = obj.next_node_index()
        if linear_ring.is_ccw:
            logging.debug('Anti-Clockwise')
        else:
            logging.debug("Clockwise")
            self.nodes = self.nodes[::-1]
        for p in self.nodes:
            e = fg_elev.probe_elev((p[0], p[1])) + 1
            obj.node(-p[1] + offset.y, e, -p[0] + offset.x)
        top_nodes = np.arange(len(self.nodes))
        self.segment_len = np.array([0] + [co.Vec2d(coord).distance_to(co.Vec2d(self.line_string.coords[i]))
                                           for i, coord in enumerate(self.line_string.coords[1:])])
        rd_len = len(self.line_string.coords)
        self.dist = np.zeros((rd_len))
        for i in range(1, rd_len):
            self.dist[i] = self.dist[i - 1] + self.segment_len[i]
        face = []
        x = 0.
        # Top Face
        for i, n in enumerate(top_nodes):
            face.append((n + o, x, 0.5))
        obj.face(face)
        # Build bottom ring
        for p in self.nodes:
            e = fg_elev.probe_elev((p[0], p[1])) - 1
            obj.node(-p[1] + offset.y, e, -p[0] + offset.x)
        # Build Sides
        for i, n in enumerate(top_nodes[1:]):
            sideface = list()
            sideface.append((n + o + rd_len - 1, x, 0.5))
            sideface.append((n + o + rd_len, x, 0.5))
            sideface.append((n + o, x, 0.5))
            sideface.append((n + o - 1, x, 0.5))
            obj.face(sideface)
コード例 #8
0
ファイル: linear.py プロジェクト: Joako360/Osm2city
    def _pillar(self, obj: ac3d.Object, x, y, h0, h1, angle):
        self.pillar_r0 = 1.
        self.pillar_r1 = 0.5
        self.pillar_nnodes = 8

        rx = self.pillar_r0
        ry = self.pillar_r1

        nodes_list = []
        ofs = obj.next_node_index()
        vert = ""
        r = np.array([[np.cos(-angle), -np.sin(-angle)],
                      [np.sin(-angle), np.cos(-angle)]])
        for a in np.linspace(0, 2 * np.pi, self.pillar_nnodes, endpoint=False):
            a += np.pi / self.pillar_nnodes
            node = np.array([rx * np.cos(a), ry * np.sin(a)])
            node = np.dot(r, node)
            obj.node(-(y + node[0]), h1, -(x + node[1]))
        for a in np.linspace(0, 2 * np.pi, self.pillar_nnodes, endpoint=False):
            a += np.pi / self.pillar_nnodes
            node = np.array([rx * np.cos(a), ry * np.sin(a)])
            node = np.dot(r, node)
            obj.node(-(y + node[0]), h0, -(x + node[1]))

        for i in range(self.pillar_nnodes - 1):
            face = [(ofs + i, 0, road.BOTTOM[0]),
                    (ofs + i + 1, 1, road.BOTTOM[0]),
                    (ofs + i + 1 + self.pillar_nnodes, 1, road.BOTTOM[1]),
                    (ofs + i + self.pillar_nnodes, 0, road.BOTTOM[1])]
            obj.face(face)

        i = self.pillar_nnodes - 1
        face = [(ofs + i, 0, road.BOTTOM[0]), (ofs, 1, road.BOTTOM[0]),
                (ofs + self.pillar_nnodes, 1, road.BOTTOM[1]),
                (ofs + i + self.pillar_nnodes, 0, road.BOTTOM[1])]
        obj.face(face)

        nodes_list.append(face)

        return ofs + 2 * self.pillar_nnodes, vert, nodes_list
コード例 #9
0
ファイル: linear.py プロジェクト: Joako360/Osm2city
    def write_to(self,
                 obj: ac3d.Object,
                 fg_elev: FGElev,
                 elev_offset,
                 offset=None) -> None:
        """
        write
        - deck
        - sides
        - bottom
        - pillars

        needs
        - pillar positions
        - deck elev
        -
        """
        n_nodes = len(self.left.coords)
        # -- deck height
        z = np.zeros(n_nodes)
        length = 0.
        for i in range(n_nodes):
            z[i] = self._deck_height(length, normalized=False)
            node = self.nodes_dict[self.way.refs[i]]
            layer = node.layer_for_way(self.way)
            if layer > 0:
                z[i] += layer * parameters.DISTANCE_BETWEEN_LAYERS
            z[i] += parameters.MIN_ABOVE_GROUND_LEVEL
            length += self.segment_len[i]

        left_top_nodes = self._write_nodes(obj,
                                           self.left,
                                           z,
                                           elev_offset,
                                           offset,
                                           join=True,
                                           is_left=True)
        right_top_nodes = self._write_nodes(obj,
                                            self.right,
                                            z,
                                            elev_offset,
                                            offset,
                                            join=True,
                                            is_left=False)

        left_bottom_edge, right_bottom_edge = self._compute_sides(self.width /
                                                                  2 * 0.85)
        left_bottom_nodes = self._write_nodes(
            obj, left_bottom_edge, z - parameters.BRIDGE_BODY_HEIGHT,
            elev_offset, offset)
        right_bottom_nodes = self._write_nodes(
            obj, right_bottom_edge, z - parameters.BRIDGE_BODY_HEIGHT,
            elev_offset, offset)
        # -- top
        self._write_quads(obj, left_top_nodes, right_top_nodes, self.tex[0],
                          self.tex[1], True)

        # -- right
        self._write_quads(obj, right_top_nodes, right_bottom_nodes,
                          road.BRIDGE_1[1], road.BRIDGE_1[0])

        # -- left
        self._write_quads(obj, left_bottom_nodes, left_top_nodes,
                          road.BRIDGE_1[0], road.BRIDGE_1[1])

        # -- bottom
        self._write_quads(obj, right_bottom_nodes, left_bottom_nodes,
                          road.BOTTOM[0], road.BOTTOM[1])

        # -- end wall 1
        the_node = self.left.coords[0]
        e = fg_elev.probe_elev(the_node) - elev_offset
        left_bottom_node = obj.node(-(the_node[1] - offset.y), e,
                                    -(the_node[0] - offset.x))

        the_node = self.right.coords[0]
        e = fg_elev.probe_elev(the_node) - elev_offset
        right_bottom_node = obj.node(-(the_node[1] - offset.y), e,
                                     -(the_node[0] - offset.x))

        face = [(left_top_nodes[0], 0, parameters.EMBANKMENT_TEXTURE[0]),
                (right_top_nodes[0], 0, parameters.EMBANKMENT_TEXTURE[1]),
                (right_bottom_node, 1, parameters.EMBANKMENT_TEXTURE[1]),
                (left_bottom_node, 1, parameters.EMBANKMENT_TEXTURE[0])]
        obj.face(face)

        # -- end wall 2
        the_node = self.left.coords[-1]
        e = fg_elev.probe_elev(the_node) - elev_offset
        left_bottom_node = obj.node(-(the_node[1] - offset.y), e,
                                    -(the_node[0] - offset.x))

        the_node = self.right.coords[-1]
        e = fg_elev.probe_elev(the_node) - elev_offset
        right_bottom_node = obj.node(-(the_node[1] - offset.y), e,
                                     -(the_node[0] - offset.x))

        face = [(left_top_nodes[-1], 0, parameters.EMBANKMENT_TEXTURE[0]),
                (right_top_nodes[-1], 0, parameters.EMBANKMENT_TEXTURE[1]),
                (right_bottom_node, 1, parameters.EMBANKMENT_TEXTURE[1]),
                (left_bottom_node, 1, parameters.EMBANKMENT_TEXTURE[0])]
        obj.face(face[::-1])

        # pillars
        z -= elev_offset
        for i in range(1, n_nodes - 1):
            z0 = fg_elev.probe_elev(self.center.coords[i]) - elev_offset - 1.
            point = self.center.coords[i]
            self._pillar(obj, point[0] - offset.x, point[1] - offset.y, z0,
                         z[i], self.angle[i])
コード例 #10
0
def separate_pyramidal(ac_object: ac.Object, b, roof_mat_idx: int) -> None:
    """Pyramidal, dome or onion roof."""
    shape = b.roof_shape
    roof_texture = b.roof_texture
        
    roof_height = sanity_roof_height_complex(b, 'pyramidal')

    bottom = b.beginning_of_roof_above_sea_level
            
    # add nodes for each of the corners
    object_node_index = ac_object.next_node_index()
    prev_ring = list()
    for pt in b.pts_all:
        ac_object.node(-pt[1], bottom, -pt[0])
        prev_ring.append([pt[0], pt[1]])

    # calculate node for the middle node of the roof
    x_centre = sum([xi[0] for xi in b.pts_all])/len(b.pts_all)
    y_centre = sum([xi[1] for xi in b.pts_all])/len(b.pts_all)

    ring = b.pts_all
    top = bottom + roof_height

    if shape in [enu.RoofShape.dome, enu.RoofShape.onion]:
        # For dome and onion we need to add new rings and faces before the top
        height_share = list()  # the share of the roof height by each ring
        radius_share = list()  # the share of the radius by each ring
        if shape is enu.RoofShape.dome:  # we use five additional rings
            height_share = [sin(radians(90 / 6)),
                            sin(radians(90 * 2 / 6)),
                            sin(radians(90 * 3 / 6)),
                            sin(radians(90 * 4 / 6)),
                            sin(radians(90 * 5 / 6))]

            radius_share = [cos(radians(90 / 6)),
                            cos(radians(90 * 2 / 6)),
                            cos(radians(90 * 3 / 6)),
                            cos(radians(90 * 4 / 6)),
                            cos(radians(90 * 5 / 6))]
        else:  # we use five additional rings based on guessed values - onion diameter gets broader than drum
            height_share = [.1, .2, .3, .4, .5, .7]

            radius_share = [1.2, 1.25, 1.2, 1., .6, .2]

        # texture it
        roof_texture_size = roof_texture.h_size_meters  # size of roof texture in meters

        n_pts = len(ring)
        for r in range(0, len(height_share)):
            ring = list()
            top = bottom + roof_height * height_share[r]
            # calculate the new points of the ring
            for pt in b.pts_all:
                x, y = coord.calc_point_on_line_local(pt[0], pt[1], x_centre, y_centre, 1 - radius_share[r])
                ac_object.node(-y, top, -x)
                ring.append([x, y])

            # create the faces
            prev_offset = r * n_pts
            this_offset = (r+1) * n_pts
            for i in range(0, n_pts):
                j = (i + 1) % n_pts  # little trick to reset to 0
                dist_edge = coord.calc_distance_local(ring[i][0], ring[i][1], ring[j][0], ring[j][1])
                dist_edge_prev = coord.calc_distance_local(prev_ring[i][0], prev_ring[i][1],
                                                           prev_ring[j][0], prev_ring[j][1])
                ring_height_diff = height_share[r] * roof_height
                ring_radius_diff = coord.calc_distance_local(ring[i][0], ring[i][1], prev_ring[i][0], prev_ring[i][1])
                len_roof_hypo = (ring_height_diff ** 2 + ring_radius_diff ** 2) ** 0.5
                repeat_x = dist_edge / roof_texture_size
                repeat_y = len_roof_hypo / roof_texture_size
                ac_object.face([(object_node_index + i + prev_offset, roof_texture.x(0), roof_texture.y(0)),
                                (object_node_index + j + prev_offset, roof_texture.x(repeat_x), roof_texture.y(0)),
                                (object_node_index + j + this_offset, roof_texture.x(repeat_x), roof_texture.y(repeat_y)),
                                (object_node_index + i + this_offset, roof_texture.x(0), roof_texture.y(repeat_y))],
                               mat_idx=roof_mat_idx)

            prev_ring = copy.deepcopy(ring)

        # prepare for pyramidal top
        top = bottom + roof_height
        bottom = bottom + roof_height * height_share[-1]
        object_node_index += len(height_share) * n_pts

    # add the pyramidal top
    _pyramidal_top(ac_object, x_centre, y_centre, top,
                   roof_texture, roof_mat_idx, top - bottom,
                   ring, object_node_index)