let segmented road generator take specific angles and lengths for segments

This commit is contained in:
Pim Nelissen
2026-03-02 13:00:14 +01:00
parent 8e429fe636
commit bb781ed082
5 changed files with 48 additions and 79 deletions

View File

@ -11,7 +11,6 @@ from .specs import (
MetadataSpec,
RuntimeSpec,
SimulationOptionsSpec,
SegmentSpec,
PathSpec,
ProceduralPathSpec,
CSVPathSpec,
@ -122,7 +121,7 @@ class ConfigParser:
def _parse_path(self) -> PathSpec:
allowed_csv = {"file", "east_col_name", "north_col_name", "z"}
allowed_proc = {"segments", "length", "z"}
allowed_proc = {"segments", "length", "z", "alpha"}
path = self.config.get("path")
if path is None:
@ -172,13 +171,15 @@ class ConfigParser:
if isinstance(raw_length, int | float):
raw_length = [float(raw_length)]
segments = self._process_segment_angles(raw_segments)
segments, angles = self._process_segment_angles(raw_segments)
lengths = self._process_segment_lengths(raw_length, len(segments))
resolved_segments = self._combine_segments_lengths(segments, lengths)
return ProceduralPathSpec(
segments=resolved_segments,
segments=segments,
angles=angles,
lengths=lengths,
z=path.get("z", defaults.DEFAULT_PATH_HEIGHT),
alpha=path.get("alpha", defaults.DEFAULT_ALPHA)
)
def _process_segment_angles(
@ -186,23 +187,25 @@ class ConfigParser:
raw_segments: List[Union[str, dict]]
) -> List[Dict[str, Any]]:
normalized = []
segments, angles = [], []
for segment in raw_segments:
if isinstance(segment, str):
normalized.append({"type": segment, "angle": None})
segments.append(segment)
angles.append(None)
elif isinstance(segment, dict):
if len(segment) != 1:
raise ValueError("Invalid segment definition.")
seg_type, angle = list(segment.items())[0]
normalized.append({"type": seg_type, "angle": angle})
segments.append(seg_type)
angles.append(angle)
else:
raise ValueError("Invalid segment entry format.")
return normalized
return segments, angles
def _process_segment_lengths(
self,
@ -219,32 +222,6 @@ class ConfigParser:
"number of elements equal to the number of segments."
)
def _combine_segments_lengths(
self,
segments: List[Dict[str, Any]],
lengths: List[float],
) -> List[SegmentSpec]:
resolved = []
for seg, length in zip(segments, lengths):
angle = seg["angle"]
if angle is not None and not self._is_turn(seg["type"]):
raise ValueError(
f"A {seg['type']} segment does not support an angle."
)
resolved.append(
SegmentSpec(
type=seg["type"],
length=length,
angle=angle,
)
)
return resolved
@staticmethod
def _is_turn(segment_type: str) -> bool:
return segment_type in {"turn_left", "turn_right"}

View File

@ -15,17 +15,10 @@ class RuntimeSpec:
@dataclass
class SimulationOptionsSpec:
air_density: float = 1.243
air_density: float
seed: int | None = None
@dataclass
class SegmentSpec:
type: str
length: float
angle: float | None
@dataclass
class PathSpec(ABC):
pass
@ -33,8 +26,11 @@ class PathSpec(ABC):
@dataclass
class ProceduralPathSpec(PathSpec):
segments: list[SegmentSpec]
segments: list[str]
angles: list[float]
lengths: list[int | None]
z: int | float
alpha: float
@dataclass

View File

@ -56,23 +56,24 @@ class LandscapeBuilder:
self,
sim_spec: SimulationSpec
):
segments = sim_spec.path.segments
types = [s.type for s in segments]
lengths = [s.length for s in segments]
angles = [s.angle for s in segments]
lengths = sim_spec.path.lengths
angles = sim_spec.path.angles
alpha = sim_spec.path.alpha
print(segments, lengths, angles)
sg = SegmentedRoadGenerator(
length=lengths,
ds=sim_spec.runtime.speed * sim_spec.runtime.acquisition_time,
velocity=sim_spec.runtime.speed,
seed=sim_spec.options.seed
)
x, y = sg.generate(
segments=types,
segments=segments,
lengths=lengths,
angles=angles
angles=angles,
alpha=alpha
)
self._path = Path(list(zip(x, y)))