diff --git a/quasar-app/design/label_icon.svg b/quasar-app/design/label_icon.svg new file mode 100644 index 0000000..0fe5d1d --- /dev/null +++ b/quasar-app/design/label_icon.svg @@ -0,0 +1,82 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/design/segment-boundary_icon.svg b/quasar-app/design/segment-boundary_icon.svg new file mode 100644 index 0000000..1287c5d --- /dev/null +++ b/quasar-app/design/segment-boundary_icon.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/design/signal_icon.svg b/quasar-app/design/signal_icon.svg new file mode 100644 index 0000000..b1abe20 --- /dev/null +++ b/quasar-app/design/signal_icon.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/design/switch_icon.svg b/quasar-app/design/switch_icon.svg new file mode 100644 index 0000000..0c54fe3 --- /dev/null +++ b/quasar-app/design/switch_icon.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/quasar-app/src/api/components.js b/quasar-app/src/api/components.js index 57268cb..b16d61f 100644 --- a/quasar-app/src/api/components.js +++ b/quasar-app/src/api/components.js @@ -5,17 +5,14 @@ export function refreshComponents(rs) { return new Promise((resolve, reject) => { axios.get(`${API_URL}/rs/${rs.id}/c`) .then(response => { - const previousSelectedComponentId = rs.selectedComponent ? rs.selectedComponent.id : null; + const previousSelectedComponentIds = rs.selectedComponents.map(c => c.id); + rs.selectedComponents.length = 0; rs.components = response.data; - if (previousSelectedComponentId !== null) { - const previousComponent = rs.components.find(c => c.id === previousSelectedComponentId); - if (previousComponent) { - rs.selectedComponent = previousComponent; - } else { - rs.selectedComponent = null; - } - } else { - rs.selectedComponent = null; + for (let i = 0; i < previousSelectedComponentIds.length; i++) { + const component = rs.components.find(c => c.id === previousSelectedComponentIds[i]); + if (component) { + rs.selectedComponents.push(component); + } } resolve(); }) @@ -42,7 +39,7 @@ export function refreshSomeComponents(rs, components) { export function getComponent(rs, id) { return new Promise((resolve, reject) => { - axios.get(`${this.apiUrl}/rs/${rs.id}/c/${id}`) + axios.get(`${API_URL}/rs/${rs.id}/c/${id}`) .then(response => resolve(response.data)) .catch(reject); }); @@ -78,7 +75,8 @@ export function createComponent(rs, data) { .then(() => { const newComponent = rs.components.find(c => c.id === newComponentId); if (newComponent) { - rs.selectedComponent = newComponent; + rs.selectedComponents.length = 0; + rs.selectedComponents.push(newComponent); } resolve(); }) diff --git a/quasar-app/src/api/linkTokens.js b/quasar-app/src/api/linkTokens.js index 38e601c..cf291ff 100644 --- a/quasar-app/src/api/linkTokens.js +++ b/quasar-app/src/api/linkTokens.js @@ -17,7 +17,7 @@ export class LinkToken { * @param {RailSystem} rs * @return {Promise} */ -export function getTokens(rs) { +export function getLinkTokens(rs) { return new Promise((resolve, reject) => { axios.get(`${API_URL}/rs/${rs.id}/lt`) .then(response => { @@ -54,4 +54,4 @@ export function deleteToken(rs, tokenId) { .then(resolve) .catch(reject); }); -} \ No newline at end of file +} diff --git a/quasar-app/src/api/railSystems.js b/quasar-app/src/api/railSystems.js index c9d9c1c..1d80a21 100644 --- a/quasar-app/src/api/railSystems.js +++ b/quasar-app/src/api/railSystems.js @@ -10,7 +10,7 @@ export class RailSystem { this.segments = []; this.components = []; this.websocket = null; - this.selectedComponent = null; + this.selectedComponents = []; } } diff --git a/quasar-app/src/assets/icons/label_icon.svg b/quasar-app/src/assets/icons/label_icon.svg new file mode 100644 index 0000000..0fe5d1d --- /dev/null +++ b/quasar-app/src/assets/icons/label_icon.svg @@ -0,0 +1,82 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/src/assets/icons/segment-boundary_icon.svg b/quasar-app/src/assets/icons/segment-boundary_icon.svg new file mode 100644 index 0000000..1287c5d --- /dev/null +++ b/quasar-app/src/assets/icons/segment-boundary_icon.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/src/assets/icons/signal_icon.svg b/quasar-app/src/assets/icons/signal_icon.svg new file mode 100644 index 0000000..b1abe20 --- /dev/null +++ b/quasar-app/src/assets/icons/signal_icon.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/quasar-app/src/assets/icons/switch_icon.svg b/quasar-app/src/assets/icons/switch_icon.svg new file mode 100644 index 0000000..0c54fe3 --- /dev/null +++ b/quasar-app/src/assets/icons/switch_icon.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/quasar-app/src/components/rs/AddComponentForm.vue b/quasar-app/src/components/rs/AddComponentForm.vue new file mode 100644 index 0000000..2f4e805 --- /dev/null +++ b/quasar-app/src/components/rs/AddComponentForm.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/quasar-app/src/components/rs/ComponentsView.vue b/quasar-app/src/components/rs/ComponentsView.vue deleted file mode 100644 index 435e06d..0000000 --- a/quasar-app/src/components/rs/ComponentsView.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - diff --git a/quasar-app/src/components/rs/MapView.vue b/quasar-app/src/components/rs/MapView.vue index dcbff73..121d426 100644 --- a/quasar-app/src/components/rs/MapView.vue +++ b/quasar-app/src/components/rs/MapView.vue @@ -5,27 +5,250 @@ Your browser doesn't support canvas. -
- -
- + +
+
+ +
+
+
+ + + + + + Add Signal + + + + Add Segment Boundary + + + + Add Switch + + + + Add Label + + + + + + + + + +
Add Signal
+

+ Add a signal to the rail system. +

+
+ + +
+ + + +
+
+ + + + + + + +
+
+
+ + + + + + +
Add Segment Boundary
+

+ Add a segment boundary to the rail system. A segment boundary is a + point where a train can cross between two segments, or "blocks" in + the system. For example, a train may move from a junction segment to + a main line. This boundary is a place where a detector component can + update the system as trains pass. +

+
+ + +
+ + + +
+
+ + + + + + + +
+
+
+ + + + + + + + + +
Add Label
+

+ Add a label to the rail system as a piece of text on the map. Labels + are purely a visual component, and do not interact with the system + in any way, besides being a helpful point of reference for users. +

+
+ + +
+ + + +
+
+ + + + + + + +
+
+
diff --git a/quasar-app/src/components/rs/SegmentListItem.vue b/quasar-app/src/components/rs/SegmentListItem.vue new file mode 100644 index 0000000..ac24153 --- /dev/null +++ b/quasar-app/src/components/rs/SegmentListItem.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/quasar-app/src/components/rs/SelectedComponentView.vue b/quasar-app/src/components/rs/SelectedComponentView.vue index 9c2a8db..bc85798 100644 --- a/quasar-app/src/components/rs/SelectedComponentView.vue +++ b/quasar-app/src/components/rs/SelectedComponentView.vue @@ -30,27 +30,18 @@ expand-separator > - - - Linked to segment: {{segment.name}} - Id: {{segment.id}} - - + - - - {{segment.name}} - Id: {{segment.id}} - - + @@ -120,6 +101,12 @@ + + + + + + @@ -127,25 +114,56 @@ + + diff --git a/quasar-app/src/pages/RailSystem.vue b/quasar-app/src/pages/RailSystem.vue index ed12a09..a752cbe 100644 --- a/quasar-app/src/pages/RailSystem.vue +++ b/quasar-app/src/pages/RailSystem.vue @@ -9,7 +9,6 @@ > - @@ -19,9 +18,6 @@ - - - @@ -36,12 +32,11 @@ import { useRailSystemsStore } from "stores/railSystemsStore"; import MapView from "components/rs/MapView.vue"; import SegmentsView from "components/rs/SegmentsView.vue"; -import ComponentsView from "components/rs/ComponentsView.vue"; import SettingsView from "components/rs/SettingsView.vue"; export default { name: "RailSystemPage", - components: { SettingsView, ComponentsView, SegmentsView, MapView }, + components: { SettingsView, SegmentsView, MapView }, data() { return { panel: "map", diff --git a/quasar-app/src/render/drawing.js b/quasar-app/src/render/drawing.js index 1ff5a04..043cbdc 100644 --- a/quasar-app/src/render/drawing.js +++ b/quasar-app/src/render/drawing.js @@ -19,6 +19,8 @@ export function drawComponent(ctx, worldTx, component) { drawSegmentBoundary(ctx, component); } else if (component.type === "SWITCH") { drawSwitch(ctx, component); + } else if (component.type === "LABEL") { + drawLabel(ctx, component); } ctx.setTransform(tx.translate(0.75, -0.75)); @@ -99,6 +101,15 @@ function drawSwitch(ctx, sw) { ctx.stroke(); } +function drawLabel(ctx, lbl) { + ctx.fillStyle = "black"; + circle(ctx, 0, 0, 0.1); + ctx.fill(); + ctx.strokeStyle = "black"; + ctx.font = "0.5px Sans-Serif"; + ctx.fillText(lbl.text, 0.1, -0.2); +} + function drawOnlineIndicator(ctx, component) { ctx.lineWidth = 0.1; if (component.online) { diff --git a/quasar-app/src/render/mapRenderer.js b/quasar-app/src/render/mapRenderer.js index 3dad275..57e666e 100644 --- a/quasar-app/src/render/mapRenderer.js +++ b/quasar-app/src/render/mapRenderer.js @@ -147,14 +147,11 @@ function getWorldTransform() { } export function isComponentHovered(component) { - for (let i = 0; i < hoveredElements.length; i++) { - if (hoveredElements[i].id === component.id) return true; - } - return false; + return hoveredElements.some(c => c.id === component.id); } export function isComponentSelected(component) { - return railSystem.selectedComponent !== null && railSystem.selectedComponent.id === component.id; + return railSystem.selectedComponents.some(c => c.id === component.id); } /** @@ -209,10 +206,11 @@ function onMouseUp() { mapTranslation.y += mapDragTranslation.y; finishedDrag = true; } - if (hoveredElements.length === 1) { - railSystem.selectedComponent = hoveredElements[0]; + if (hoveredElements.length > 0) { + railSystem.selectedComponents.length = 0; + railSystem.selectedComponents.push(...hoveredElements); } else if (!finishedDrag) { - railSystem.selectedComponent = null; + railSystem.selectedComponents.length = 0; } mapDragOrigin = null; mapDragTranslation = null; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java index 804f91e..9499075 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java @@ -3,7 +3,6 @@ package nl.andrewl.railsignalapi.rest.dto.component.in; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import nl.andrewl.railsignalapi.model.component.Label; import nl.andrewl.railsignalapi.model.component.Position; import javax.validation.constraints.NotBlank; @@ -15,7 +14,7 @@ import javax.validation.constraints.NotNull; @JsonSubTypes.Type(value = SignalPayload.class, name = "SIGNAL"), @JsonSubTypes.Type(value = SwitchPayload.class, name = "SWITCH"), @JsonSubTypes.Type(value = SegmentBoundaryPayload.class, name = "SEGMENT_BOUNDARY"), - @JsonSubTypes.Type(value = Label.class, name = "LABEL") + @JsonSubTypes.Type(value = LabelPayload.class, name = "LABEL") }) public abstract class ComponentPayload { @NotNull @NotBlank