new stair interpolation based on dijkstra
This commit is contained in:
parent
2573ffc05b
commit
3c9013220e
1 changed files with 54 additions and 37 deletions
|
@ -1,9 +1,11 @@
|
||||||
import itertools
|
import itertools
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter, itemgetter
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from scipy.sparse.csgraph._shortest_path import dijkstra
|
||||||
from shapely.affinity import scale
|
from shapely.affinity import scale
|
||||||
from shapely.geometry import JOIN_STYLE, LineString
|
from shapely.geometry import JOIN_STYLE, LineString
|
||||||
from shapely.ops import cascaded_union
|
from shapely.ops import cascaded_union
|
||||||
|
@ -256,46 +258,61 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
||||||
del area_connections[tmpid]
|
del area_connections[tmpid]
|
||||||
|
|
||||||
# interpolate altitudes
|
# interpolate altitudes
|
||||||
while areas_without_altitude:
|
areas_with_altitude = [i for i in range(len(areas)) if i not in areas_without_altitude]
|
||||||
# find a area without an altitude that is connected
|
for i, tmpid in enumerate(areas_with_altitude):
|
||||||
# to one with an altitude to start the chain
|
areas[tmpid].i = i
|
||||||
chain = []
|
|
||||||
for tmpid in areas_without_altitude:
|
csgraph = np.zeros((len(areas), len(areas)), dtype=bool)
|
||||||
|
for area in areas:
|
||||||
|
for connected_tmpid in area.connected_to:
|
||||||
|
csgraph[area.tmpid, connected_tmpid] = True
|
||||||
|
|
||||||
|
repeat = True
|
||||||
|
while repeat:
|
||||||
|
repeat = False
|
||||||
|
distances, predecessors = dijkstra(csgraph, directed=False, return_predecessors=True, unweighted=True)
|
||||||
|
relevant_distances = distances[np.array(areas_with_altitude)[:, None], np.array(areas_with_altitude)]
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
for from_i, to_i in np.argwhere(np.logical_and(relevant_distances < np.inf, relevant_distances > 1)):
|
||||||
|
from_area = areas[areas_with_altitude[from_i]]
|
||||||
|
to_area = areas[areas_with_altitude[to_i]]
|
||||||
|
if from_area.altitude == to_area.altitude:
|
||||||
|
continue
|
||||||
|
|
||||||
|
path = [to_area.tmpid]
|
||||||
|
while path[-1] != from_area.tmpid:
|
||||||
|
path.append(predecessors[from_area.tmpid, path[-1]])
|
||||||
|
|
||||||
|
from_altitude = from_area.altitude
|
||||||
|
delta_altitude = (to_area.altitude-from_altitude)/(len(path)-1)
|
||||||
|
|
||||||
|
if set(path[1:-1]).difference(areas_without_altitude):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for i, tmpid in enumerate(reversed(path[1:-1]), start=1):
|
||||||
|
area = areas[tmpid]
|
||||||
|
area.altitude = from_altitude+delta_altitude*i
|
||||||
|
areas_without_altitude.discard(tmpid)
|
||||||
|
area.i = len(areas_with_altitude)
|
||||||
|
areas_with_altitude.append(tmpid)
|
||||||
|
|
||||||
|
for from_tmpid, to_tmpid in zip(path[:-1], path[1:]):
|
||||||
|
csgraph[from_tmpid, to_tmpid] = False
|
||||||
|
csgraph[to_tmpid, from_tmpid] = False
|
||||||
|
|
||||||
|
repeat = True
|
||||||
|
|
||||||
|
# remaining areas: copy altitude from connected areas if any
|
||||||
|
repeat = True
|
||||||
|
while repeat:
|
||||||
|
repeat = False
|
||||||
|
for tmpid in tuple(areas_without_altitude):
|
||||||
area = areas[tmpid]
|
area = areas[tmpid]
|
||||||
connected_with_altitude = area.connected_to-areas_without_altitude
|
connected_with_altitude = area.connected_to-areas_without_altitude
|
||||||
if connected_with_altitude:
|
if connected_with_altitude:
|
||||||
chain = [next(iter(connected_with_altitude)), tmpid]
|
area.altitude = areas[next(iter(connected_with_altitude))].altitude
|
||||||
current = area
|
areas_without_altitude.discard(tmpid)
|
||||||
break
|
repeat = True
|
||||||
else:
|
|
||||||
# there are no more chains possible
|
|
||||||
break
|
|
||||||
|
|
||||||
# continue chain as long as possible
|
|
||||||
while True:
|
|
||||||
connected_with_altitude = (current.connected_to-areas_without_altitude).difference(chain)
|
|
||||||
if connected_with_altitude:
|
|
||||||
# interpolate
|
|
||||||
area = areas[next(iter(connected_with_altitude))]
|
|
||||||
from_altitude = areas[chain[0]].altitude
|
|
||||||
delta_altitude = area.altitude-from_altitude
|
|
||||||
for i, tmpid in enumerate(chain[1:], 1):
|
|
||||||
areas[tmpid].altitude = from_altitude+delta_altitude*i/len(chain)
|
|
||||||
areas_without_altitude.difference_update(chain)
|
|
||||||
break
|
|
||||||
|
|
||||||
connected = current.connected_to.difference(chain)
|
|
||||||
if not connected:
|
|
||||||
# end of chain
|
|
||||||
altitude = areas[chain[0]].altitude
|
|
||||||
for i, tmpid in enumerate(chain[1:], 1):
|
|
||||||
areas[tmpid].altitude = altitude
|
|
||||||
areas_without_altitude.difference_update(chain)
|
|
||||||
break
|
|
||||||
|
|
||||||
# continue chain
|
|
||||||
current = areas[next(iter(connected))]
|
|
||||||
chain.append(current.tmpid)
|
|
||||||
|
|
||||||
# remaining areas which belong to a room that has an altitude somewhere
|
# remaining areas which belong to a room that has an altitude somewhere
|
||||||
for contained_areas in space_areas.values():
|
for contained_areas in space_areas.values():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue