algoritmo di posizionamento verticale gantt con ottimizzazione delle posizioni in riga.
This commit is contained in:
+6
-2
@@ -45,7 +45,11 @@ export default class GanttComponent extends Vue {
|
||||
return this.value.showDelay ? Math.max(this.value.estimatedDelay / 1000, this.ganttOptions.block_delay_minDuration) : 0;
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.value.showDelay = true;
|
||||
get statusDuration() {
|
||||
return this.showStatus ? this.ganttOptions.block_status_minDuration : 0;
|
||||
}
|
||||
|
||||
// mounted() {
|
||||
// this.value.showDelay = true;
|
||||
// }
|
||||
}
|
||||
+20
-2
@@ -2,7 +2,7 @@
|
||||
<svg
|
||||
class="gantt-block"
|
||||
:class="blockType"
|
||||
:width="ganttOptions.secondSize * (delayDuration + duration)"
|
||||
:width="ganttOptions.secondSize * (delayDuration + duration + statusDuration)"
|
||||
:height="ganttOptions.elementHeight"
|
||||
>
|
||||
<rect x="0" y="0" width="100%" height="70" class="body" />
|
||||
@@ -34,8 +34,26 @@
|
||||
<foreignObject :width="duration * ganttOptions.secondSize" height="26" x="0" y="44">
|
||||
<div class="body-footer">{{(value.estimatedDuration / 1000) | round(1)}}s</div>
|
||||
</foreignObject>
|
||||
<line
|
||||
v-if="showStatus"
|
||||
class="vertical-line"
|
||||
:x1="duration * ganttOptions.secondSize"
|
||||
:x2="duration * ganttOptions.secondSize"
|
||||
:y1="0"
|
||||
:y2="70"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
v-if="showStatus"
|
||||
:transform="`translate(${(delayDuration + duration) * ganttOptions.secondSize} 0)`"
|
||||
>
|
||||
<foreignObject :width="statusDuration * ganttOptions.secondSize" height="44" x="0" y="0">
|
||||
<div class="status-header">{{value.label | localize(value.label)}}</div>
|
||||
</foreignObject>
|
||||
<foreignObject :width="statusDuration * ganttOptions.secondSize" height="26" x="0" y="44">
|
||||
<div class="status-footer">{{(value.estimatedDuration / 1000) | round(1)}}s</div>
|
||||
</foreignObject>
|
||||
</g>
|
||||
<g v-if="showStatus" />
|
||||
</svg>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
||||
+43
-20
@@ -3,6 +3,7 @@ import Vue from "vue";
|
||||
import { Prop, InjectReactive } from "vue-property-decorator";
|
||||
import { IGanttOptions } from "./gantt";
|
||||
import block from "./gantt-component.vue";
|
||||
import { relativeTimeRounding } from "moment";
|
||||
|
||||
@Component({ components: { block } })
|
||||
export default class GanttRow extends Vue {
|
||||
@@ -26,19 +27,31 @@ export default class GanttRow extends Vue {
|
||||
return blockStartPosition(block, this.blocks, this.ganttOptions) * this.ganttOptions.secondSize;
|
||||
}
|
||||
|
||||
verticalPosition(block: server.Modblock) {
|
||||
|
||||
/*
|
||||
Questa funzione calcola la posizione verticale di ogni blocco seguendo l'ordine dei blocchi ed utilizza una variabile
|
||||
per memorizzare temporaneamente la posizione assegnata ad ogni blocco.
|
||||
*/
|
||||
verticalPosition(block: server.Modblock, rows: { id: number, from: number, to: number }[][] = [], recurring: boolean = false) {
|
||||
let idx = this.blocksInSection.indexOf(block);
|
||||
let myStartPosition = this.startPosition(block);
|
||||
|
||||
let result = this.blocksInSection.slice(0, idx).filter(p => {
|
||||
let sp = this.startPosition(p);
|
||||
let ep = sp + p.estimatedDuration / 1000;
|
||||
// calcolo le posizioni dei predecessori con una ricorsione solo se non sono già in una ricorsione..
|
||||
if (idx && !recurring)
|
||||
for (const b of this.blocksInSection.slice(0, idx)) this.verticalPosition(b, rows, true);
|
||||
|
||||
return sp <= myStartPosition && ep >= myStartPosition;
|
||||
}).length * (this.ganttOptions.elementHeight + this.ganttOptions.elementPadding * 2);
|
||||
// calcolo la posizione orizzontale del mio blocco.
|
||||
let myStartPosition = blockStartPosition(block, this.blocks, this.ganttOptions);
|
||||
let myEndPosition = myStartPosition + blockDuration(block, this.ganttOptions, true);
|
||||
|
||||
// cerco la prima delle righe disponibili che avrebbero posto per il mio blocco
|
||||
let myrow = rows.filter(r => r.filter(b => (b.from >= myStartPosition && b.from <= myEndPosition) || (b.to >= myStartPosition && b.to <= myEndPosition)).length == 0).shift();
|
||||
if (!myrow) rows.push(myrow = []);
|
||||
|
||||
return result;
|
||||
// inserisco il mio blocco per occupare la posizione
|
||||
myrow.push({ from: myStartPosition, to: myEndPosition, id: block.id });
|
||||
|
||||
// la posizione verticale è al posizione della riga nell'elenco delle righe.
|
||||
return rows.indexOf(myrow) * (this.ganttOptions.elementHeight + this.ganttOptions.elementPadding * 2);
|
||||
}
|
||||
|
||||
get rowHeight() {
|
||||
@@ -49,24 +62,34 @@ export default class GanttRow extends Vue {
|
||||
function blockEndPosition(block: server.Modblock, blocks: server.Modblock[], options: IGanttOptions) {
|
||||
let ids = block.precedingId.filter(i => i > 0);
|
||||
let duration = Math.max(0, ...blocks.filter(i => ids.indexOf(i.id) >= 0).map(i => blockEndPosition(i, blocks, options)));
|
||||
return duration + blockDuration(block, options, false);
|
||||
}
|
||||
|
||||
// check della durata del blocco se si vede il ritardo
|
||||
function blockDuration(block: server.Modblock, options: IGanttOptions, includeDelay: boolean) {
|
||||
let duration = 0;
|
||||
if (block.showDelay)
|
||||
duration += Math.max(block.estimatedDelay, options.block_delay_minDuration) / 1000;
|
||||
duration += Math.max(block.estimatedDelay / 1000, includeDelay ? options.block_delay_minDuration : 0);
|
||||
|
||||
// check della durata del blocco in base al tipo
|
||||
var minduration = 0;
|
||||
switch (block.type) {
|
||||
case 1: minduration = options.block_body_Heating_minDuration;
|
||||
case 2: minduration = options.block_body_Drawing_minDuration;
|
||||
case 3: minduration = options.block_body_Movement_minDuration;
|
||||
case 4: minduration = options.block_body_Vacuum_minDuration;
|
||||
case 5: minduration = options.block_body_Cooling_minDuration;
|
||||
case 6: minduration = options.block_body_Extraction_minDuration;
|
||||
}
|
||||
return duration += Math.max(minduration, block.estimatedDuration) / 1000;
|
||||
if (includeDelay)
|
||||
switch (block.type) {
|
||||
case 1: minduration = options.block_body_Heating_minDuration;
|
||||
case 2: minduration = options.block_body_Drawing_minDuration;
|
||||
case 3: minduration = options.block_body_Movement_minDuration;
|
||||
case 4: minduration = options.block_body_Vacuum_minDuration;
|
||||
case 5: minduration = options.block_body_Cooling_minDuration;
|
||||
case 6: minduration = options.block_body_Extraction_minDuration;
|
||||
}
|
||||
duration += Math.max(minduration, block.estimatedDuration / 1000);
|
||||
|
||||
// return duration + (block.estimatedDuration / 1000);
|
||||
|
||||
if (block.idParam > 0 && includeDelay)
|
||||
duration += options.block_status_minDuration;
|
||||
|
||||
if (includeDelay)
|
||||
duration += options.block_padding;
|
||||
return duration;
|
||||
}
|
||||
|
||||
function blockStartPosition(block: server.Modblock, blocks: server.Modblock[], options: IGanttOptions) {
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
.gantt-block .delay-header,
|
||||
.gantt-block .delay-footer,
|
||||
.gantt-block .body-footer,
|
||||
.gantt-block .body-header {
|
||||
.gantt-block .body-header,
|
||||
.gantt-block .status-footer,
|
||||
.gantt-block .status-header {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
.gantt-block .delay-header,
|
||||
.gantt-block .body-header {
|
||||
.gantt-block .body-header,
|
||||
.gantt-block .status-header {
|
||||
height: calc(100% - 10px);
|
||||
padding: 5px;
|
||||
align-items: flex-end;
|
||||
@@ -24,24 +27,29 @@
|
||||
color: #545454;
|
||||
}
|
||||
.gantt-block .delay-footer,
|
||||
.gantt-block .body-footer {
|
||||
.gantt-block .body-footer,
|
||||
.gantt-block .status-footer {
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: #fff;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.gantt-block .body-header {
|
||||
.gantt-block .body-header,
|
||||
.gantt-block .status-header {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.gantt-block .body-footer {
|
||||
.gantt-block .body-footer,
|
||||
.gantt-block .status-footer {
|
||||
justify-content: center;
|
||||
}
|
||||
.gantt-block .body {
|
||||
color: #fff;
|
||||
rx: 6;
|
||||
}
|
||||
.gantt-block .header {
|
||||
color: #545454;
|
||||
rx: 4;
|
||||
}
|
||||
.gantt-block.heating .body {
|
||||
fill: #913030;
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
.delay-header,
|
||||
.delay-footer,
|
||||
.body-footer,
|
||||
.body-header {
|
||||
.body-header,
|
||||
.status-footer,
|
||||
.status-header {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@@ -18,7 +20,8 @@
|
||||
}
|
||||
|
||||
.delay-header,
|
||||
.body-header {
|
||||
.body-header,
|
||||
.status-header {
|
||||
height: calc(~'100% - 10px');
|
||||
padding: 5px;
|
||||
align-items: flex-end;
|
||||
@@ -27,28 +30,33 @@
|
||||
}
|
||||
|
||||
.delay-footer,
|
||||
.body-footer {
|
||||
.body-footer,
|
||||
.status-footer {
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: #fff;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.body-header {
|
||||
.body-header,
|
||||
.status-header {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.body-footer {
|
||||
.body-footer,
|
||||
.status-footer {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
color: #fff;
|
||||
rx: 6;
|
||||
}
|
||||
|
||||
.header {
|
||||
color: #545454;
|
||||
rx: 4;
|
||||
}
|
||||
|
||||
&.heating {
|
||||
|
||||
@@ -14,6 +14,9 @@ export default class Gantt extends Vue {
|
||||
@Prop()
|
||||
blocks: server.Modblock[];
|
||||
|
||||
padX: number = 0;
|
||||
padY: number = 0;
|
||||
|
||||
@ProvideReactive()
|
||||
ganttOptions: IGanttOptions = {
|
||||
width: 2000,
|
||||
@@ -23,7 +26,7 @@ export default class Gantt extends Vue {
|
||||
stepDuration: 2,
|
||||
labelInterval: 10,
|
||||
maxGanttDuration: 500,
|
||||
block_status_minDuration: 1.5,
|
||||
block_status_minDuration: 4,
|
||||
block_delay_minDuration: 1.5,
|
||||
block_body_Heating_minDuration: 5,
|
||||
block_body_Drawing_minDuration: 5,
|
||||
@@ -31,6 +34,7 @@ export default class Gantt extends Vue {
|
||||
block_body_Vacuum_minDuration: 5,
|
||||
block_body_Cooling_minDuration: 5,
|
||||
block_body_Extraction_minDuration: 5,
|
||||
block_padding: 0.01
|
||||
};
|
||||
|
||||
currentDate: Date = null;
|
||||
@@ -44,9 +48,6 @@ export default class Gantt extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export interface IGanttElement {
|
||||
from: Date,
|
||||
to?: Date,
|
||||
@@ -70,4 +71,5 @@ export interface IGanttOptions {
|
||||
block_body_Vacuum_minDuration: number;
|
||||
block_body_Cooling_minDuration: number;
|
||||
block_body_Extraction_minDuration: number;
|
||||
block_padding: number;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<svg ref="mainContainer" preserveAspectRation="xMinYMax" width="100%" height="100%">
|
||||
<gantt-header />
|
||||
<gantt-header :padding-horizontal="padX" />
|
||||
<g :transform="`translate(0 ${ganttOptions.stepDuration * ganttOptions.secondSize})`">
|
||||
<gantt-row :section="1" :blocks="blocks" ref="section1">
|
||||
<gantt-row :section="1" :blocks="blocks" ref="section1" :padding-horizontal="padX">
|
||||
<span>{{`process-heating` | localize('Riscaldo')}}</span>
|
||||
</gantt-row>
|
||||
<gantt-row
|
||||
:padding-horizontal="padX"
|
||||
:section="2"
|
||||
:blocks="blocks"
|
||||
ref="section2"
|
||||
@@ -14,6 +15,7 @@
|
||||
<span>{{`process-forming` | localize('Formatura')}}</span>
|
||||
</gantt-row>
|
||||
<gantt-row
|
||||
:padding-horizontal="padX"
|
||||
:section="3"
|
||||
:blocks="blocks"
|
||||
:transform="`translate(0 ${getRowHeight($refs.section1) + getRowHeight($refs.section2) + ganttOptions.elementPadding*2})`"
|
||||
|
||||
Reference in New Issue
Block a user