Implement create_staircase page

This commit is contained in:
Fabio Giovanazzi 2025-08-02 09:46:43 +02:00
parent 2804fd4104
commit e893e53151
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
5 changed files with 186 additions and 5 deletions

View file

@ -185,6 +185,8 @@ editor = {
// Clear snap indicators when unloading
editor._clear_snap_indicators();
editor._destroy_staircase_editing();
},
_fill_level_control: function (level_control, level_list, geometryURLs) {
var levels = level_list.find('a');
@ -1365,7 +1367,7 @@ editor = {
} else if (mapitem_type) {
// creating a new geometry, already drawn but form was rejected
options = editor._get_mapitem_type_style(mapitem_type);
if (mapitem_type === 'area') {
if (mapitem_type === 'area' || mapitem_type === 'staircase') {
options.fillOpacity = 0.5;
}
}
@ -1385,7 +1387,7 @@ editor = {
} else if (form.is('[data-new]')) {
// create new geometry
options = editor._get_mapitem_type_style(mapitem_type);
if (mapitem_type === 'area') {
if (mapitem_type === 'area' || mapitem_type === 'staircase') {
options.fillOpacity = 0.5;
}
form.addClass('creation-lock');
@ -1434,6 +1436,10 @@ editor = {
}
startGeomEditing(selected_geomtype);
}
if (mapitem_type === 'staircase') {
editor._setup_staircase_editing()
}
}
},
_cancel_editing: function () {
@ -1886,6 +1892,127 @@ editor = {
if (editor._snap_indicator) {
editor._snap_indicator.clearLayers();
}
},
_setup_staircase_editing: function() {
editor._staircase_steps_count = 10
editor._staircase_layer = L.layerGroup().addTo(editor.map);
$('#stairway-steps').on('input', function() {
editor._staircase_steps_count = parseInt($(this).val()) || 10;
editor._update_staircase_preview();
});
editor.map.on('editable:editing', editor._update_staircase_preview);
},
_destroy_staircase_editing: function() {
if (editor._staircase_layer !== null) {
editor.map.removeLayer(editor._staircase_layer)
editor._staircase_layer = null
}
editor.map.off('editable:editing', editor._update_staircase_preview)
if (editor._current_editing_shape !== null) {
editor._current_editing_shape.editor.cancelDrawing()
editor._current_editing_shape.remove()
editor._current_editing_shape = null
}
},
_transform_point_for_staircase: function(p, p0, cos_a, sin_a) {
return {
x: + (p.x - p0.x) * cos_a + (p.y - p0.y) * sin_a + p0.x,
y: - (p.x - p0.x) * sin_a + (p.y - p0.y) * cos_a + p0.y,
}
},
_transform_for_staircase: function(xs, ys, num_stairs) {
let base_length = Math.sqrt((xs[1]-xs[0])**2 + (ys[1]-ys[0])**2)
let cos_a = (xs[1] - xs[0]) / base_length
let sin_a = (ys[1] - ys[0]) / base_length
let p0 = { x: xs[0], y: ys[0] }
xs = points.map(p => editor._transform_point_for_staircase(p, p0, cos_a, sin_a).x)
ys = points.map(p => editor._transform_point_for_staircase(p, p0, cos_a, sin_a).y)
n = xs.length
if (Math.abs(Math.max(...ys) - ys[0]) > Math.abs(Math.min(...ys) - ys[0])) {
height = Math.max(...ys) - ys[0]
} else {
height = Math.min(...ys) - ys[0]
}
//console.log(xs, ys, base_length, height)
lines = [{p1: { x: xs[0], y: ys[0] }, p2: { x: xs[1], y: ys[1] }}]
for (i = 1; i < num_stairs; ++i) {
// intersect line y=y0+height/num_stairs*i with all transformed (xs,ys)
y = ys[0] + height/num_stairs*i
inters_xs = []
for (j = 0; j < n; ++j) {
y1 = ys[j]
y2 = ys[(j+1)%n]
x1 = xs[j]
x2 = xs[(j+1)%n]
if ((y1 > y && y2 > y) || (y1 < y && y2 < y)) {
//console.log("disconnected", y, j, x1,y1, x2,y2, xs, ys)
continue
}
if (Math.abs(x2 - x1) < 0.0001) {
// vertical line, m would be infinity
inters_xs.push(x1)
continue
}
m = (y2 - y1) / (x2 - x1)
q = y2 - m * x2
//console.log("connected", y, j, x1,y1, x2,y2, m, q, xs, ys)
inters_xs.push((y - q) / m)
}
//console.log("inters_xs", inters_xs)
if (inters_xs.length < 2) {
continue
}
min_xs = Math.min(...inters_xs)
max_xs = Math.max(...inters_xs)
lines.push({p1: {x: min_xs-2, y: y}, p2: {x: max_xs+2, y: y}})
}
//console.log("untransformed lines", lines)
lines = lines.map(l => ({
p1: editor._transform_point_for_staircase(l.p1, p0, cos_a, -sin_a),
p2: editor._transform_point_for_staircase(l.p2, p0, cos_a, -sin_a),
}))
//console.log(lines)
return lines
},
_update_staircase_preview: function(e = null) {
if (editor._current_editing_shape === null) {
return
}
points = editor._current_editing_shape._parts[0] || []
editor._staircase_layer.clearLayers()
//console.log(points)
if (points.length < 3) {
return
}
xs = points.map(p => p.x)
ys = points.map(p => p.y)
lines = editor._transform_for_staircase(xs, ys, editor._staircase_steps_count)
lines.forEach(l => {
L.polyline(
[
editor.map.layerPointToLatLng([l.p1.x, l.p1.y]),
editor.map.layerPointToLatLng([l.p2.x, l.p2.y]),
],
{color: "red"}
).addTo(editor._staircase_layer);
})
}
};

View file

@ -0,0 +1,40 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_levels.html' %}
<h3>
{% blocktrans %}Add staircase{% endblocktrans %}
</h3>
{% bootstrap_messages %}
<form {% if nozoom %}data-nozoom {% endif %}data-onbeforeunload data-new="staircase" data-geomtype="polygon" {% if access_restriction_select %} data-access-restriction-select{% endif %}>
{% csrf_token %}
{% bootstrap_form form %}
<div class="form-group">
<label for="stairway-steps">Number of Steps:</label>
<input type="number" id="stairway-steps" class="form-control" value="10">
</div>
{% buttons %}
<button class="invisiblesubmit" type="submit"></button>
<!-- <div class="btn-group">
<button type="button" id="generate-staircase" accesskey="g" class="btn btn-primary pull-right">
{% trans 'Generate stairs' %}
</button>
</div> -->
{% if can_edit %}
{% if not nosave %}
<button type="submit" accesskey="m" class="btn btn-primary pull-right">
{% trans 'Save' %}
</button>
{% endif %}
{% endif %}
<a class="btn {% if new %}btn-danger{% else %}btn-default {% if can_edit %}pull-right{% endif %}{% endif %} cancel-btn" href="{{ back_url }}">
{% if can_edit %}
{% trans 'Cancel' %}
{% else %}
{% trans 'Back' %}
{% endif %}
</a>
{% endbuttons %}
</form>

View file

@ -20,6 +20,11 @@
<a class="btn btn-default btn-xs" accesskey="n" href="{{ create_url }}">
<i class="glyphicon glyphicon-plus"></i> {% blocktrans %}New {{ model_title }}{% endblocktrans %}
</a>
{% if model_title == "Stair" %}
<a class="btn btn-default btn-xs" accesskey="n" href="/editor/spaces/{{ space.id }}/staircase">
<i class="glyphicon glyphicon-plus"></i> {% blocktrans %}New staircase{% endblocktrans %}
</a>
{% endif %}
{% endif %}
{% if explicit_edit %}

View file

@ -4,7 +4,7 @@ from django.views.generic import TemplateView
from c3nav.editor.views.account import change_password_view, login_view, logout_view, register_view
from c3nav.editor.views.changes import changeset_detail, changeset_edit, changeset_redirect
from c3nav.editor.views.edit import edit, graph_edit, level_detail, list_objects, main_index, sourceimage, space_detail
from c3nav.editor.views.edit import edit, graph_edit, level_detail, list_objects, main_index, staircase_edit, sourceimage, space_detail
from c3nav.editor.views.overlays import overlays_list, overlay_features, overlay_feature_edit
from c3nav.editor.views.quest import QuestFormView
from c3nav.editor.views.users import user_detail, user_redirect
@ -33,7 +33,6 @@ def add_editor_urls(model_name, parent_model_name=None, with_list=True, explicit
])
return result
# todo: custom path converters
urlpatterns = [
path('levels/<int:pk>/', level_detail, name='editor.levels.detail'),
@ -91,3 +90,4 @@ urlpatterns.extend(add_editor_urls('LeaveDescription', 'Space'))
urlpatterns.extend(add_editor_urls('CrossDescription', 'Space'))
urlpatterns.extend(add_editor_urls('BeaconMeasurement', 'Space'))
urlpatterns.extend(add_editor_urls('RangingBeacon', 'Space'))
urlpatterns.append(path('spaces/<int:space>/staircase', edit, name='editor.stairs.staircase', kwargs={'model': apps.get_model('mapdata', 'Stair')}))

View file

@ -70,6 +70,12 @@ def main_index(request):
})
@etag(editor_etag_func)
@accesses_mapdata
@sidebar_view
def staircase_edit(request, space):
return render(request, "editor/create_staircase.html")
@etag(editor_etag_func)
@accesses_mapdata
@sidebar_view
@ -405,7 +411,10 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e
"access_restriction_select": True,
})
return render(request, 'editor/edit.html', ctx)
if request.path.endswith("staircase"):
return render(request, 'editor/create_staircase.html', ctx)
else:
return render(request, 'editor/edit.html', ctx)
def get_visible_spaces(request):