genetic-worker.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. function lerp(x, a, b) {
  2. return x * (b - a) + a;
  3. }
  4. function CalculateFitness(srcData, dstData) {
  5. let fitness = 0;
  6. const D1 = srcData.data;
  7. const D2 = dstData.data;
  8. for (let i = 0; i < D1.length; i+=4) {
  9. for (let j = 0; j < 3; j++) {
  10. const c1 = D1[i + j] / 255.0;
  11. const c2 = D2[i + j] / 255.0;
  12. fitness += (c1 - c2) ** 2;
  13. }
  14. }
  15. fitness /= (srcData.width * srcData.height * 3);
  16. fitness = Math.max(fitness, 0.001);
  17. return 1.0 / fitness;
  18. }
  19. function DrawTexture_ELLIPSE(genotype, dstWidth, dstHeight) {
  20. const canvas = new OffscreenCanvas(dstWidth, dstHeight);
  21. const ctx = canvas.getContext('2d');
  22. ctx.fillStyle = 'rgba(0, 0, 0, 1)';
  23. ctx.fillRect(0, 0, dstWidth, dstHeight);
  24. for (let gene of genotype) {
  25. const r = gene[0] * 255;
  26. const g = gene[1] * 255;
  27. const b = gene[2] * 255;
  28. const a = lerp(gene[3], 0.05, 0.25);
  29. const x1 = gene[4] * dstWidth;
  30. const y1 = gene[5] * dstHeight;
  31. const w = lerp(gene[6], 0.01, 0.25) * dstWidth;
  32. const h = lerp(gene[7], 0.01, 0.25) * dstHeight;
  33. ctx.beginPath();
  34. ctx.ellipse(x1, y1, w, h, 0, 0, 2 * Math.PI);
  35. ctx.closePath();
  36. ctx.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
  37. ctx.fill();
  38. }
  39. const data = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
  40. return data;
  41. }
  42. function DrawTexture_LINE(genotype, dstWidth, dstHeight) {
  43. const key = dstWidth + '-' + dstHeight;
  44. if (!(key in _CONTEXTS)) {
  45. const canvas = new OffscreenCanvas(dstWidth, dstHeight);
  46. _CONTEXTS[key] = canvas.getContext('2d');
  47. }
  48. const ctx = _CONTEXTS[key];
  49. ctx.fillStyle = 'rgba(0, 0, 0, 1)';
  50. ctx.fillRect(0, 0, dstWidth, dstHeight);
  51. for (let gene of genotype) {
  52. const r = gene[0] * 255;
  53. const g = gene[1] * 255;
  54. const b = gene[2] * 255;
  55. const a = lerp(gene[3], 0.05, 0.25);
  56. const lw = gene[4] * dstWidth * 0.25;
  57. const x1 = gene[5] * dstWidth;
  58. const y1 = gene[6] * dstHeight;
  59. const x2 = gene[7] * dstWidth;
  60. const y2 = gene[8] * dstHeight;
  61. ctx.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
  62. ctx.lineWidth = lw;
  63. ctx.beginPath();
  64. ctx.moveTo(x1, y1);
  65. ctx.lineTo(x2, y2);
  66. ctx.closePath();
  67. ctx.stroke();
  68. }
  69. const data = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
  70. return data;
  71. }
  72. function ProcessWorkItem(e) {
  73. const data = e.data;
  74. if (data.action == 'setup') {
  75. _FRAME_PARAMS = data;
  76. return {action: 'ready'};
  77. } else if (data.action == 'work') {
  78. const fitnesses = [];
  79. for (const workItem of data.work) {
  80. let resultData = null;
  81. if (data.type == 'line') {
  82. resultData = DrawTexture_LINE(
  83. workItem.genotype,
  84. _FRAME_PARAMS.srcData.width, _FRAME_PARAMS.srcData.height);
  85. } else if (data.type == 'ellipse') {
  86. resultData = DrawTexture_ELLIPSE(
  87. workItem.genotype,
  88. _FRAME_PARAMS.srcData.width, _FRAME_PARAMS.srcData.height);
  89. }
  90. fitnesses.push({
  91. fitness: CalculateFitness(_FRAME_PARAMS.srcData, resultData),
  92. index: workItem.index,
  93. });
  94. }
  95. return {
  96. action: 'work-complete',
  97. result: fitnesses
  98. };
  99. } else if (data.action == 'draw') {
  100. let resultData = null;
  101. if (data.type == 'line') {
  102. resultData = DrawTexture_LINE(data.genotype, data.width, data.height);
  103. } else if (data.type == 'ellipse') {
  104. resultData = DrawTexture_ELLIPSE(data.genotype, data.width, data.height);
  105. }
  106. return {
  107. action: 'work-complete',
  108. result: {imageData: resultData}
  109. };
  110. }
  111. }
  112. let _FRAME_PARAMS = null;
  113. const _CONTEXTS = {};
  114. onmessage = function(e) {
  115. postMessage(ProcessWorkItem(e));
  116. }