示例#1
0
class RoadGenerator:
    """Generate random roads given the configuration parameters"""

    MAX_ANGLE = 80
    NUM_SPLINE_NODES = 20
    NUM_INITIAL_SEGMENTS_THRESHOLD = 2
    NUM_UNDO_ATTEMPTS = 20
    SEG_LENGTH = 25

    def __init__(self,
                 num_control_nodes=15,
                 max_angle=MAX_ANGLE,
                 seg_length=SEG_LENGTH,
                 num_spline_nodes=NUM_SPLINE_NODES,
                 initial_node=(0.0, 0.0, -28.0, 8.0),
                 bbox_size=(-250, 0, 250, 500)):
        assert num_control_nodes > 1 and num_spline_nodes > 0
        assert 0 <= max_angle <= 360
        assert seg_length > 0
        assert len(initial_node) == 4 and len(bbox_size) == 4
        self.num_control_nodes = num_control_nodes
        self.num_spline_nodes = num_spline_nodes
        self.initial_node = initial_node
        self.max_angle = max_angle
        self.seg_length = seg_length
        self.road_bbox = RoadBoundingBox(bbox_size)
        assert not self.road_bbox.intersects_vertices(
            self._get_initial_point())
        assert self.road_bbox.intersects_sides(self._get_initial_point())

    def generate_control_nodes(self,
                               visualise=False,
                               attempts=NUM_UNDO_ATTEMPTS) -> List[Tuple4F]:
        condition = True
        while condition:
            nodes = [self._get_initial_control_node(), self.initial_node]

            # i is the number of valid generated control nodes.
            i = 0

            # When attempt >= attempts and the skeleton of the road is still invalid,
            # the construction of the skeleton starts again from the beginning.
            # attempt is incremented every time the skeleton is invalid.
            attempt = 0

            while i < self.num_control_nodes and attempt <= attempts:
                nodes.append(
                    self._get_next_node(nodes[-2], nodes[-1],
                                        self._get_next_max_angle(i)))
                road_polygon = RoadPolygon.from_nodes(nodes)

                # budget is the number of iterations used to attempt to add a valid next control node
                # before also removing the previous control node.
                budget = self.num_control_nodes - i
                assert budget >= 1

                if visualise:
                    fig = plot_road_bbox(self.road_bbox)
                    plot_road_polygon(road_polygon,
                                      title="RoadPolygon i=%s" % i,
                                      fig=fig)

                intersect_boundary = self.road_bbox.intersects_boundary(
                    road_polygon.polygons[-1])
                is_valid = road_polygon.is_valid() and (
                    ((i == 0) and intersect_boundary) or
                    ((i > 0) and not intersect_boundary))
                while not is_valid and budget > 0:
                    nodes.pop()
                    budget -= 1
                    attempt += 1

                    nodes.append(
                        self._get_next_node(nodes[-2], nodes[-1],
                                            self._get_next_max_angle(i)))
                    road_polygon = RoadPolygon.from_nodes(nodes)

                    intersect_boundary = self.road_bbox.intersects_boundary(
                        road_polygon.polygons[-1])
                    is_valid = road_polygon.is_valid() and (
                        ((i == 0) and intersect_boundary) or
                        ((i > 0) and not intersect_boundary))
                    if visualise:
                        fig = plot_road_bbox(self.road_bbox)
                        plot_road_polygon(road_polygon,
                                          title="RoadPolygon i=%s" % i,
                                          fig=fig)

                if is_valid:
                    i += 1
                else:
                    assert budget == 0
                    nodes.pop()
                    if len(nodes) > 2:
                        nodes.pop()
                        i -= 1

                assert RoadPolygon.from_nodes(nodes).is_valid()
                assert 0 <= i <= self.num_control_nodes
            print(len(nodes))
            print(self.num_control_nodes)
            # The road generation ends when there are the control nodes plus the two extra nodes needed by the current Catmull-Rom model
            if len(nodes) - 2 == self.num_control_nodes:
                condition = False
        return nodes

    def generate(self, visualise=False) -> BeamNGMember:
        control_nodes = self.generate_control_nodes(visualise)
        sample_nodes = catmull_rom(control_nodes, self.num_spline_nodes)
        road = BeamNGMember(control_nodes, sample_nodes, self.num_spline_nodes,
                            self.road_bbox)
        while not road.is_valid():
            control_nodes = self.generate_control_nodes(visualise)
            sample_nodes = catmull_rom(control_nodes, self.num_spline_nodes)
            road = BeamNGMember(control_nodes, sample_nodes,
                                self.num_spline_nodes, self.road_bbox)
        return road

    def _get_initial_point(self) -> Point:
        return Point(self.initial_node[0], self.initial_node[1])

    def _get_initial_control_node(self) -> Tuple4F:
        x0, y0, z, width = self.initial_node
        x, y = self._get_next_xy(x0, y0, 270)
        assert not (self.road_bbox.bbox.contains(Point(x, y)))

        return x, y, z, width

    def _get_next_node(self, first_node, second_node: Tuple4F,
                       max_angle) -> Tuple4F:
        v = np.subtract(second_node, first_node)
        start_angle = int(np.degrees(np.arctan2(v[1], v[0])))
        angle = randint(start_angle - max_angle, start_angle + max_angle)
        x0, y0, z0, width0 = second_node
        x1, y1 = self._get_next_xy(x0, y0, angle)
        return x1, y1, z0, width0

    def _get_next_xy(self, x0: float, y0: float, angle: float) -> Tuple2F:
        angle_rad = math.radians(angle)
        return x0 + self.seg_length * math.cos(
            angle_rad), y0 + self.seg_length * math.sin(angle_rad)

    def _get_next_max_angle(self,
                            i: int,
                            threshold=NUM_INITIAL_SEGMENTS_THRESHOLD) -> float:
        if i < threshold or i == self.num_control_nodes - 1:
            return 0
        else:
            return self.max_angle
示例#2
0
class RoadGenerator:
    """A class that is able to generate random roads given some parameters, such as
    the number of segments."""

    # Initial setup
    #num_control_nodes = 15
    #INITIAL_SEGMENTS_MAX_ANGLE = 90
    #NUM_INITIAL_SEGMENTS_THRESHOLD = 4
    #NUM_UNDO_ATTEMPTS = 20
    #max_angle=360
    #seg_length=50
    #num_spline_nodes=20
    #START_ANGLE = 45

    START_ANGLE = 60
    INITIAL_SEGMENTS_MAX_ANGLE = 60
    #MAX_ANGLE = 160
    MAX_ANGLE = 60

    NUM_SPLINE_NODES = 20
    NUM_INITIAL_SEGMENTS_THRESHOLD = 4
    NUM_UNDO_ATTEMPTS = 20
    SEG_LENGTH = 25

    def __init__(self,
                 num_control_nodes=15,
                 max_angle=MAX_ANGLE,
                 seg_length=SEG_LENGTH,
                 num_spline_nodes=NUM_SPLINE_NODES,
                 initial_node=(0.0, 0.0, -28.0, 8.0),
                 bbox_size=(-250, 0, 250, 500)):
        assert num_control_nodes > 1 and num_spline_nodes > 0
        assert 0 <= max_angle <= 360
        assert seg_length > 0
        assert len(initial_node) == 4 and len(bbox_size) == 4
        self.num_control_nodes = num_control_nodes
        self.num_spline_nodes = num_spline_nodes
        self.initial_node = initial_node
        self.max_angle = max_angle
        self.seg_length = seg_length
        self.road_bbox = RoadBoundingBox(bbox_size)
        assert not self.road_bbox.intersects_vertices(
            self._get_initial_point())
        assert self.road_bbox.intersects_sides(self._get_initial_point())

    def generate_control_nodes(self,
                               visualise=False,
                               attempts=NUM_UNDO_ATTEMPTS) -> List[Tuple4F]:
        while True:
            nodes = [self._get_initial_control_node(), self.initial_node]

            # i is the number of valid generated control nodes.
            i = 0

            # When attempt >= attempts and the skeleton of the road is still invalid,
            # the construction of the skeleton starts again from the beginning.
            # attempt is incremented every time the skeleton is invalid.
            attempt = 0

            while i < self.num_control_nodes:
                nodes.append(
                    self._get_next_node(nodes[-1],
                                        self._get_next_max_angle(i)))
                road_polygon = RoadPolygon.from_nodes(nodes)

                if visualise:
                    fig = plot_road_bbox(self.road_bbox)
                    plot_road_polygon(road_polygon,
                                      title="RoadPolygon i=%s" % i,
                                      fig=fig)

                # budget is the number of iterations used to attempt to add a valid next control node
                # before also removing the previous control node.
                budget = self.num_control_nodes - i
                assert budget >= 1

                is_valid = road_polygon.is_valid()
                while not is_valid and budget > 0:
                    nodes.pop()
                    budget = budget - 1
                    attempt += 1

                    nodes.append(
                        self._get_next_node(nodes[-1],
                                            self._get_next_max_angle(i)))
                    road_polygon = RoadPolygon.from_nodes(nodes)
                    is_valid = road_polygon.is_valid()

                    if visualise:
                        fig = plot_road_bbox(self.road_bbox)
                        plot_road_polygon(road_polygon,
                                          title="RoadPolygon i=%s" % i,
                                          fig=fig)

                if not is_valid:
                    assert budget == 0
                    nodes.pop()
                    if len(nodes) > 2:
                        nodes.pop()
                        i -= 1
                else:
                    i += 1

                assert RoadPolygon.from_nodes(nodes).is_valid()
                assert 0 <= i <= self.num_control_nodes

                if i >= 2 and self.road_bbox.intersects_boundary(
                        road_polygon.polygons[-1]):
                    break

                if attempt >= attempts:
                    break

            if attempt < attempts:
                return nodes

    def generate(self, visualise=False) -> BeamNGMember:
        control_nodes = self.generate_control_nodes(visualise)
        sample_nodes = catmull_rom(control_nodes, self.num_spline_nodes)
        road = BeamNGMember(control_nodes, sample_nodes, self.num_spline_nodes,
                            self.road_bbox)
        while not road.is_valid():
            control_nodes = self.generate_control_nodes(visualise)
            sample_nodes = catmull_rom(control_nodes, self.num_spline_nodes)
            road = BeamNGMember(control_nodes, sample_nodes,
                                self.num_spline_nodes, self.road_bbox)
        return road

    def _get_initial_point(self) -> Point:
        return Point(self.initial_node[0], self.initial_node[1])

    def _get_initial_control_node(self) -> Tuple4F:
        init_max_angle = self._get_next_max_angle(0)
        x, y, z, width = self._get_next_node(self.initial_node,
                                             init_max_angle,
                                             start_angle=240)
        #x, y, z, width = self._get_next_node(self.initial_node, self.max_angle)
        while self.road_bbox.bbox.contains(Point(x, y)):
            #x, y, _, _ = self._get_next_node(self.initial_node, self.max_angle)
            x, y, _, _ = self._get_next_node(self.initial_node,
                                             init_max_angle,
                                             start_angle=240)
        return x, y, z, width

    def _get_next_node(self,
                       initial_node: Tuple4F,
                       max_angle,
                       start_angle=START_ANGLE) -> Tuple4F:
        angle = randint(start_angle, max_angle + start_angle)
        x0, y0, z0, width0 = initial_node
        x1, y1 = self._get_next_xy(x0, y0, angle)
        return x1, y1, z0, width0

    def _get_next_xy(self, x0: float, y0: float, angle: float) -> Tuple2F:
        angle_rad = math.radians(angle)
        return x0 + self.seg_length * math.cos(
            angle_rad), y0 + self.seg_length * math.sin(angle_rad)

    def _get_next_max_angle(self,
                            i: int,
                            threshold=NUM_INITIAL_SEGMENTS_THRESHOLD) -> float:
        if i < threshold:
            return self.INITIAL_SEGMENTS_MAX_ANGLE
        else:
            return self.max_angle