From 782b5f8914b0dbcf962356b73e3c0ecc44a9d6c1 Mon Sep 17 00:00:00 2001 From: "Georgy.Khatuncev" Date: Sat, 21 Sep 2024 00:32:07 +0500 Subject: [PATCH] Optimize test page --- CanvasCycleVDP/BlinkStatus.js | 110 +++++++++++ CanvasCycleVDP/Diagram.js | 295 ++++++++++++++++++++++++++++ CanvasCycleVDP/Diagram.min.js | 1 + CanvasCycleVDP/NumberColumn.js | 71 +++++++ CanvasCycleVDP/PercentColumn.js | 92 +++++++++ CanvasCycleVDP/ProcessColumn.js | 183 +++++++++++++++++ CanvasCycleVDP/Rectangle.js | 46 +++++ scripts/test.js | 337 ++++++++++++++++++++++++++++++++ styles/test.css | 10 + test.html | 41 ++++ 10 files changed, 1186 insertions(+) create mode 100644 CanvasCycleVDP/BlinkStatus.js create mode 100644 CanvasCycleVDP/Diagram.js create mode 100644 CanvasCycleVDP/Diagram.min.js create mode 100644 CanvasCycleVDP/NumberColumn.js create mode 100644 CanvasCycleVDP/PercentColumn.js create mode 100644 CanvasCycleVDP/ProcessColumn.js create mode 100644 CanvasCycleVDP/Rectangle.js create mode 100644 scripts/test.js create mode 100644 styles/test.css create mode 100644 test.html diff --git a/CanvasCycleVDP/BlinkStatus.js b/CanvasCycleVDP/BlinkStatus.js new file mode 100644 index 0000000..2a4b899 --- /dev/null +++ b/CanvasCycleVDP/BlinkStatus.js @@ -0,0 +1,110 @@ +function BlinkStatus(x, y, w, h) { + //Основной прямоугольник + var rAll = new Rectangle(x, y, w, h); + //Массив цветов статусов + var statColor = []; + //Текущий статус + var status = -1; + /*Проценты поднимаются снизу вверх (false) + *или слева направо (true)*/ + var rotate = false; + /*Функции для работы с координатами + * основного прямоугольника */ + this.X = function (val) { + if (val === undefined) return rAll().X(); + rAll().X(val); + }; + this.Y = function (val) { + if (val === undefined) return rAll().Y(); + rAll().Y(val); + }; + this.W = function (val) { + if (val === undefined) return rAll().W(); + rAll().W(val); + }; + this.H = function (val) { + if (val === undefined) return rAll().H(); + rAll().H(val); + }; + //Установка статуса от -1 до (кол-во элементов в массиве статусов - 1) + this.Status = function (val) { + if (val === undefined) return status; + var v = parseInt(val); + status = (isNaN(v) || v < -1) ? -1 : + v >= statColor.length ? (statColor.length - 1) : v; + }; + //Количество статусов + this.StatCount = function () { + return statColor.length; + }; + //Функция изменения начальной координаты и размеров прямоугольника + this.RectParam = function (_x, _y, _w, _h) { + rAll.RectParam(_x, _y, _w, _h); + }; + //Функция изменения прямоугольника по другому прямоугольнику + this.Rect = function (val) { + if (val === undefined) return rAll; + rAll.Rect(val); + }; + //Изменение направления отрисовки текста + this.Rotate = function (val) { + if (val === undefined) return rotate; + rotate = (typeof val === 'boolean') ? val : false; + }; + //Установка нового статуса + this.AddStatus = function (index, color) { + if (color === undefined) color = '#000'; + var c = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i); + c = (c === null) ? '#000000' : c[0]; + var idx = parseInt(index); + if (isNaN(idx) || idx < 0 || idx > statColor.length - 1) + statColor.push(c); + else + statColor.splice(idx, 0, c); + }; + //Изменение статуса + this.ChangeStatus = function (index, color) { + if (color === undefined) color = '#000'; + var c = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i); + c = (c === null) ? '#000000' : c[0]; + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < statColor.length) + statColor.splice(idx, 1, c); + }; + //Удаление статуса + this.DelStatus = function (index) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < statColor.length) + statColor.splice(idx, 1); + else if (statColor.length > 0) + statColor.pop(); + }; + //Заполнение мигалки по умолчанию + this.BuildDefault = function () { + var defaultColor = ['#00f', '#f00', '#0f0']; + for (var i = 0; i < defaultColor.length; i++) + this.AddStatus(0, defaultColor[i]); + }; + //Печать мигалки + this.Print = function (ctx) { + ctx.fillStyle = status >= 0 && status < statColor.length ? statColor[status] : '#ddd'; + ctx.fillRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + ctx.strokeStyle = '#000'; //'#333' + ctx.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + }; + //Печать текста мигалки + this.PrintText = function (ctx) { + ctx.save(); + ctx.translate(rAll.X() + rAll.W() / 2, rAll.Y() + rAll.H() / 2); + if (rotate) ctx.rotate(3 * Math.PI / 2); + ctx.fillStyle = '#888'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + if (rotate) + ctx.font = (rAll.W() - 2 < 0 ? 0 : rAll.W() - 2) + 'px Arial'; + else + ctx.font = (rAll.H() - 2 < 0 ? 0 : rAll.H() - 2) + 'px Arial'; + ctx.fillText((status + 1) + '/' + statColor.length, 0, 0); + ctx.restore(); + }; +} \ No newline at end of file diff --git a/CanvasCycleVDP/Diagram.js b/CanvasCycleVDP/Diagram.js new file mode 100644 index 0000000..d5e823f --- /dev/null +++ b/CanvasCycleVDP/Diagram.js @@ -0,0 +1,295 @@ +function Diagram(x, y, w, h) { +// \|/ \|/ // + // + var stI = 2; + // + var stE = 2; + // + var szE = 4; + // + var szB = 10; + // + var szN = 17; + // + var szP = 40; + // + var timeCycle = 1000; +// /|\ /|\ // + // + var rAll = new Rectangle(x, y, w, h); + // + var rProc = []; + // + var rBlink = []; + // + var rNumb = []; + // + var rDStart = []; + // + var rDEnd = []; + // + var cCycle = false; + // + var wCycle = -1; + /* (false) + * (true)*/ + var rotate = false; + // + this.minBoardW = function () { + return (stW + 1) * (rProc.length + 1) + (rProc.length * 5); + }; + // + this.minBoardH = function () { + return (stH + 1) * 4 + szHP + szHB + szHN; + }; + /* + * */ + this.X = function (val) { + if (val === undefined) return rAll().X(); + rAll().X(val); + }; + this.Y = function (val) { + if (val === undefined) return rAll().Y(); + rAll().Y(val); + }; + this.W = function (val) { + if (val === undefined) return rAll().W(); + rAll().W(val); + }; + this.H = function (val) { + if (val === undefined) return rAll().H(); + rAll().H(val); + }; + // + this.RectParam = function (_x, _y, _w, _h) { + rAll.RectParam(_x, _y, _w, _h); + }; + // + this.Rect = function (val) { + if (val === undefined) return rAll; + rAll.Rect(val); + }; + // + this.ProcCount = function () { + return rProc.length; + }; + // + this.AddProc = function (index, proc, blink, numb) { + var idx = parseInt(index); + idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx; + var date = new Date(); + if (idx == -1) { + rProc.push(proc); + rBlink.push(blink); + rNumb.push(numb); + rDStart.push(date); + rDEnd.push(date); + } else { + rProc.splice(idx, 0, proc); + rBlink.splice(idx, 0, blink); + rNumb.splice(idx, 0, numb); + rDStart.splice(idx, 0, date); + rDEnd.splice(idx, 0, date); + } + }; + // + this.ChangeProc = function (index, proc, blink, numb) { + var idx = parseInt(index); + idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx; + var date = new Date(); + if (idx == -1) return; + rProc.splice(idx, 1, proc); + rBlink.splice(idx, 1, blink); + rNumb.splice(idx, 1, numb); + rDStart.splice(idx, 1, date); + rDEnd.splice(idx, 1, date); + }; + // + this.DelStove = function (index) { + if (rProc.length == 0) return; + var idx = parseInt(index); + idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx; + if (idx == -1) { + rProc.pop(); + rBlink.pop(); + rNumb.pop(); + rDStart.pop(); + rDEnd.pop(); + } else { + rProc.splice(idx, 1); + rBlink.splice(idx, 1); + rNumb.splice(idx, 1); + rDStart.splice(idx, 1); + rDEnd.splice(idx, 1); + } + }; + // + this.StartDate = function (index, date) { + var idx = parseInt(index); + idx = (isNaN(idx) || idx < 0 || idx >= rDStart.length) ? -1 : idx; + if (idx == -1) return; + rDStart[idx] = date; + }; + // + this.EndDate = function (index, date) { + var idx = parseInt(index); + idx = (isNaN(idx) || idx < 0 || idx >= rDStart.length) ? -1 : idx; + if (idx == -1) return; + rDEnd[idx] = date; + }; + // + this.ChangeStatProc = function (index, stat, percent) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < rProc.length) { + rProc[idx].Status(stat); + rProc[idx].Percent(percent); + } + }; + // + this.ChangeStatBlink = function (index, stat) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= -1 && idx < rBlink.length) + rBlink[idx].Status(stat); + }; + // + this.ChangeStatNumb = function (index, prostoy) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < rNumb.length) + rNumb[idx].Prostoy(prostoy); + }; + // + this.Rotate = function (val) { + if (val === undefined) return rotate; + rotate = (typeof val === 'boolean') ? val : false; + for (var i = 0; i < rProc.length; i++) { + rProc[i].Rotate(rotate); + rNumb[i].Rotate(rotate); + rBlink[i].Rotate(rotate); + } + }; + // + this.BuildDefault = function () { + while (rProc.length < 48) { + rProc.push(new ProcessColumn(0, 0, 0, 0)); + rProc[rProc.length - 1].BuildDefault(); + rBlink.push(new BlinkStatus(0, 0, 0, 0)); + rBlink[rProc.length - 1].BuildDefault(); + rNumb.push(new NumberColumn(0, 0, 0, 0, rProc.length)); + var date = new Date; + rDStart.push(date); + rDEnd.push(date); + } + }; + // + this.Rebuild = function () { + var x = rAll.X() + stE + 1; + var y = rAll.Y() + stE + 1; + var wAll = rAll.W() - (rProc.length + 1) * (stE + 1); + var hAll = rAll.H() - (rProc.length + 1) * (stE + 1); + for (var i = 0; i < rProc.length; i++) { + var xP = 0, xB = 0, xN = 0; + var yP = 0, yB = 0, yN = 0; + var wP = 0, wB = 0, wN = 0; + var hP = 0, hB = 0, hN = 0; + if (!rotate) { + xP = x; xB = x; xN = x; + var w = Math.ceil(wAll / (rProc.length - i)); + w = (w < szE) ? szE : w; + wAll -= w; + wP = w; wB = w; wN = w; + hB = szB; hN = szN; + hP = rAll.H() - (stI + 1) * 4 - szB - szN; + hP = (hP < szP) ? szP : hP; + yP = y; + yB = yP + hP + (stI + 1); + yN = yB + szB + (stI + 1); + } else { + yP = y; yB = y; yN = y; + var h = Math.ceil(hAll / (rProc.length - i)); + h = (h < szE) ? szE : h; + hAll -= h; + hP = h; hB = h; hN = h; + wB = szB; wN = szN; + wP = rAll.W() - (stI + 1) * 4 - szB - szN; + wP = (wP < szP) ? szP : wP; + xP = x; + xB = xP + wP + (stI + 1); + xN = xB + szB + (stI + 1); + xN = x; + xB = xN + wN + (stI + 1); + xP = xB + szB + (stI + 1); + } + rProc[i].RectParam(xP, yP, wP, hP); + rBlink[i].RectParam(xB, yB, wB, hB); + rNumb[i].RectParam(xN, yN, wN, hN); + if (!rotate) + x += wP + stE + 1; + else + y += hP + stE + 1; + } + }; + // + this.Print = function (a) { + updateCycle(); + this.Rotate(rotate); + this.Rebuild(); + a.clearRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + a.fillStyle = "#ddd"; + a.fillRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + for (var i = 0; i < rProc.length; i++) { + rProc[i].Print(a); + rBlink[i].Print(a); + rNumb[i].Print(a); + } + a.strokeStyle = "#333"; + a.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + }; + // + this.PrintText = function (a) { + for (var i = 0; i < rProc.length; i++) { + rProc[i].PrintText(a); + rBlink[i].PrintText(a); + } + }; + // + function updateCycle(currDate) { + var date = (currDate == undefined) ? new Date() : currDate; + var cycle = false; + for (var i = 0; i < rProc.length; i++) { + if (rDStart[i] == rDEnd[i]) continue; + var d = rDEnd[i] - rDStart[i]; + var t = date - rDStart[i]; + d = 100 * (date - rDStart[i]) / d; + rProc[i].Percent(d); + rNumb[i].Prostoy(d >= 100); + cycle = cycle || d < 100; + } + cCycle = cCycle && cycle; + date.setMilliseconds(date.getMilliseconds() + timeCycle); + if (cCycle) + if (currDate == undefined) + wCycle = setTimeout(updateCycle, timeCycle); + else + wCycle = setTimeout(updateCycle, timeCycle, date); + }; + // + this.Cycle = function (start, currDate) { + if (start == undefined || !(typeof start === 'boolean')) return cCycle; + if (start) { + this.CheckDate(); + clearTimeout(wCycle); + cCycle = true; + updateCycle(currDate); + } + }; + // + this.CheckDate = function () { + for (var i = 0; i < rProc.length; i++) { + if (rDStart[i] > rDEnd[i]) { + var tmp = rDStart[i]; + rDStart[i] = rDEnd[i]; + rDEnd[i] = tmp; + } + } + }; +}; \ No newline at end of file diff --git a/CanvasCycleVDP/Diagram.min.js b/CanvasCycleVDP/Diagram.min.js new file mode 100644 index 0000000..592f3fa --- /dev/null +++ b/CanvasCycleVDP/Diagram.min.js @@ -0,0 +1 @@ +function Rectangle(n,i,e,r){function o(t){t=parseFloat(t);return isNaN(t)||t<0?0:t}this.X=function(t){if(void 0===t)return n;n=o(t)},this.Y=function(t){if(void 0===t)return i;i=o(t)},this.W=function(t){if(void 0===t)return e;e=o(t)},this.H=function(t){if(void 0===t)return r;r=o(t)},this.RectParam=function(t,n,i,e){this.X(t),this.Y(n),this.W(i),this.H(e)},this.Rect=function(t){if(void 0===t)return this;this.RectParam(t.X(),t.Y(),t.W(),t.H())},void 0===n?n=0:this.X(n),void 0===i?i=0:this.Y(i),void 0===e?e=0:this.W(e),void 0===r?r=0:this.H(r)}function PercentColumn(t,n,i,e){var r=new Rectangle(t,n,i,e),o=new Rectangle,s=!1,a=0,l="#000";this.X=function(t){if(void 0===t)return r.X();r.X(t)},this.Y=function(t){if(void 0===t)return r.Y();r.Y(t)},this.W=function(t){if(void 0===t)return r.W();r.W(t)},this.H=function(t){if(void 0===t)return r.H();r.H(t)},this.Percent=function(t){if(void 0===t)return a;t=parseFloat(t),t=isNaN(t)||t<0?0:100=l.length;)l.push(1);l[i]=isNaN(n)||n<=0?1:n}}function c(){for(var t=0,n=0;n=a.length?a.length-1:t},this.RectParam=function(t,n,i,e){s.RectParam(t,n,i,e)},this.Rect=function(t){if(void 0===t)return s;s.Rect(t)},this.Rotate=function(t){if(void 0===t)return f;f="boolean"==typeof t&&t;for(var n=0;na.length?(a.push(new PercentColumn),t=a.length-1):a.splice(t,0,new PercentColumn),a[t].Color(i),u(t,n)},this.ChangeRStat=function(t,n,i){t=parseInt(t);!isNaN(t)&&0<=t&&t=o.length?o.length-1:t},this.StatCount=function(){return o.length},this.RectParam=function(t,n,i,e){r.RectParam(t,n,i,e)},this.Rect=function(t){if(void 0===t)return r;r.Rect(t)},this.Rotate=function(t){if(void 0===t)return a;a="boolean"==typeof t&&t},this.AddStatus=function(t,n){void 0===n&&(n="#000");n=null===(n=n.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i))?"#000000":n[0],t=parseInt(t);isNaN(t)||t<0||t>o.length-1?o.push(n):o.splice(t,0,n)},this.ChangeStatus=function(t,n){void 0===n&&(n="#000");n=null===(n=n.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i))?"#000000":n[0],t=parseInt(t);!isNaN(t)&&0<=t&&t=P.length?-1:r,t=new Date;-1==r?(P.push(n),W.push(i),N.push(e),s.push(t),a.push(t)):(P.splice(r,0,n),W.splice(r,0,i),N.splice(r,0,e),s.splice(r,0,t),a.splice(r,0,t))},this.ChangeProc=function(t,n,i,e){var r=parseInt(t),r=isNaN(r)||r<0||r>=P.length?-1:r,t=new Date;-1!=r&&(P.splice(r,1,n),W.splice(r,1,i),N.splice(r,1,e),s.splice(r,1,t),a.splice(r,1,t))},this.DelStove=function(t){0!=P.length&&(t=parseInt(t),-1==(t=isNaN(t)||t<0||t>=P.length?-1:t)?(P.pop(),W.pop(),N.pop(),s.pop(),a.pop()):(P.splice(t,1),W.splice(t,1),N.splice(t,1),s.splice(t,1),a.splice(t,1)))},this.StartDate=function(t,n){t=parseInt(t);-1!=(t=isNaN(t)||t<0||t>=s.length?-1:t)&&(s[t]=n)},this.EndDate=function(t,n){t=parseInt(t);-1!=(t=isNaN(t)||t<0||t>=s.length?-1:t)&&(a[t]=n)},this.ChangeStatProc=function(t,n,i){t=parseInt(t);!isNaN(t)&&0<=t&&ta[n]&&(t=s[n],s[n]=a[n],a[n]=t)}} \ No newline at end of file diff --git a/CanvasCycleVDP/NumberColumn.js b/CanvasCycleVDP/NumberColumn.js new file mode 100644 index 0000000..8f71564 --- /dev/null +++ b/CanvasCycleVDP/NumberColumn.js @@ -0,0 +1,71 @@ +function NumberColumn(x, y, w, h, number) { + //Основной прямоугольник + var rAll = new Rectangle(x, y, w, h); + //Текущий статус по простою + var prostoy = true; + /*Проценты поднимаются снизу вверх (false) + *или слева направо (true)*/ + var rotate = false; + /*Функции для работы с координатами + * основного прямоугольника */ + this.X = function (val) { + if (val === undefined) return rAll().X(); + rAll().X(val); + }; + this.Y = function (val) { + if (val === undefined) return rAll().Y(); + rAll().Y(val); + }; + this.W = function (val) { + if (val === undefined) return rAll().W(); + rAll().W(val); + }; + this.H = function (val) { + if (val === undefined) return rAll().H(); + rAll().H(val); + }; + //Функция изменения начальной координаты и размеров прямоугольника + this.RectParam = function (_x, _y, _w, _h) { + rAll.RectParam(_x, _y, _w, _h); + }; + //Функция изменения прямоугольника по другому прямоугольнику + this.Rect = function (val) { + if (val === undefined) return rAll; + rAll.Rect(val); + }; + //Функция установки номера + this.Number = function (val) { + if (val === undefined) return number; + var v = parseInt(val); + number = (isNaN(v) || v < 0) ? 0 : v; + }; + this.Number(number); + //Функция установки статуса по простою + this.Prostoy = function (val) { + if (val === undefined) return prostoy; + prostoy = (typeof val === 'boolean') ? val : false; + }; + //Изменение направления отрисовки текста + this.Rotate = function (val) { + if (val === undefined) return rotate; + rotate = (typeof val === 'boolean') ? val : false; + }; + //Печать номера + this.Print = function (ctx) { + ctx.fillStyle = (!prostoy) ? '#ff0' : '#ff8000'; + ctx.fillRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + ctx.strokeStyle = '#000';//'#333' + ctx.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + ctx.save(); + ctx.translate(rAll.X() + rAll.W() / 2, rAll.Y() + rAll.H() / 2); + if (rotate) ctx.rotate(3 * Math.PI / 2); + ctx.fillStyle = '#000'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + var px = rAll.H() < rAll.W() ? rAll.H() - 3 : rAll.W() - 3; + px = px < 0 ? 0 : px; + ctx.font = px + 'px Arial'; + ctx.fillText(number, 0, 0); + ctx.restore(); + }; +} diff --git a/CanvasCycleVDP/PercentColumn.js b/CanvasCycleVDP/PercentColumn.js new file mode 100644 index 0000000..eec7bc1 --- /dev/null +++ b/CanvasCycleVDP/PercentColumn.js @@ -0,0 +1,92 @@ +function PercentColumn(x, y, w, h) { + //Основной прямоугольник + var rAll = new Rectangle(x, y, w, h); + //Прямоугольник процентов + var rPerc = new Rectangle(); + /*Проценты поднимаются снизу вверх (false) + *или слева направо (true)*/ + var rotate = false; + //Проценты статуса + var percent = 0; + //Цвет + var color = '#000'; + + /*Функции для работы с координатами + * основного прямоугольника */ + this.X = function (val) { + if (val === undefined) return rAll.X(); + rAll.X(val); + }; + this.Y = function (val) { + if (val === undefined) return rAll.Y(); + rAll.Y(val); + }; + this.W = function (val) { + if (val === undefined) return rAll.W(); + rAll.W(val); + }; + this.H = function (val) { + if (val === undefined) return rAll.H(); + rAll.H(val); + }; + //Установка процента от 0 до 100 + this.Percent = function (val) { + if (val === undefined) return percent; + var v = parseFloat(val); + v = (isNaN(v) || v < 0) ? 0 : (v > 100) ? 100 : v; + percent = v; + }; + //Установка цвета в формате #****** или #*** + this.Color = function (val) { + if (val === undefined) return color; + var c = val.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i); + color = (c === null) ? '#000000' : c[0]; + }; + //Функция изменения начальной координаты и размеров прямоугольника + this.RectParam = function (_x, _y, _w, _h) { + rAll.RectParam(_x, _y, _w, _h); + }; + //Функция изменения прямоугольника по другому прямоугольнику + this.Rect = function (val) { + if (val === undefined) return rAll; + rAll.Rect(val); + }; + //Изменение направления отрисовки процентов + this.Rotate = function (val) { + if (val === undefined) return rotate; + rotate = (typeof val === 'boolean') ? val : false; + }; + //Перерасчитываем элементы + function Rebuild() { + var size = ((!rotate) ? rAll.H() : rAll.W()) * percent / 100; + rPerc.RectParam( + rAll.X(), + (rotate) ? rAll.Y() : rAll.Y() + rAll.H() - size, + (!rotate) ? rAll.W() : size, + (rotate) ? rAll.H() : size + ); + } + //Рисуем проценты + this.Print = function (ctx) { + Rebuild(); + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.fillRect(rPerc.X(), rPerc.Y(), rPerc.W(), rPerc.H()); + ctx.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + }; + //Рисуем текст + this.PrintText = function (ctx) { + ctx.save(); + ctx.translate(rAll.X() + rAll.W() / 2, rAll.Y() + rAll.H() / 2); + if (!rotate) ctx.rotate(3 * Math.PI / 2); + ctx.fillStyle = '#888'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + if (!rotate) + ctx.font = (rAll.W() - 10 < 0 ? 0 : rAll.W() - 10) + 'px Arial'; + else + ctx.font = (rAll.H() - 10 < 0 ? 0 : rAll.H() - 10) + 'px Arial'; + ctx.fillText(Math.floor(percent * 100) / 100 + '%', 0, 0); + ctx.restore(); + }; +} diff --git a/CanvasCycleVDP/ProcessColumn.js b/CanvasCycleVDP/ProcessColumn.js new file mode 100644 index 0000000..601492e --- /dev/null +++ b/CanvasCycleVDP/ProcessColumn.js @@ -0,0 +1,183 @@ +function ProcessColumn(x, y, w, h) { + //Основной прямоугольник + var rAll = new Rectangle(x, y, w, h); + //Массив элементов под статусы + var rStat = []; + //Массив элементы под коэффициенты + var point = []; + //Проценты статуса + var percent = 0; + //Номер статуса + var status = -1; + /*Проценты поднимаются снизу вверх (false) + *или слева направо (true)*/ + var rotate = false; + /*Функции для работы с координатами + * основного прямоугольника */ + this.X = function (val) { + if (val === undefined) return rAll().X(); + rAll().X(val); + }; + this.Y = function (val) { + if (val === undefined) return rAll().Y(); + rAll().Y(val); + }; + this.W = function (val) { + if (val === undefined) return rAll().W(); + rAll().W(val); + }; + this.H = function (val) { + if (val === undefined) return rAll().H(); + rAll().H(val); + }; + //Установка процента от 0 до 100 + this.Percent = function (val) { + if (val === undefined) return percent; + var v = parseFloat(val); + percent = (isNaN(v) || v < 0) ? 0 : + (v > 100) ? 100 : v; + }; + //Установка статуса от 0 до (кол-во элементов в массиве статусов - 1) + this.Status = function (val) { + if (val === undefined) return status; + var v = parseInt(val); + status = (isNaN(v) || v < -1 || rStat.length === 0) ? -1 : + (v >= rStat.length) ? (rStat.length - 1) : v; + }; + //Функция изменения начальной координаты и размеров прямоугольника + this.RectParam = function (_x, _y, _w, _h) { + rAll.RectParam(_x, _y, _w, _h); + }; + //Функция изменения прямоугольника по другому прямоугольнику + this.Rect = function (val) { + if (val === undefined) return rAll; + rAll.Rect(val); + }; + //Изменение направления отрисовки процентов + this.Rotate = function (val) { + if (val === undefined) return rotate; + rotate = (typeof val === 'boolean') ? val : false; + for (var i = 0; i < rStat.length; i++) + rStat[i].Rotate(rotate); + }; + //Количество статусов + this.StatCount = function () { + return rStat.length; + }; + //Установка коеффициента + function Point(_index, _point) { + var idx = parseInt(_index); + var pnt = parseFloat(_point); + if (!isNaN(idx)) { + while (idx >= point.length) + point.push(1); + point[idx] = isNaN(pnt) || pnt <= 0 ? 1 : pnt; + } + } + //Установка нового статуса + this.AddRStat = function (index, point, color) { + var idx = parseInt(index); + if (isNaN(idx) || idx < 0 || idx - 1 > rStat.length) { + rStat.push(new PercentColumn()); + idx = rStat.length - 1; + } + else + rStat.splice(idx, 0, new PercentColumn()); + rStat[idx].Color(color); + Point(idx, point); + }; + //Изменение статуса + this.ChangeRStat = function (index, point, color) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < rStat.length) { + rStat[idx].Color(color); + Point(idx, point); + } + }; + //Удаление статуса + this.DelRStat = function (index) { + var idx = parseInt(index); + if (!isNaN(idx) && idx >= 0 && idx < rStat.length) { + rStat.splice(idx, 1); + point.splice(idx, 1); + } + else if (rStat.length > 0) { + rStat.pop(); + point.pop(); + } + }; + //Заполнение столбца процесса по умолчанию + this.BuildDefault = function () { + var defaultPoint = [57, 49, 49, 71, 28, 49, 21, 171, 114, 103]; + var defaultColor = ["#0ff", "#00f", "#f00", "#0f0", "#0ff", "#00f", "#8500b6", "#f00", "#f0f", "#0f0"]; + while (rStat.length > 0) + this.DelRStat(); + for (var i = 0; i < defaultPoint.length; i++) + this.AddRStat(i, defaultPoint[i], defaultColor[i]); + }; + //Сумма всех коэффициентов + function AllPoint() { + var count = 0; + for (var i = 0; i < point.length; i++) + count += point[i]; + return count; + } + // + function PointSize() { + var res = ((rotate) ? rAll.W() : rAll.H()) - (1 + rStat.length); + res = (res < 0) ? 0 : res; + return (AllPoint() <= 0) ? 0 : (res / AllPoint()); + } + //Перерасчет статусов и процентов + function Restat() { + for (var i = 0; i < rStat.length; i++) { + rStat[i].Percent(i < status ? 100 : i == status ? percent : 0); + } + } + //Перерасчет размеров статусов + function Rebuild() { + var _x = rAll.X() + 1; + var _y = rAll.Y() + 1; + + for (var i = 0; i < rStat.length; i++) { + var currStat = (rotate) ? i : rStat.length - (i + 1); + var _w = (!rotate) ? rAll.W() - 2 : point[currStat] * PointSize(); + var _h = (rotate) ? rAll.H() - 2 : point[currStat] * PointSize(); + rStat[currStat].RectParam(_x, _y, _w, _h); + _x = (!rotate) ? _x : _x + 1 + _w; + _y = (rotate) ? _y : _y + 1 + _h; + } + } + //Печать столбца процесса + this.Print = function (ctx) { + this.Rotate(rotate); + Restat(); + Rebuild(); + ctx.clearRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + ctx.fillStyle = '#ddd'; + ctx.fillRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + for (var i = 0; i < rStat.length; i++) + if (i <= status) + rStat[i].Print(ctx); + ctx.strokeStyle = '#000'; //'#333' + ctx.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H()); + }; + //Печать текста столбца процесса + this.PrintText = function (ctx) { + this.Rotate(rotate); + Restat(); + Rebuild(); + ctx.save(); + ctx.translate(rAll.X() + rAll.W() / 2, rAll.Y() + rAll.H() / 2); + if (!rotate) ctx.rotate(3 * Math.PI / 2); + ctx.fillStyle = '#888'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + if (!rotate) + ctx.font = (rAll.W() - 10 < 0 ? 0 : rAll.W() - 10) + 'px Arial'; + else + ctx.font = (rAll.H() - 10 < 0 ? 0 : rAll.H() - 10) + 'px Arial'; + ctx.fillText((Math.floor(percent * 100) / 100) + '% ' + (status + 1) + '/' + rStat.length, 0, 0); + ctx.restore(); + }; +} \ No newline at end of file diff --git a/CanvasCycleVDP/Rectangle.js b/CanvasCycleVDP/Rectangle.js new file mode 100644 index 0000000..c1795e5 --- /dev/null +++ b/CanvasCycleVDP/Rectangle.js @@ -0,0 +1,46 @@ +//Объявление класса прямоугольника +function Rectangle(x, y, w, h) { + /*Функции для работы с координатами + * основного прямоугольника */ + function chkVal(val) { + var a = parseFloat(val); + return (isNaN(a) || a < 0) ? 0 : a; + } + this.X = function (val) { + if (val === undefined) return x; + x = chkVal(val); + }; + this.Y = function (val) { + if (val === undefined) return y; + y = chkVal(val); + }; + this.W = function (val) { + if (val === undefined) return w; + w = chkVal(val); + }; + this.H = function (val) { + if (val === undefined) return h; + h = chkVal(val); + }; + //Функция изменения начальной координаты и размеров прямоугольника + this.RectParam = function (_x, _y, _w, _h) { + this.X(_x); + this.Y(_y); + this.W(_w); + this.H(_h); + }; + //Функция изменения прямоугольника по другому прямоугольнику + this.Rect = function (val) { + if (val === undefined) return this; + this.RectParam(val.X(), val.Y(), val.W(), val.H()); + }; + //Первая инициализация + if (x === undefined) x = 0; + else this.X(x); + if (y === undefined) y = 0; + else this.Y(y); + if (w === undefined) w = 0; + else this.W(w); + if (h === undefined) h = 0; + else this.H(h); +} \ No newline at end of file diff --git a/scripts/test.js b/scripts/test.js new file mode 100644 index 0000000..75c0e05 --- /dev/null +++ b/scripts/test.js @@ -0,0 +1,337 @@ +let canvas = document.getElementById("canvas"); +let ctx = canvas.getContext("2d"); + +//Функция изменения размера полотна +let menu = document.getElementById('menu'); +let resizeTimeout; +function Resize() { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(() => { + canvas.width = window.innerWidth - 20; + canvas.height = window.innerHeight - 30 - menu.clientHeight; + }, 100); +} +window.addEventListener("load", Resize, false); +window.addEventListener("resize", Resize, false); + +//Флаги для теста +let chkText = document.getElementById('PText'); +let chkData = document.getElementById('DefData'); +let chkRotate = document.getElementById('Rotate'); +let chkloop = document.getElementById('ChkLoop'); +//Глобальные параметры для теста +const columnHeightBase = 200; +const columnWidthBase = 40; +const rectHeightBase = 40; +const rectWidthBase = 70; + +const elementSpacing = 5; + +//Функция проверки столбцов процесса +let but1 = document.getElementById('TestPercent'); +but1.onclick = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + let height = columnHeightBase; + let width = columnWidthBase; + + if (chkRotate.checked){ + [height, width] = [width, height]; + } + + const maxColumnsX = Math.floor((canvas.width - 1) / (width + elementSpacing)) + const maxColumnsY = Math.floor((canvas.height - 1) / (height + elementSpacing)) + + for (let i = 0; i < maxColumnsX; i++) { + for (let j = 0; j < maxColumnsY; j++) { + const x = (i * (width + elementSpacing)) + 0.5; + const y = (j * (height + elementSpacing)) + 0.5; + + const t = new PercentColumn(x, y, width, height); + if (!chkData.checked){ + t.Color(getRColor()); + } + t.Percent(Math.random() * 101); + t.Rotate(chkRotate.checked); + t.Print(ctx); + if (chkText.checked){ + t.PrintText(ctx); + } + } + } +}; + +//Функция проверки столбцов статусов +let but2 = document.getElementById('TestProcess'); +but2.onclick = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + let height = columnHeightBase; + let width = columnWidthBase; + + if (chkData.checked){ + height *= 2; + } + + if (chkRotate.checked){ + [height, width] = [width, height]; + } + + const count = Math.floor(Math.random() * 6 + 2); + const arrPoint = Array.from({ length: count }, () => Math.random() * 5 + 1); + const arrColor = Array.from({ length: count }, () => getRColor()); + + const maxColumnsX = Math.floor((canvas.width - 1) / (width + elementSpacing)) + const maxColumnsY = Math.floor((canvas.height - 1) / (height + elementSpacing)) + + + for (let i = 0; i < maxColumnsX; i++) { + for (let j = 0; j < maxColumnsY; j++) { + const x = (i * (width + elementSpacing)) + 0.5; + const y = (j * (height + elementSpacing)) + 0.5; + + const t = new ProcessColumn(x, y, width, height); + t.Rotate(chkRotate.checked); + if (chkData.checked){ + t.BuildDefault(); + } else { + for (let k = 0; k < count; k++) { + t.AddRStat(k, arrPoint[k], arrColor[k]); + } + } + t.Status(Math.floor(Math.random() * (t.StatCount() + 1) - 1)); + t.Percent(Math.random() * 101); + t.Print(ctx); + if (chkText.checked) { + t.PrintText(ctx); + } + } + } +}; + +//Функция проверки мигалки +let but3 = document.getElementById('TestBlinkStatus'); +but3.onclick = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + let height = rectHeightBase; + let width = rectWidthBase; + + if (chkRotate.checked){ + [height, width] = [width, height]; + } + + const count = Math.floor(Math.random() * 5 + 1); + const arrColor = Array.from({ length: count }, () => getRColor()); + + const maxColumnsX = Math.floor((canvas.width - 1) / (width + elementSpacing)); + const maxColumnsY = Math.floor((canvas.height - 1) / (height + elementSpacing)); + + for (let i = 0; i < maxColumnsX; i++) { + for (let j = 0; j < maxColumnsY; j++) { + const x = (i * (width + elementSpacing)) + 0.5; + const y = (j * (height + elementSpacing)) + 0.5; + + const t = new BlinkStatus( x, y, width, height); + t.Rotate(chkRotate.checked); + if (chkData.checked) { + t.BuildDefault(); + } else { + for (let k = 0; k < count; k++) { + t.AddStatus(k, arrColor[k]); + } + } + t.Status(Math.floor(Math.random() * (t.StatCount() + 1) - 1)); + t.Print(ctx); + if (chkText.checked) { + t.PrintText(ctx); + } + } + } +}; + +//Функция проверки номера +let but4 = document.getElementById('TestNumberColumn'); +but4.onclick = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + let height = rectHeightBase; + let width = rectWidthBase; + + if (chkRotate.checked){ + [height, width] = [width, height]; + } + + const maxColumnsX = Math.floor((canvas.width - 1) / (width + elementSpacing)); + const maxColumnsY = Math.floor((canvas.height - 1) / (height + elementSpacing)); + + let num = 0; + for (var i = 0; i < maxColumnsX; i++) { + for (var j = 0; j < maxColumnsY; j++) { + const x = (i * (width + elementSpacing)) + 0.5; + const y = (j * (height + elementSpacing)) + 0.5; + const t = new NumberColumn(x, y, width, height, num++); + t.Rotate(chkRotate.checked); + t.Prostoy(Math.random() < 0.5); + t.Print(ctx); + } + } +}; + +//Переменные для диаграммы +let cycle; +let diagTest; +//Функция цикла +function cycle_print() { + diagTest.RectParam(0.5, 0.5, canvas.width - 1, canvas.height - 1) + diagTest.Print(ctx); + if (chkText.checked) { + diagTest.PrintText(ctx); + } + if (diagTest.Cycle()) { + cycle = setTimeout(cycle_print, 1000); + } +} +let but5 = document.getElementById('TestDiagramStove'); +but5.onclick = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + diagTest = new Diagram(0.5, 0.5, canvas.width - 1, canvas.height - 1); + + if (chkData.checked) { + diagTest.BuildDefault(); + for (let i = 0; i < diagTest.ProcCount(); i++) { + diagTest.ChangeStatProc(i, + Math.floor(Math.random() * 11), + Math.floor(Math.random() * 101)); + diagTest.ChangeStatBlink(i, Math.floor(Math.random() * 5 - 1)); + diagTest.ChangeStatNumb(i, Math.random() < 0.5); + } + } else { + const countStove = Math.floor(Math.random() * 20 + 30); + const countProc = Math.floor(Math.random() * 9 + 1); + const countBlink = Math.floor(Math.random() * 4 + 1); + + const colorProc = Array.from({ length: countProc }, () => getRColor()); + const pointProc = Array.from({ length: countProc }, () => Math.random() * 10); + const colorBlink = Array.from({ length: countBlink }, () => getRColor()); + + for (let i = 0; i < countStove; i++) { + const tP = new ProcessColumn(0, 0, 0, 0); + for (let k = 0; k < countProc; k++) { + tP.AddRStat(k, pointProc[k], colorProc[k]); + } + tP.Status(Math.floor(Math.random() * countProc)); + tP.Percent(Math.floor(Math.random() * 101)); + + const tB = new BlinkStatus(0, 0, 0, 0); + for (let k = 0; k < countBlink; k++) { + tB.AddStatus(k, colorBlink[k]); + } + tB.Status(Math.floor(Math.random() * (countBlink + 1) -1)); + + const tN = new NumberColumn(0, 0, 0, 0, i + 1); + tN.Prostoy(Math.random() < 0.5); + + diagTest.AddProc(i, tP, tB, tN); + } + } + diagTest.Rotate(chkRotate.checked); + + if (chkloop.checked) { + for (let i = 0; i < diagTest.ProcCount(); i++) { + const dStart = new Date(); + const dEnd = new Date(); + dStart.setSeconds(dStart.getSeconds() + Math.floor(Math.random() * 61 - 50)); + dEnd.setSeconds(dEnd.getSeconds() + Math.floor(Math.random() * 61 - 10)); + diagTest.StartDate(i, dStart); + diagTest.EndDate(i, dEnd); + } + diagTest.Cycle(true); + cycle_print(); + } else { + diagTest.Print(ctx); + if (chkText.checked) { + diagTest.PrintText(ctx); + } + } +}; + +let but6 = document.getElementById('TestPost'); +let btn6CycleSend; + +but6.onclick = function () { + sendCycleData(); +} + +function sendCycleData() { + const url = `${window.location.origin}/currcycles`; + fetch(url, { + method: 'POST', + }) + .then(response => { + if (!response.ok) { + throw new Error(`${response.status}: ${response.statusText}`); + } + return response.text(); + }) + .then(data => { + PrintDiagram(data); + handleLoop(); + }) + .catch(error => { + alert(error.message); + }); +}; + +function handleLoop() { + but6.disabled = chkloop.checked; + if (but6.disabled) { + btn6CycleSend = setTimeout(sendCycleData, 60000); + } +}; + +function PrintDiagram(pechstatus) { + const Pech = JSON.parse(pechstatus); + ctx.clearRect(0, 0, canvas.width, canvas.height); + cpnst t = new Diagram(0.5, 0.5, canvas.width - 1, canvas.height - 1); + t.BuildDefault(); + t.Rotate(chkRotate.checked); + + const cycleMapping = { + 0: { s: 0, b: -1 }, + 1: { s: 0, b: 1 }, + 2: { s: 1, b: 0 }, + 5: { s: 2, b: 0 }, + 6: { s: 3, b: 0 }, + 7: { s: 4, b: 0 }, + 8: { s: 5, b: 3 }, + 9: { s: 6, b: 3 }, + 10: { s: 7, b: 3 }, + 11: { s: 8, b: 3 }, + 12: { s: 9, b: 3 }, + 14: { s: 1, b: 1 }, + 15: { s: 2, b: 1 }, + 16: { s: 3, b: 1 }, + }; + + for(const key in Pech.data) { + const idx = Pech.data[key].vdp - 1; + const cycleInfo = cycleMapping[Pech.data[key].cycle] || { s: 0, b: -1 }; + + t.ChangeStatProc(idx, cycleInfo.s, 0); + t.ChangeStatBlink(idx, cycleInfo.b); + t.StartDate(idx, new Date(Pech.data[key].factStart)); + t.EndDate(idx, new Date(Pech.data[key].thinkEnd)); + } + + t.Print(ctx); + if (chkText.checked) { + t.PrintText(ctx); + } +}; + +function getRColor() { + const letters = '0123456789ABCDEF'; + const color = '#' + Array.from({ length: 6 }, () => letters[Math.floor(Math.random() * 16)]).join(''); + return color; +}; diff --git a/styles/test.css b/styles/test.css new file mode 100644 index 0000000..41bb70a --- /dev/null +++ b/styles/test.css @@ -0,0 +1,10 @@ + body { + margin: 0; + padding: 0; +} +#menu input { + margin: 10px; +} +#canvas{ + margin: 10px; +} diff --git a/test.html b/test.html new file mode 100644 index 0000000..4ba574c --- /dev/null +++ b/test.html @@ -0,0 +1,41 @@ + + + + + Canvas Cycle VDP + + + + + + + + + + + + + + + + +