remove all old rendering methods
This commit is contained in:
parent
c365debfba
commit
bf1e8a9b11
1 changed files with 1 additions and 183 deletions
|
@ -1,20 +1,13 @@
|
||||||
import os
|
|
||||||
from decimal import Decimal
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Prefetch
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from shapely.geometry import JOIN_STYLE, box
|
|
||||||
from shapely.ops import cascaded_union
|
from shapely.ops import cascaded_union
|
||||||
|
|
||||||
from c3nav.mapdata.models.locations import SpecificLocation
|
from c3nav.mapdata.models.locations import SpecificLocation
|
||||||
from c3nav.mapdata.utils.geometry import assert_multipolygon
|
|
||||||
from c3nav.mapdata.utils.scad import add_indent, polygon_scad
|
|
||||||
|
|
||||||
|
|
||||||
class LevelManager(models.Manager):
|
class LevelManager(models.Manager):
|
||||||
|
@ -98,50 +91,6 @@ class Level(SpecificLocation, models.Model):
|
||||||
result['editor_url'] = reverse('editor.levels.detail', kwargs={'pk': self.pk})
|
result['editor_url'] = reverse('editor.levels.detail', kwargs={'pk': self.pk})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _render_space_ground(self, svg, space):
|
|
||||||
areas_by_color = {}
|
|
||||||
for area in space.areas.all():
|
|
||||||
areas_by_color.setdefault(area.get_color(), []).append(area)
|
|
||||||
areas_by_color.pop(None, None)
|
|
||||||
areas_by_color.pop('', None)
|
|
||||||
for i, (color, color_areas) in enumerate(areas_by_color.items()):
|
|
||||||
geometries = cascaded_union(tuple(area.geometry for area in color_areas)).intersection(space.geometry)
|
|
||||||
svg.add_geometry(geometries, fill_color=color)
|
|
||||||
|
|
||||||
stair_geometries = tuple(stair.geometry for stair in space.stairs.all())
|
|
||||||
svg.add_geometry(cascaded_union(stair_geometries).intersection(space.geometry),
|
|
||||||
stroke_width=0.06, stroke_color='#000000', opacity=0.15)
|
|
||||||
for i in range(2):
|
|
||||||
svg.add_geometry(cascaded_union(tuple(g.parallel_offset(0.06+0.04*i, 'right', join_style=JOIN_STYLE.mitre)
|
|
||||||
for g in stair_geometries)).intersection(space.geometry),
|
|
||||||
stroke_width=0.04, stroke_color='#000000', opacity=0.07-0.05*i)
|
|
||||||
|
|
||||||
def _render_space_inventory(self, svg, space):
|
|
||||||
obstacle_geometries = cascaded_union(
|
|
||||||
tuple(obstacle.geometry for obstacle in space.obstacles.all()) +
|
|
||||||
tuple(obstacle.buffered_geometry for obstacle in space.lineobstacles.all())
|
|
||||||
).intersection(space.geometry)
|
|
||||||
svg.add_geometry(obstacle_geometries, fill_color='#999999')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _give_height_to_areas_with_one_neighbor(accessible_area, areas_by_altitude):
|
|
||||||
# give height to all obstacles that touch only one altitude
|
|
||||||
remaining_polygons = []
|
|
||||||
for polygon in assert_multipolygon(accessible_area):
|
|
||||||
buffered = polygon.buffer(0.001)
|
|
||||||
found_altitude = None
|
|
||||||
for altitude, area in areas_by_altitude.items():
|
|
||||||
if buffered.intersects(area[0]):
|
|
||||||
if found_altitude is not None:
|
|
||||||
found_altitude = None
|
|
||||||
break
|
|
||||||
found_altitude = altitude
|
|
||||||
if found_altitude is None:
|
|
||||||
remaining_polygons.append(polygon)
|
|
||||||
else:
|
|
||||||
areas_by_altitude[found_altitude].append(polygon)
|
|
||||||
return cascaded_union(remaining_polygons)
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def min_altitude(self):
|
def min_altitude(self):
|
||||||
return min(self.altitudeareas.all(), key=attrgetter('altitude'), default=self.base_altitude).altitude
|
return min(self.altitudeareas.all(), key=attrgetter('altitude'), default=self.base_altitude).altitude
|
||||||
|
@ -150,134 +99,3 @@ class Level(SpecificLocation, models.Model):
|
||||||
def bounds(self):
|
def bounds(self):
|
||||||
return cascaded_union(tuple(item.geometry.buffer(0)
|
return cascaded_union(tuple(item.geometry.buffer(0)
|
||||||
for item in chain(self.altitudeareas.all(), self.buildings.all()))).bounds
|
for item in chain(self.altitudeareas.all(), self.buildings.all()))).bounds
|
||||||
|
|
||||||
def _render_scad_polygon(self, f, geometry, altitude, height=Decimal('0.0'), low_clip=()):
|
|
||||||
for low_altitude, low_area in low_clip:
|
|
||||||
intersection = geometry.intersection(low_area)
|
|
||||||
if not intersection.is_empty:
|
|
||||||
geometry = geometry.difference(intersection)
|
|
||||||
|
|
||||||
low_height = max(altitude - low_altitude, 0)
|
|
||||||
total_height = low_height+height
|
|
||||||
if total_height:
|
|
||||||
f.write(' ')
|
|
||||||
f.write('translate([0, 0, %.2f]) ' % (altitude - low_height))
|
|
||||||
f.write(add_indent(polygon_scad(intersection, total_height))[4:])
|
|
||||||
if not geometry.is_empty:
|
|
||||||
f.write(' ')
|
|
||||||
f.write('translate([0, 0, %.2f]) ' % (altitude - Decimal('0.5')))
|
|
||||||
f.write(add_indent(polygon_scad(geometry, height+Decimal('0.5')))[4:])
|
|
||||||
|
|
||||||
def _render_scad(self, f, low_clip=(), spaces=None, request=None):
|
|
||||||
f.write(' // '+self.title+'\n')
|
|
||||||
|
|
||||||
if spaces is None:
|
|
||||||
from c3nav.mapdata.models import Area, Space
|
|
||||||
spaces = self.spaces.filter(Space.q_for_request(request, allow_none=True)).prefetch_related(
|
|
||||||
Prefetch('areas', Area.qs_for_request(request, allow_none=True)),
|
|
||||||
'groups', 'columns', 'holes', 'areas__groups',
|
|
||||||
'stairs', 'obstacles', 'lineobstacles'
|
|
||||||
)
|
|
||||||
|
|
||||||
f.write('')
|
|
||||||
|
|
||||||
for area in self.altitudeareas.all():
|
|
||||||
area.geometry = area.geometry.buffer(0)
|
|
||||||
self._render_scad_polygon(f, area.geometry, area.altitude, low_clip=low_clip)
|
|
||||||
|
|
||||||
draw_obstacles = {}
|
|
||||||
height_spaces = {}
|
|
||||||
for space in spaces:
|
|
||||||
columns = cascaded_union(tuple(columns.geometry for columns in space.columns.all()))
|
|
||||||
space.geometry = space.geometry.difference(columns)
|
|
||||||
if self.on_top_of_id is None and not space.outside:
|
|
||||||
height = space.height or self.default_height
|
|
||||||
height_spaces.setdefault(height, []).append(space.geometry)
|
|
||||||
holes = cascaded_union(tuple(hole.geometry for hole in space.holes.all()))
|
|
||||||
for lineobstacle in space.lineobstacles.all():
|
|
||||||
lineobstacle.geometry = lineobstacle.buffered_geometry
|
|
||||||
for obstacle in chain(space.obstacles.all(), space.lineobstacles.all()):
|
|
||||||
geometry = obstacle.geometry.intersection(space.geometry).difference(holes)
|
|
||||||
for altitudearea in self.altitudeareas.all():
|
|
||||||
intersection = geometry.intersection(altitudearea.geometry)
|
|
||||||
if not intersection.is_empty:
|
|
||||||
geometry = geometry.difference(intersection.buffer(0.001, join_style=JOIN_STYLE.mitre))
|
|
||||||
draw_obstacles.setdefault((altitudearea.altitude, obstacle.height), []).append(intersection)
|
|
||||||
if not geometry.is_empty:
|
|
||||||
for polygon in assert_multipolygon(geometry):
|
|
||||||
center = polygon.centroid
|
|
||||||
altitude = min(self.altitudeareas.all(), key=lambda a: a.geometry.distance(center)).altitude
|
|
||||||
draw_obstacles.setdefault((altitude, obstacle.height), []).append(polygon)
|
|
||||||
|
|
||||||
for (altitude, height), polygons in draw_obstacles.items():
|
|
||||||
self._render_scad_polygon(f, cascaded_union(polygons), altitude, height, low_clip=low_clip)
|
|
||||||
|
|
||||||
spaces_geom = cascaded_union(tuple(space.geometry for space in self.spaces.all() if not space.outside))
|
|
||||||
buildings_geom = cascaded_union(tuple(building.geometry for building in self.buildings.all()))
|
|
||||||
doors_geom = cascaded_union(tuple(door.geometry for door in self.doors.all()))
|
|
||||||
walls_geom = buildings_geom.difference(doors_geom).difference(spaces_geom)
|
|
||||||
|
|
||||||
drawn_walls = {}
|
|
||||||
for height, polygons in sorted(height_spaces.items(), key=itemgetter(0)):
|
|
||||||
polygons = cascaded_union(polygons)
|
|
||||||
for area in self.altitudeareas.all():
|
|
||||||
intersection = area.geometry.intersection(polygons)
|
|
||||||
if not intersection.is_empty:
|
|
||||||
walls = intersection.buffer(0.5, join_style=JOIN_STYLE.mitre).intersection(walls_geom)
|
|
||||||
walls = walls.buffer(0.001, join_style=JOIN_STYLE.mitre)
|
|
||||||
self._render_scad_polygon(f, walls, area.altitude+height, low_clip=low_clip)
|
|
||||||
drawn_walls.setdefault(area.altitude+height, []).append(walls)
|
|
||||||
|
|
||||||
remaining_walls_geom = walls_geom.difference(cascaded_union(tuple(chain(*drawn_walls.values()))))
|
|
||||||
|
|
||||||
drawn_walls = {altitude: cascaded_union(walls) for altitude, walls in drawn_walls.items()}
|
|
||||||
drawn_walls_sorted = sorted(drawn_walls.items(), key=itemgetter(0))
|
|
||||||
for wall in assert_multipolygon(remaining_walls_geom):
|
|
||||||
buffered = wall.buffer(0.001, join_style=JOIN_STYLE.mitre)
|
|
||||||
try:
|
|
||||||
altitude = next(iter(altitude for altitude, geom in drawn_walls_sorted if geom.intersects(buffered)))
|
|
||||||
except StopIteration:
|
|
||||||
altitude = min(drawn_walls_sorted, key=lambda a: buffered.distance(a[1]))[0]
|
|
||||||
self._render_scad_polygon(f, buffered, altitude, low_clip=low_clip)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _render_scad_levels(cls, levels, filename, level_spaces):
|
|
||||||
bounds = cascaded_union(tuple(box(*level.bounds) for level in levels)).bounds
|
|
||||||
center = tuple(box(*bounds).centroid.coords[0])
|
|
||||||
min_altitude = min((level.min_altitude for level in levels), default=0)
|
|
||||||
|
|
||||||
filename = os.path.join(settings.RENDER_ROOT, filename)
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write('translate([%.2f, %.2f, %.2f]) {\n' % (0-center[0], 0-center[1], 0-min_altitude+Decimal('0.5')))
|
|
||||||
first = True
|
|
||||||
for level in levels:
|
|
||||||
low_clip = []
|
|
||||||
if first:
|
|
||||||
low_clip = [(level.min_altitude-Decimal('0.5'), box(*bounds))]
|
|
||||||
first = False
|
|
||||||
level._render_scad(f, spaces=level_spaces.get(level.pk, []), low_clip=low_clip)
|
|
||||||
f.write('}\n')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def render_scad_all(cls, levels=None, request=None):
|
|
||||||
from c3nav.mapdata.models import Level, Area, Space
|
|
||||||
spaces = Space.objects.filter(Space.q_for_request(request, allow_none=True)).prefetch_related(
|
|
||||||
Prefetch('areas', Area.qs_for_request(request, allow_none=True)),
|
|
||||||
'groups', 'columns', 'holes', 'areas__groups',
|
|
||||||
'stairs', 'obstacles', 'lineobstacles'
|
|
||||||
)
|
|
||||||
level_spaces = {}
|
|
||||||
for space in spaces:
|
|
||||||
level_spaces.setdefault(space.level_id, []).append(space)
|
|
||||||
|
|
||||||
if levels is None:
|
|
||||||
levels = Level.objects
|
|
||||||
levels = levels.prefetch_related('buildings', 'doors', 'altitudeareas').order_by('base_altitude')
|
|
||||||
|
|
||||||
cls._render_scad_levels(levels, 'all.levels.scad', level_spaces)
|
|
||||||
|
|
||||||
for level in levels:
|
|
||||||
if level.on_top_of_id is not None:
|
|
||||||
continue
|
|
||||||
sublevels = tuple(sublevel for sublevel in levels if sublevel.on_top_of_id == level.pk)
|
|
||||||
cls._render_scad_levels((level, )+sublevels, level.get_slug()+'.scad', level_spaces)
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue