diff --git a/map_machine/feature/building.py b/map_machine/feature/building.py index 2f9d502..4909b2a 100644 --- a/map_machine/feature/building.py +++ b/map_machine/feature/building.py @@ -38,19 +38,26 @@ def __init__( ) self.has_walls: bool = tags.get("building") != "roof" + self.default_fill: Color + self.default_stroke: Color if self.is_construction: - self.fill: Color = scheme.get_color("building_construction_color") - self.stroke: Color = scheme.get_color( + self.default_fill = scheme.get_color("building_construction_color") + self.default_stroke = scheme.get_color( "building_construction_border_color" ) else: - if color := tags.get("roof:colour"): - self.fill = scheme.get_color(color) - self.stroke: Color = Color(self.fill) - self.stroke.set_luminance(self.fill.get_luminance() * 0.85) - else: - self.fill: Color = scheme.get_color("building_color") - self.stroke: Color = scheme.get_color("building_border_color") + self.default_fill = scheme.get_color("building_color") + self.default_stroke = scheme.get_color("building_border_color") + + self.fill: Color + self.stroke: Color + if color := tags.get("roof:colour"): + self.fill = scheme.get_color(color) + self.stroke = Color(self.fill) + self.stroke.set_luminance(self.fill.get_luminance() * 0.85) + else: + self.fill = scheme.get_color("building_color") + self.stroke = scheme.get_color("building_border_color") self.parts: list[Segment] = [] @@ -65,31 +72,23 @@ def __init__( self.height: float = BUILDING_MINIMAL_HEIGHT self.min_height: float = 0.0 - self.wall_color: Color + self.wall_default_color: Color if self.is_construction: - self.wall_color = scheme.get_color("wall_construction_color") + self.wall_default_color = scheme.get_color( + "wall_construction_color" + ) else: - self.wall_color = scheme.get_color("wall_color") + self.wall_default_color = scheme.get_color("wall_color") + self.wall_color: Color = self.wall_default_color if material := tags.get("building:material"): if material in scheme.material_colors: self.wall_color = Color(scheme.material_colors[material]) - if color := tags.get("building:colour"): self.wall_color = scheme.get_color(color) - if color := tags.get("colour"): self.wall_color = scheme.get_color(color) - self.wall_bottom_color_1: Color = Color(self.wall_color) - self.wall_bottom_color_1.set_luminance( - self.wall_color.get_luminance() * 0.70 - ) - self.wall_bottom_color_2: Color = Color(self.wall_color) - self.wall_bottom_color_2.set_luminance( - self.wall_color.get_luminance() * 0.85 - ) - if levels := self.get_float("building:levels"): self.height = BUILDING_MINIMAL_HEIGHT + levels * LEVEL_HEIGHT @@ -102,18 +101,24 @@ def __init__( if height := self.get_length("min_height"): self.min_height = BUILDING_MINIMAL_HEIGHT + height - def draw(self, svg: Drawing, flinger: Flinger) -> None: + def draw( + self, svg: Drawing, flinger: Flinger, use_building_colors: bool + ) -> None: """Draw simple building shape.""" path: Path = Path( d=self.get_path(flinger), - stroke=self.stroke.hex, - fill=self.fill.hex, + stroke=self.stroke.hex + if use_building_colors + else self.default_stroke.hex, + fill=self.fill.hex + if use_building_colors + else self.default_fill.hex, stroke_linejoin="round", ) svg.add(path) def draw_shade(self, building_shade: Group, flinger: Flinger) -> None: - """Draw shade casted by the building.""" + """Draw shade cast by the building.""" scale: float = flinger.get_scale() * SHADE_SCALE shift_1: np.ndarray = np.array((scale * self.min_height, 0.0)) shift_2: np.ndarray = np.array((scale * self.height, 0.0)) @@ -141,7 +146,12 @@ def draw_shade(self, building_shade: Group, flinger: Flinger) -> None: building_shade.add(path) def draw_walls( - self, svg: Drawing, height: float, previous_height: float, scale: float + self, + svg: Drawing, + height: float, + previous_height: float, + scale: float, + use_building_colors: bool, ) -> None: """Draw building walls.""" if not self.has_walls: @@ -153,16 +163,36 @@ def draw_walls( shift_2: np.ndarray = np.array((0.0, -height * scale * BUILDING_SCALE)) for segment in self.parts: - draw_walls(svg, self, segment, height, shift_1, shift_2) + draw_walls( + svg, + self, + segment, + height, + shift_1, + shift_2, + use_building_colors, + ) - def draw_roof(self, svg: Drawing, flinger: Flinger, scale: float) -> None: + def draw_roof( + self, + svg: Drawing, + flinger: Flinger, + scale: float, + use_building_colors: bool, + ) -> None: """Draw building roof.""" + + fill: Color = self.fill if use_building_colors else self.default_fill + stroke: Color = ( + self.stroke if use_building_colors else self.default_stroke + ) + path: Path = Path( d=self.get_path( flinger, np.array([0.0, -self.height * scale * BUILDING_SCALE]) ), - stroke=self.stroke, - fill="none" if self.is_construction else self.fill.hex, + stroke=stroke, + fill="none" if self.is_construction else fill.hex, stroke_linejoin="round", ) svg.add(path) @@ -175,33 +205,42 @@ def draw_walls( height: float, shift_1: np.ndarray, shift_2: np.ndarray, -): + use_building_colors: bool, +) -> None: """ Draw walls for buildings as a quadrangle. Color of the wall is based on illumination. """ + color: Color = ( + building.wall_color + if use_building_colors + else building.wall_default_color + ) + color: Color if building.is_construction: color_part: float = segment.angle * 0.2 color = Color( rgb=( - building.wall_color.get_red() + color_part, - building.wall_color.get_green() + color_part, - building.wall_color.get_blue() + color_part, + color.get_red() + color_part, + color.get_green() + color_part, + color.get_blue() + color_part, ) ) elif height <= 0.25 / BUILDING_SCALE: - color = building.wall_bottom_color_1 + color = Color(color) + color.set_luminance(color.get_luminance() * 0.70) elif height <= 0.5 / BUILDING_SCALE: - color = building.wall_bottom_color_2 + color = Color(color) + color.set_luminance(color.get_luminance() * 0.85) else: color_part: float = segment.angle * 0.2 - 0.1 color = Color( rgb=( - max(min(building.wall_color.get_red() + color_part, 1), 0), - max(min(building.wall_color.get_green() + color_part, 1), 0), - max(min(building.wall_color.get_blue() + color_part, 1), 0), + max(min(color.get_red() + color_part, 1), 0), + max(min(color.get_green() + color_part, 1), 0), + max(min(color.get_blue() + color_part, 1), 0), ) ) diff --git a/map_machine/mapper.py b/map_machine/mapper.py index 91a4412..55ed2f3 100644 --- a/map_machine/mapper.py +++ b/map_machine/mapper.py @@ -92,7 +92,7 @@ def draw(self, constructor: Constructor) -> None: for crater in constructor.craters: crater.draw(self.svg, self.flinger) - self.draw_buildings(constructor) + self.draw_buildings(constructor, self.configuration.use_building_colors) for direction_sector in constructor.direction_sectors: direction_sector.draw(self.svg, self.scheme) @@ -133,16 +133,18 @@ def draw(self, constructor: Constructor) -> None: if self.configuration.show_credit: self.draw_credits(constructor.flinger.size) - def draw_buildings(self, constructor: Constructor) -> None: + def draw_buildings( + self, constructor: Constructor, use_building_colors: bool + ) -> None: """Draw buildings: shade, walls, and roof.""" if self.configuration.building_mode == BuildingMode.NO: return if self.configuration.building_mode == BuildingMode.FLAT: for building in constructor.buildings: - building.draw(self.svg, self.flinger) + building.draw(self.svg, self.flinger, use_building_colors) return - logging.info("Drawing buildings...") + logging.info("Drawing isometric buildings...") scale: float = self.flinger.get_scale() building_shade: Group = Group(opacity=0.1) @@ -171,12 +173,22 @@ def draw_buildings(self, constructor: Constructor) -> None: if building.height < height or building.min_height >= height: continue - draw_walls(self.svg, building, wall, height, shift_1, shift_2) + draw_walls( + self.svg, + building, + wall, + height, + shift_1, + shift_2, + use_building_colors, + ) if self.configuration.draw_roofs: for building in constructor.buildings: if building.height == height: - building.draw_roof(self.svg, self.flinger, scale) + building.draw_roof( + self.svg, self.flinger, scale, use_building_colors + ) previous_height = height