Added gantt chart and canvas extensions
This commit is contained in:
parent
d6048a20be
commit
a51b73ee5d
187
canvas.js
187
canvas.js
|
@ -185,6 +185,126 @@ class Canvas {
|
|||
a._ctx.clearRect(0, 0, a._ctx.canvas.width, a._ctx.canvas.height);
|
||||
}
|
||||
|
||||
DrawArrowS(rectangle, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
if (a._ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opt = Object.assign({
|
||||
LineWidth: 1,
|
||||
LineDash: [],
|
||||
FillColour: null
|
||||
}, options);
|
||||
|
||||
// Adjust for pen discrepancy
|
||||
rectangle.X += 0.5;
|
||||
rectangle.Y += 0.5;
|
||||
rectangle.W -= opt.LineWidth;
|
||||
rectangle.H -= opt.LineWidth;
|
||||
|
||||
a._ctx.beginPath();
|
||||
a._ctx.strokeStyle = penColour;
|
||||
a._ctx.lineWidth = opt.LineWidth;
|
||||
a._ctx.setLineDash(opt.LineDash);
|
||||
|
||||
a._ctx.moveTo(rectangle.X, rectangle.Y);
|
||||
a._ctx.lineTo((rectangle.X + rectangle.W), rectangle.Y);
|
||||
a._ctx.lineTo((rectangle.X + Math.half(rectangle.W)), (rectangle.Y + rectangle.H));
|
||||
|
||||
a._ctx.closePath();
|
||||
|
||||
if (opt.FillColour != null) {
|
||||
a._ctx.fillStyle = opt.FillColour;
|
||||
a._ctx.fill();
|
||||
}
|
||||
|
||||
a._ctx.stroke();
|
||||
}
|
||||
|
||||
DrawCircle(x, y, width, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
if (a._ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opt = Object.assign({
|
||||
LineWidth: 1,
|
||||
LineDash: [],
|
||||
FillColour: null
|
||||
}, options);
|
||||
|
||||
let rectangle = {
|
||||
X: x + Math.half(width),
|
||||
Y: y + Math.half(width),
|
||||
W: Math.half(width),
|
||||
H: Math.half(width)
|
||||
};
|
||||
|
||||
// Adjust for pen discrepancy
|
||||
rectangle.X += 0.5;
|
||||
rectangle.Y += 0.5;
|
||||
rectangle.W -= Math.half(opt.LineWidth);
|
||||
rectangle.H -= Math.half(opt.LineWidth);
|
||||
|
||||
a._ctx.beginPath();
|
||||
a._ctx.strokeStyle = penColour;
|
||||
a._ctx.lineWidth = opt.LineWidth;
|
||||
a._ctx.setLineDash(opt.LineDash);
|
||||
|
||||
a._ctx.arc(rectangle.X, rectangle.Y, rectangle.W, 0, 2 * Math.PI, false);
|
||||
|
||||
a._ctx.closePath();
|
||||
|
||||
if (opt.FillColour != null) {
|
||||
a._ctx.fillStyle = opt.FillColour;
|
||||
a._ctx.fill();
|
||||
}
|
||||
|
||||
a._ctx.stroke();
|
||||
}
|
||||
|
||||
DrawDiamond(rectangle, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
if (a._ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opt = Object.assign({
|
||||
LineWidth: 1,
|
||||
LineDash: [],
|
||||
FillColour: null
|
||||
}, options);
|
||||
|
||||
// Adjust for pen discrepancy
|
||||
rectangle.X += 0.5;
|
||||
rectangle.Y += 0.5;
|
||||
rectangle.W -= opt.LineWidth;
|
||||
rectangle.H -= opt.LineWidth;
|
||||
|
||||
a._ctx.beginPath();
|
||||
a._ctx.strokeStyle = penColour;
|
||||
a._ctx.lineWidth = opt.LineWidth;
|
||||
a._ctx.setLineDash(opt.LineDash);
|
||||
|
||||
a._ctx.moveTo((rectangle.X + Math.half(rectangle.W)), rectangle.Y);
|
||||
a._ctx.lineTo((rectangle.X + rectangle.W), (rectangle.Y + Math.half(rectangle.H)));
|
||||
a._ctx.lineTo((rectangle.X + Math.half(rectangle.W)), (rectangle.Y + rectangle.H));
|
||||
a._ctx.lineTo(rectangle.X, (rectangle.Y + Math.half(rectangle.H)));
|
||||
|
||||
a._ctx.closePath();
|
||||
|
||||
if (opt.FillColour != null) {
|
||||
a._ctx.fillStyle = opt.FillColour;
|
||||
a._ctx.fill();
|
||||
}
|
||||
|
||||
a._ctx.stroke();
|
||||
}
|
||||
|
||||
DrawRectangle(rectangle, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
|
@ -215,21 +335,69 @@ class Canvas {
|
|||
a._ctx.fill();
|
||||
}
|
||||
|
||||
a._ctx.closePath();
|
||||
a._ctx.stroke();
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
// DrawLine(x1, y1, x2, y2, width, colour) {
|
||||
// const a = this;
|
||||
DrawLine(startX, startY, finishX, finishY, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
// a.CTX.beginPath();
|
||||
// a.CTX.moveTo(x1, y1);
|
||||
// a.CTX.lineTo(x2, (y2 - width));
|
||||
// a.CTX.lineWidth = width;
|
||||
// a.CTX.strokeStyle = colour;
|
||||
// a.CTX.stroke();
|
||||
// }
|
||||
if (a._ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opt = Object.assign({
|
||||
LineWidth: 1,
|
||||
LineDash: []
|
||||
}, options);
|
||||
|
||||
startY -= 0.5;
|
||||
// y -= penWidth;
|
||||
|
||||
a._ctx.beginPath();
|
||||
a._ctx.strokeStyle = penColour;
|
||||
a._ctx.lineWidth = opt.LineWidth;
|
||||
a._ctx.setLineDash(opt.LineDash);
|
||||
a._ctx.moveTo(startX, startY);
|
||||
a._ctx.lineTo(finishX, finishY);
|
||||
a._ctx.stroke();
|
||||
}
|
||||
|
||||
DrawLines(points, penColour, options) {
|
||||
const a = this;
|
||||
|
||||
if (a._ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (points == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (points.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opt = Object.assign({
|
||||
LineWidth: 1,
|
||||
LineDash: []
|
||||
}, options);
|
||||
|
||||
a._ctx.beginPath();
|
||||
a._ctx.strokeStyle = penColour;
|
||||
a._ctx.lineWidth = opt.LineWidth;
|
||||
a._ctx.setLineDash(opt.LineDash);
|
||||
|
||||
a._ctx.moveTo(points[0].X, points[0].Y);
|
||||
|
||||
for (let i=1; i<points.length; i++) {
|
||||
a._ctx.lineTo(points[i].X, points[i].Y);
|
||||
}
|
||||
|
||||
a._ctx.stroke();
|
||||
}
|
||||
|
||||
DrawHorizontalLine(x, y, width, penColour, options) {
|
||||
const a = this;
|
||||
|
@ -373,5 +541,4 @@ class Canvas {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -40,6 +40,12 @@ class RyzGanttChart extends Canvas {
|
|||
FillColour: "#555555"
|
||||
}
|
||||
},
|
||||
Line: {
|
||||
Margin: 5,
|
||||
Colour: "#555555",
|
||||
Width: 1,
|
||||
ArrowSize: 5
|
||||
},
|
||||
DateFont: "7pt sans-serif",
|
||||
DateForeColour: "#636363",
|
||||
BorderWidth: 1,
|
||||
|
@ -48,6 +54,11 @@ class RyzGanttChart extends Canvas {
|
|||
};
|
||||
}
|
||||
|
||||
get HeaderHeight() {
|
||||
const a = this;
|
||||
|
||||
return a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
||||
}
|
||||
|
||||
// Invalidate() {
|
||||
// const a = this;
|
||||
|
@ -70,8 +81,7 @@ class RyzGanttChart extends Canvas {
|
|||
|
||||
const tasks = project.ExportTasks();
|
||||
const width = ((project.Duration + 2) * a.Options.DayWidth);
|
||||
const headerHeight = (a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1]);
|
||||
const height = (tasks.length * a.Options.Row.Height) + headerHeight;
|
||||
const height = (tasks.length * a.Options.Row.Height) + a.HeaderHeight;
|
||||
|
||||
a.AutoSize = false;
|
||||
a.ClientWidth = width;
|
||||
|
@ -81,6 +91,7 @@ class RyzGanttChart extends Canvas {
|
|||
|
||||
a.#drawChartLabel(project);
|
||||
a.#drawTasks(tasks);
|
||||
a.#drawLines(tasks);
|
||||
}
|
||||
|
||||
#drawChartLabel(project) {
|
||||
|
@ -89,7 +100,6 @@ class RyzGanttChart extends Canvas {
|
|||
const width = a.ClientWidth;
|
||||
const height = a.ClientHeight;
|
||||
const displayDays = project.Duration + 2;
|
||||
const headerHeight = (a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1]);
|
||||
|
||||
let startDate = new Date(a.StartDate);
|
||||
startDate.addDays(-1);
|
||||
|
@ -100,7 +110,7 @@ class RyzGanttChart extends Canvas {
|
|||
}
|
||||
|
||||
a.DrawHorizontalLine(0, a.Options.HeaderRow.Height[0], width, a.Options.BorderColour);
|
||||
a.DrawHorizontalLine(0, (headerHeight - a.Options.BorderWidth), width, a.Options.BorderColour);
|
||||
a.DrawHorizontalLine(0, (a.HeaderHeight - a.Options.BorderWidth), width, a.Options.BorderColour);
|
||||
|
||||
// Write dates
|
||||
for (let i=0; i<displayDays; i++) {
|
||||
|
@ -135,9 +145,9 @@ class RyzGanttChart extends Canvas {
|
|||
|
||||
if (project.Project.StartOfWeek == date.getDay()) {
|
||||
// Draw start-of-the-week guideline
|
||||
a.DrawVerticalLine(x, headerHeight, guidelineHeight, a.Options.BorderColour, {});
|
||||
a.DrawVerticalLine(x, a.HeaderHeight, guidelineHeight, a.Options.BorderColour, {});
|
||||
} else {
|
||||
a.DrawVerticalLine(x, headerHeight, guidelineHeight, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
a.DrawVerticalLine(x, a.HeaderHeight, guidelineHeight, a.Options.BorderColour, { LineDash: a.Options.BorderDashPattern });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,30 +155,86 @@ class RyzGanttChart extends Canvas {
|
|||
#drawTasks(tasks) {
|
||||
const a = this;
|
||||
|
||||
const labelHeight = a.Options.HeaderRow.Height[0] + a.Options.HeaderRow.Height[1];
|
||||
|
||||
for (let i=0; i<tasks.length; i++) {
|
||||
if (tasks[i].IsCollated == true) {
|
||||
const rectangle = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, tasks[i].StartDate) + 1)),
|
||||
Y: (labelHeight + (a.Options.Row.Height * i) + a.Options.Row.CollatedTask.PaddingTop),
|
||||
W: (a.Options.DayWidth * tasks[i].Duration),
|
||||
H: a.Options.Row.CollatedTask.Height
|
||||
};
|
||||
const style = ((tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
||||
const rectangle = a.#getTaskRectangle(tasks[i], style);
|
||||
|
||||
a.DrawRectangle(rectangle, a.Options.Row.CollatedTask.BorderColour, { FillColour: a.Options.Row.CollatedTask.FillColour });
|
||||
if (tasks[i].Duration <= 0) {
|
||||
a.DrawDiamond(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
} else {
|
||||
const rectangle = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, tasks[i].StartDate) + 1)),
|
||||
Y: (labelHeight + (a.Options.Row.Height * i) + a.Options.Row.Task.PaddingTop),
|
||||
W: (a.Options.DayWidth * tasks[i].Duration),
|
||||
H: a.Options.Row.Task.Height
|
||||
};
|
||||
|
||||
a.DrawRectangle(rectangle, a.Options.Row.Task.BorderColour, { FillColour: a.Options.Row.Task.FillColour });
|
||||
a.DrawRectangle(rectangle, style.BorderColour, { FillColour: style.FillColour });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#drawLines(tasks) {
|
||||
const a = this;
|
||||
|
||||
for (let i=0; i<tasks.length; i++) {
|
||||
if (tasks[i].PredecessorTaskID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tasks[i].PredecessorTaskNo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const style = ((tasks[i].IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
||||
const rectangle = a.#getTaskRectangle(tasks[i], style);
|
||||
|
||||
const predecessorTask = tasks.first("Order", tasks[i].PredecessorTaskNo);
|
||||
if (predecessorTask == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const predecessorStyle = ((predecessorTask.IsCollated == true) ? a.Options.Row.CollatedTask : a.Options.Row.Task);
|
||||
const predecessorRectangle = a.#getTaskRectangle(predecessorTask, predecessorStyle);
|
||||
|
||||
let points = [];
|
||||
points.push({ X: (predecessorRectangle.X + predecessorRectangle.W), Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: (predecessorRectangle.Y + Math.half(predecessorRectangle.H)) });
|
||||
|
||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: (rectangle.Y - a.Options.Line.Margin) });
|
||||
points.push({ X: (rectangle.X + a.Options.Line.Margin), Y: rectangle.Y });
|
||||
|
||||
a.DrawLines(points, a.Options.Line.Colour, { LineWidth: a.Options.Line.Width });
|
||||
|
||||
const arrowRectangle = {
|
||||
X: (rectangle.X + a.Options.Line.Margin - Math.half(a.Options.Line.ArrowSize)),
|
||||
Y: (rectangle.Y - a.Options.Line.ArrowSize),
|
||||
W: a.Options.Line.ArrowSize,
|
||||
H: a.Options.Line.ArrowSize
|
||||
}
|
||||
|
||||
a.DrawArrowS(arrowRectangle, a.Options.Line.Colour, { FillColour: a.Options.Line.Colour });
|
||||
}
|
||||
}
|
||||
|
||||
#getTaskRectangle(task, style) {
|
||||
const a = this;
|
||||
const i = (task.Order - 1);
|
||||
|
||||
let rectangle = {};
|
||||
|
||||
if (task.Duration <= 0) {
|
||||
rectangle = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + style.PaddingTop),
|
||||
W: style.Height,
|
||||
H: style.Height
|
||||
};
|
||||
|
||||
rectangle.X -= Math.half(rectangle.W);
|
||||
} else {
|
||||
rectangle = {
|
||||
X: (a.Options.DayWidth * (Date.diffDays(a.StartDate, task.StartDate) + 1)),
|
||||
Y: (a.HeaderHeight + (a.Options.Row.Height * i) + style.PaddingTop),
|
||||
W: (a.Options.DayWidth * task.Duration),
|
||||
H: style.Height
|
||||
};
|
||||
}
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
}
|
|
@ -62,7 +62,7 @@ body {
|
|||
ID: 1,
|
||||
Name: "Task A",
|
||||
StartDelay: 0,
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: null,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: null
|
||||
|
@ -72,7 +72,7 @@ body {
|
|||
ID: 2,
|
||||
Name: "Task B",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: 1,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: null
|
||||
|
@ -82,7 +82,7 @@ body {
|
|||
ID: 5,
|
||||
Name: "Task B1",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: 2,
|
||||
IsCollated: true,
|
||||
CollatedTaskID: null
|
||||
|
@ -92,7 +92,7 @@ body {
|
|||
ID: 6,
|
||||
Name: "Task B11",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: null,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: 5
|
||||
|
@ -102,7 +102,7 @@ body {
|
|||
ID: 7,
|
||||
Name: "Task B12",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: null,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: 5,
|
||||
|
@ -113,7 +113,7 @@ body {
|
|||
ID: 8,
|
||||
Name: "Task E",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: null,
|
||||
IsCollated: true,
|
||||
CollatedTaskID: null
|
||||
|
@ -123,7 +123,7 @@ body {
|
|||
ID: 9,
|
||||
Name: "Task E1",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: null,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: 8
|
||||
|
@ -133,7 +133,7 @@ body {
|
|||
ID: 3,
|
||||
Name: "Task C",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: 8,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: null
|
||||
|
@ -143,7 +143,7 @@ body {
|
|||
ID: 4,
|
||||
Name: "Task D",
|
||||
StartDelay: Math.randomN(0, 5),
|
||||
Duration: Math.randomN(1, 30),
|
||||
Duration: Math.randomN(0, 28),
|
||||
PredecessorTaskID: 3,
|
||||
IsCollated: false,
|
||||
CollatedTaskID: null
|
||||
|
|
Loading…
Reference in New Issue