1(function (root, factory) { 2 var api = factory(); 3 if (typeof module === "object" && module.exports) { 4 module.exports = api; 5 } else { 6 root.SarafanovoFabricCalculator = api; 7 } 8}(typeof self !== "undefined" ? self : this, function () { 9 "use strict"; 10 11 var EPSILON = 1e-9; 12 13 function number(value, name, min, max) { 14 var parsed = typeof value === "number" ? value : Number(String(value).replace(",", ".")); 15 if (!Number.isFinite(parsed) || parsed < min || parsed > max) { 16 throw new RangeError(name + ": ожидаеÑÑÑ ÑиÑло Ð¾Ñ " + min + " до " + max); 17 } 18 return parsed; 19 } 20 21 function integer(value, name, min, max) { 22 var parsed = number(value, name, min, max); 23 if (!Number.isInteger(parsed)) { 24 throw new RangeError(name + ": ожидаеÑÑÑ Ñелое ÑиÑло"); 25 } 26 return parsed; 27 } 28 29 function roundUp(value, step) { 30 if (!step) { 31 return value; 32 } 33 return Math.ceil((value - EPSILON) / step) * step; 34 } 35 36 function effectiveFabricWidth(input, min, max) { 37 var nominal = number(input.fabricWidthCm, "ÐоминалÑÐ½Ð°Ñ ÑиÑина Ñкани", min, max); 38 var excluded = number(input.excludedWidthCm == null ? 0 : input.excludedWidthCm, "ÐÑклÑÑÐ°ÐµÐ¼Ð°Ñ ÑиÑина кÑомок", 0, 40); 39 var widthShrinkage = number(input.widthShrinkagePct == null ? 0 : input.widthShrinkagePct, "УÑадка по ÑиÑине", 0, 30); 40 var usable = (nominal - excluded) * (1 - widthShrinkage / 100); 41 if (usable < min) { 42 throw new RangeError("ÐÐ¾Ð»ÐµÐ·Ð½Ð°Ñ ÑиÑина поÑле кÑомок и ÑÑадки менÑÑе " + min + " Ñм"); 43 } 44 return { 45 nominalCm: nominal, 46 excludedCm: excluded, 47 widthShrinkagePct: widthShrinkage, 48 usableCm: usable 49 }; 50 } 51 52 function normalizePiece(piece, index) { 53 var quantity = integer(piece.quantity == null ? 1 : piece.quantity, "ÐолиÑеÑÑво деÑали " + (index + 1), 1, 1000); 54 return { 55 name: String(piece.name || ("ÐеÑÐ°Ð»Ñ " + (index + 1))), 56 widthCm: number(piece.widthCm, "ШиÑина деÑали " + (index + 1), 0.1, 1000), 57 lengthCm: number(piece.lengthCm, "Ðлина деÑали " + (index + 1), 0.1, 5000), 58 quantity: quantity, 59 rotatable: piece.rotatable !== false 60 }; 61 } 62 63 function expandPieces(pieces) { 64 var expanded = []; 65 pieces.map(normalizePiece).forEach(function (piece) { 66 for (var i = 0; i < piece.quantity; i += 1) { 67 expanded.push({ 68 name: piece.name, 69 widthCm: piece.widthCm, 70 lengthCm: piece.lengthCm, 71 rotatable: piece.rotatable 72 }); 73 } 74 }); 75 return expanded.sort(function (a, b) { 76 return (b.widthCm * b.lengthCm) - (a.widthCm * a.lengthCm); 77 }); 78 } 79 80 function orientations(piece, directional) { 81 var result = [{ widthCm: piece.widthCm, lengthCm: piece.lengthCm, rotated: false }]; 82 if (!directional && piece.rotatable && Math.abs(piece.widthCm - piece.lengthCm) > EPSILON) { 83 result.push({ widthCm: piece.lengthCm, lengthCm: piece.widthCm, rotated: true }); 84 } 85 return result; 86 } 87 88 /** 89 * Deterministic shelf layout for rectangular bedding/table-linen pieces. 90 * It is an estimate, not a replacement for a production cutting marker. 91 */ 92 function packRectangles(pieces, fabricWidthCm, directional) { 93 var width = number(fabricWidthCm, "ШиÑина Ñкани", 30, 500); 94 var shelves = []; 95 var placements = []; 96 97 expandPieces(pieces).forEach(function (piece) { 98 var options = orientations(piece, Boolean(directional)); 99 var best = null; 100 101 options.forEach(function (orientation) { 102 if (orientation.widthCm > width + EPSILON) { 103 return; 104 } 105
106 shelves.forEach(function (shelf, shelfIndex) { 107 if (shelf.usedWidthCm + orientation.widthCm <= width + EPSILON) { 108 var addedLength = Math.max(0, orientation.lengthCm - shelf.lengthCm); 109 var candidate = { 110 addedLengthCm: addedLength, 111 unusedWidthCm: width - shelf.usedWidthCm - orientation.widthCm, 112 shelfIndex: shelfIndex, 113 orientation: orientation 114 }; 115 if (!best || candidate.addedLengthCm < best.addedLengthCm - EPSILON || 116 (Math.abs(candidate.addedLengthCm - best.addedLengthCm) <= EPSILON && candidate.unusedWidthCm < best.unusedWidthCm)) { 117 best = candidate; 118 } 119 } 120 }); 121 122 var newShelf = { 123 addedLengthCm: orientation.lengthCm, 124 unusedWidthCm: width - orientation.widthCm, 125 shelfIndex: shelves.length, 126 orientation: orientation 127 }; 128 if (!best || newShelf.addedLengthCm < best.addedLengthCm - EPSILON || 129 (Math.abs(newShelf.addedLengthCm - best.addedLengthCm) <= EPSILON && newShelf.unusedWidthCm < best.unusedWidthCm)) { 130 best = newShelf; 131 } 132 }); 133 134 if (!best) { 135 throw new RangeError("ÐеÑÐ°Ð»Ñ Â«" + piece.name + "» не помеÑаеÑÑÑ Ð² ÑиÑÐ¸Ð½Ñ Ñкани " + width + " Ñм"); 136 } 137 138 if (best.shelfIndex === shelves.length) { 139 shelves.push({ usedWidthCm: 0, lengthCm: 0, placements: [] }); 140 } 141 var shelf = shelves[best.shelfIndex]; 142 shelf.usedWidthCm += best.orientation.widthCm; 143 shelf.lengthCm = Math.max(shelf.lengthCm, best.orientation.lengthCm); 144 shelf.placements.push(piece.name); 145 placements.push({ 146 name: piece.name, 147 shelf: best.shelfIndex + 1, 148 widthCm: best.orientation.widthCm, 149 lengthCm: best.orientation.lengthCm, 150 rotated: best.orientation.rotated 151 }); 152 }); 153 154 var layout = { 155 fabricWidthCm: width, 156 layoutLengthCm: shelves.reduce(function (sum, shelf) { return sum + shelf.lengthCm; }, 0), 157 shelves: shelves, 158 placements: placements 159 }; 160 161 // A greedy heuristic can make a locally attractive rotation that worsens 162 // later shelves. Free rotation must never report more fabric than the same 163 // valid all-unrotated layout, so compare that deterministic fallback. 164 if (!directional) { 165 try { 166 var unrotatedLayout = packRectangles(pieces, width, true); 167 if (unrotatedLayout.layoutLengthCm < layout.layoutLengthCm - EPSILON) { 168 return unrotatedLayout; 169 } 170 } catch (error) { 171 // The unrotated fallback may be impossible even though a rotated 172 // placement fits. The already-built free layout remains valid. 173 if (!(error instanceof RangeError)) { 174 throw error; 175 } 176 } 177 } 178 179 return layout; 180 } 181 182 function finishLength(layoutLengthCm, options) { 183 var shrinkagePct = number(options.shrinkagePct == null ? 0 : options.shrinkagePct, "УÑадка", 0, 30); 184 var reservePct = number(options.reservePct == null ? 0 : options.reservePct, "ÐапаÑ", 0, 50); 185 var repeatCm = number(options.repeatCm == null ? 0 : options.repeatCm, "РаппоÑÑ", 0, 500); 186 var valueCm = layoutLengthCm; 187 188 // To retain the required cut length after shrinkage, divide by the retained fraction. 189 valueCm = valueCm / (1 - shrinkagePct / 100); 190 valueCm *= 1 + reservePct / 100; 191 var beforeRoundingCm = valueCm; 192 // A repeat is measured on the fabric as purchased, so it must be the final 193 // rounding increment after shrinkage and reserve. Otherwise those allowances 194 // destroy the promised repeat multiple. Without a repeat, use a practical 195 // 5 cm purchase increment. 196 var roundingIncrementCm = repeatCm > 0 ? repeatCm : 5; 197 valueCm = roundUp(valueCm, roundingIncrementCm); 198 199 return { 200 beforeAllowancesM: layoutLengthCm / 100,
201 afterAllowancesBeforeRoundingM: beforeRoundingCm / 100, 202 purchaseLengthM: valueCm / 100, 203 shrinkagePct: shrinkagePct, 204 reservePct: reservePct, 205 repeatCm: repeatCm, 206 roundingIncrementCm: roundingIncrementCm, 207 roundingBasis: repeatCm > 0 ? "pattern-repeat" : "purchase-increment" 208 }; 209 } 210 211 function beddingPieces(input) { 212 var seam = number(input.seamCm == null ? 1.5 : input.seamCm, "ÐÑипÑÑк на Ñов", 0, 10); 213 var hem = number(input.hemCm == null ? 2.5 : input.hemCm, "Ðодгибка", 0, 20); 214 var flap = number(input.pillowFlapCm == null ? 25 : input.pillowFlapCm, "Ðлапан наволоÑки", 0, 50); 215 var directional = Boolean(input.directional); 216 var pieces = []; 217 218 // Aggregate limits cover up to 50 UI sets while each editable set keeps 219 // its stricter 10/10/20 component limits in the form controller. 220 var duvetQty = integer(input.duvetQuantity == null ? 0 : input.duvetQuantity, "ÐолиÑеÑÑво пододеÑлÑников", 0, 500); 221 if (duvetQty) { 222 var duvetWidth = number(input.duvetWidthCm, "ШиÑина пододеÑлÑника", 40, 400); 223 var duvetLength = number(input.duvetLengthCm, "Ðлина пододеÑлÑника", 60, 400); 224 pieces.push({ 225 name: "ÐододеÑлÑник", 226 widthCm: duvetWidth + 2 * seam, 227 lengthCm: 2 * duvetLength + 2 * seam, 228 quantity: duvetQty, 229 rotatable: !directional 230 }); 231 } 232 233 var sheetQty = integer(input.sheetQuantity == null ? 0 : input.sheetQuantity, "ÐолиÑеÑÑво пÑоÑÑÑней", 0, 500); 234 if (sheetQty) { 235 var sheetWidth = number(input.sheetWidthCm, "ШиÑина пÑоÑÑÑни", 40, 500); 236 var sheetLength = number(input.sheetLengthCm, "Ðлина пÑоÑÑÑни", 60, 500); 237 var fitted = Boolean(input.fittedSheet); 238 if (fitted) { 239 var mattressHeight = number(input.mattressHeightCm, "ÐÑÑоÑа маÑÑаÑа", 2, 80); 240 var tuck = number(input.fittedTuckCm == null ? 15 : input.fittedTuckCm, "ÐÐ°Ð¿Ð°Ñ Ð¿Ð¾Ð´ маÑÑаÑ", 5, 40); 241 // `tuck` is the finished reserve under the mattress. Keep the edge 242 // finish/casing separate so it cannot silently consume that reserve. 243 sheetWidth += 2 * (mattressHeight + tuck + hem); 244 sheetLength += 2 * (mattressHeight + tuck + hem); 245 } else { 246 sheetWidth += 2 * hem; 247 sheetLength += 2 * hem; 248 } 249 pieces.push({ name: fitted ? "ÐÑоÑÑÑÐ½Ñ Ð½Ð° Ñезинке" : "ÐÑоÑÑÑнÑ", widthCm: sheetWidth, lengthCm: sheetLength, quantity: sheetQty, rotatable: !directional }); 250 } 251 252 var pillowQty = integer(input.pillowQuantity == null ? 0 : input.pillowQuantity, "ÐолиÑеÑÑво наволоÑек", 0, 1000); 253 if (pillowQty) { 254 var pillowWidth = number(input.pillowWidthCm, "ШиÑина наволоÑки", 20, 150); 255 var pillowLength = number(input.pillowLengthCm, "Ðлина наволоÑки", 20, 150); 256 pieces.push({ 257 name: "ÐаволоÑка", 258 widthCm: pillowWidth + 2 * seam, 259 lengthCm: 2 * pillowLength + flap + 2 * seam, 260 quantity: pillowQty, 261 rotatable: !directional 262 }); 263 } 264 265 if (!pieces.length) { 266 throw new RangeError("ÐобавÑÑе Ñ Ð¾ÑÑ Ð±Ñ Ð¾Ð´Ð½Ð¾ изделие"); 267 } 268 return pieces; 269 } 270 271 function calculateBedding(input) { 272 var pieces = beddingPieces(input); 273 var width = effectiveFabricWidth(input, 30, 500); 274 var packed = packRectangles(pieces, width.usableCm, input.directional); 275 var finished = finishLength(packed.layoutLengthCm, input); 276 return { 277 kind: "bedding", 278 pieces: pieces, 279 layout: packed, 280 width: width, 281 result: finished, 282 disclaimer: "ÐÑедваÑиÑелÑнÑй ÑаÑÑÑÑ Ð¿ÑÑмоÑголÑной ÑаÑкладки. ÐкÑÑгление обÑего меÑÑажа по ÑаппоÑÑÑ Ð½Ðµ гаÑанÑиÑÑÐµÑ ÑовмеÑение ÑиÑÑнка на каждой деÑали. ÐеÑед покÑпкой пÑовеÑÑÑе ÑÐµÑ Ð½Ð¾Ð»Ð¾Ð³Ð¸Ñ Ñвов, напÑавление ÑиÑÑнка и каÑÑÑ ÑаÑкÑоÑ." 283 }; 284 } 285 286 var GARMENT_DEFAULTS = { 287 straight_skirt: { label: "ÐÑÑÐ¼Ð°Ñ Ñбка", bodyLengthCm: 65, sleeveLengthCm: 0, easeCm: 4, extraCm: 15, pieceFactor: 1 }, 288 trousers: { label: "ÐÑÑки", bodyLengthCm: 110, sleeveLengthCm: 0, easeCm: 8, extraCm: 25, pieceFactor: 1.45 }, 289 blouse: { label: "ÐлÑзка / ÑÑбаÑка", bodyLengthCm: 70, sleeveLengthCm: 62, easeCm: 12, extraCm: 25, pieceFactor: 1 }, 290 straight_dress: { label: "ÐÑÑмое плаÑÑе", bodyLengthCm: 110, sleeveLengthCm: 62, easeCm: 10, extraCm: 30, pieceFactor: 1 }, 291 jacket: { label: "ÐакеÑ", bodyLengthCm: 75, sleeveLengthCm: 64, easeCm: 14, extraCm: 35, pieceFactor: 1.35 }, 292 tshirt: { label: "ФÑÑболка / лонгÑлив", bodyLengthCm: 70, sleeveLengthCm: 25, easeCm: 8, extraCm: 15, pieceFactor: 1 }, 293 sundress: { label: "ÐÑоÑÑой ÑаÑаÑан", bodyLengthCm: 110, sleeveLengthCm: 0, easeCm: 10, extraCm: 30, pieceFactor: 1 }, 294 robe: { label: "Ð¥Ð°Ð»Ð°Ñ Ð¿ÑоÑÑого кÑоÑ", bodyLengthCm: 115, sleeveLengthCm: 58, easeCm: 18, extraCm: 55, pieceFactor: 1.15 }, 295 nightgown: { label: "ÐоÑÐ½Ð°Ñ ÑоÑоÑка", bodyLengthCm: 105, sleeveLengthCm: 25, easeCm: 18, extraCm: 35, pieceFactor: 1 }, 296 child_dress: { label: "ÐеÑÑкое плаÑÑе", bodyLengthCm: 70, sleeveLengthCm: 22, easeCm: 10, extraCm: 25, pieceFactor: 1 } 297 }; 298 299 function calculateGarment(input) { 300 var preset = GARMENT_DEFAULTS[input.garment]; 301 if (!preset) { 302 throw new RangeError("ÐÑбеÑиÑе Ñип изделиÑ"); 303 } 304 var width = effectiveFabricWidth(input, 70, 320); 305 var fabricWidth = width.usableCm; 306 var circumference = number(input.circumferenceCm, "ÐонÑÑолÑнÑй Ð¾Ð±Ñ Ð²Ð°Ñ", 40, 250); 307 var bodyLength = number(input.bodyLengthCm == null ? preset.bodyLengthCm : input.bodyLengthCm, "Ðлина изделиÑ", 20, 250); 308 var sleeveLength = number(input.sleeveLengthCm == null ? preset.sleeveLengthCm : input.sleeveLengthCm, "Ðлина ÑÑкава", 0, 120); 309 var ease = number(input.easeCm == null ? preset.easeCm : input.easeCm, "ÐÑибавка на ÑвободÑ", 0, 50); 310 var seam = number(input.seamCm == null ? 1.5 : input.seamCm, "ÐÑипÑÑк на Ñов", 0, 10); 311 var halfPatternWidth = (circumference + ease) / 2 + 2 * seam; 312 var bodyRows = fabricWidth + EPSILON >= 2 * halfPatternWidth ? 1 : 2; 313 var sleeveRows = sleeveLength > 0 ? (fabricWidth + EPSILON >= circumference / 2 + ease + 4 * seam ? 1 : 2) : 0; 314 var baseCm = (bodyLength * bodyRows + sleeveLength * sleeveRows + preset.extraCm) * preset.pieceFactor; 315 if (input.directional) { 316 baseCm *= 1.08; 317 } 318 var finished = finishLength(baseCm, input); 319 var center = finished.purchaseLengthM; 320 return { 321 kind: "garment", 322 preset: preset.label, 323 width: width, 324 result: finished, 325 estimateMinM: Math.ceil(center * 0.9 * 20) / 20, 326 estimateMaxM: Math.ceil(center * 1.15 * 20) / 20, 327 bodyRows: bodyRows, 328 sleeveRows: sleeveRows, 329 disclaimer: "ÐÑиенÑиÑовоÑнÑй диапазон без конкÑеÑной вÑкÑойки. ÐÑо не гоÑÐ¾Ð²Ð°Ñ ÐºÐ°ÑÑа ÑаÑкÑоÑ: пеÑед покÑпкой ÑвеÑÑÑе меÑÑаж Ñ Ð¸Ð½ÑÑÑÑкÑией к вÑбÑанной вÑкÑойке. ÐаÑманÑ, воÑоÑник, подкладка, клеÑка, кÑÑпнÑй ÑаппоÑÑ Ð¸ ÑложнÑй ÑаÑон могÑÑ ÑвелиÑиÑÑ ÑаÑÑ Ð¾Ð´." 330 }; 331 } 332 333 function calculateArea(widthCm, lengthM) { 334 return number(widthCm, "ШиÑина Ñкани", 0.1, 1000) / 100 * number(lengthM, "Ðлина Ñкани", 0.01, 100000); 335 } 336 337 function calculateRollWeight(widthCm, lengthM, gsm) { 338 return calculateArea(widthCm, lengthM) * number(gsm, "ÐлоÑноÑÑÑ", 1, 5000) / 1000; 339 } 340 341 function calculateRollOuterVolume(diameterCm, widthCm) { 342 var diameterM = number(diameterCm, "ÐиамеÑÑ ÑÑлона", 1, 500) / 100; 343 var widthM = number(widthCm, "ШиÑина ÑÑлона", 1, 1000) / 100; 344 return Math.PI * diameterM * diameterM / 4 * widthM; 345 } 346 347 function calculateRollFabricVolume(diameterCm, widthCm, coreDiameterCm) { 348 var diameter = number(diameterCm, "ÐиамеÑÑ ÑÑлона", 1, 500); 349 var diameterM = diameter / 100; 350 var coreM = number(coreDiameterCm == null ? 0 : coreDiameterCm, "ÐиамеÑÑ Ð²ÑÑлки", 0, diameter) / 100; 351 var widthM = number(widthCm, "ШиÑина ÑÑлона", 1, 1000) / 100; 352 return Math.PI * (diameterM * diameterM - coreM * coreM) / 4 * widthM; 353 } 354 355 // Backward-compatible alias: historically this engine exposed the annular 356 // fabric-winding volume under the shorter name. 357 function calculateRollVolume(diameterCm, widthCm, coreDiameterCm) { 358 return calculateRollFabricVolume(diameterCm, widthCm, coreDiameterCm); 359 } 360 361 function calculateNapkins(input) { 362 var width = effectiveFabricWidth(input, 20, 500); 363 var fabricWidth = width.usableCm; 364 var napkinWidth = number(input.napkinWidthCm, "ШиÑина ÑалÑеÑки", 5, 300); 365 var napkinLength = number(input.napkinLengthCm, "Ðлина ÑалÑеÑки", 5, 300); 366 var hem = number(input.hemCm == null ? 2 : input.hemCm, "Ðодгибка", 0, 20); 367 var count = integer(input.quantity, "ÐолиÑеÑÑво ÑалÑеÑок", 1, 100000); 368 var cutW = napkinWidth + 2 * hem; 369 var cutL = napkinLength + 2 * hem; 370 var acrossA = Math.floor(fabricWidth / cutW); 371 var acrossB = input.directional ? 0 : Math.floor(fabricWidth / cutL); 372 var useRotated = acrossB > acrossA; 373 var across = useRotated ? acrossB : acrossA; 374 var rowLength = useRotated ? cutW : cutL; 375 if (across < 1) { 376 throw new RangeError("СалÑеÑка Ñ Ð¿Ð¾Ð´Ð³Ð¸Ð±ÐºÐ¾Ð¹ не помеÑаеÑÑÑ Ð² ÑиÑÐ¸Ð½Ñ Ñкани"); 377 } 378 var rows = Math.ceil(count / across); 379 var finished = finishLength(rows * rowLength, input); 380 return { across: across, rows: rows, rotated: useRotated, width: width, result: finished }; 381 } 382 383 var PROJECT_PRESETS = { 384 square_scarf: { label: "ÐоÑÑнка или квадÑаÑнÑй плаÑок", widthCm: 70, lengthCm: 70, count: 1, hemCm: 1, seamCm: 1, kind: "rectangle" }, 385 handkerchief: { label: "ÐоÑовой или ÑейнÑй плаÑок", widthCm: 40, lengthCm: 40, count: 3, hemCm: 0.7, seamCm: 1, kind: "rectangle" }, 386 curtain: { label: "ÐÑÐ³ÐºÐ°Ñ Ð·Ð°Ð½Ð°Ð²ÐµÑка", widthCm: 140, lengthCm: 170, count: 2, hemCm: 4, seamCm: 1, topAllowanceCm: 10, fullness: 1.5, kind: "curtain" }, 387 pillow_cover: { label: "ÐекоÑаÑÐ¸Ð²Ð½Ð°Ñ Ð½Ð°Ð²Ð¾Ð»Ð¾Ñка-конвеÑÑ", widthCm: 40, lengthCm: 40, count: 2, hemCm: 2, seamCm: 1.5, flapCm: 20, kind: "pillow" }, 388 storage_bag: { label: "ÐеÑоÑек Ð´Ð»Ñ Ñ ÑанениÑ", widthCm: 30, lengthCm: 40, count: 2, hemCm: 2, seamCm: 1.5, topAllowanceCm: 6, lining: false, kind: "bag" }, 389 organizer: { label: "ÐаÑÑеннÑй оÑганайзеÑ", widthCm: 45, lengthCm: 60, count: 1, hemCm: 2, seamCm: 1.5, pocketWidthCm: 18, pocketLengthCm: 18, pocketCount: 6, kind: "organizer" }, 390 patchwork: { label: "ÐоÑкÑÑное полоÑно из квадÑаÑов", widthCm: 60, lengthCm: 80, count: 1, hemCm: 0, seamCm: 0.7, module
390Cm: 10, kind: "patchwork" }, 391 tablecloth: { label: "СкаÑеÑÑÑ", widthCm: 140, lengthCm: 180, count: 1, hemCm: 3, seamCm: 1, kind: "rectangle" }, 392 table_runner: { label: "ÐоÑожка на ÑÑол", widthCm: 40, lengthCm: 150, count: 1, hemCm: 2, seamCm: 1, kind: "rectangle" }, 393 custom_rectangle: { label: "ÐÑÑмоÑголÑÐ½Ð°Ñ Ð´ÐµÑÐ°Ð»Ñ Ð¿Ð¾ Ñвоим ÑазмеÑам", widthCm: 50, lengthCm: 50, count: 1, hemCm: 2, seamCm: 1, kind: "rectangle" } 394 }; 395 396 function projectPieces(input) { 397 var preset = PROJECT_PRESETS[input.project]; 398 if (!preset) { 399 throw new RangeError("ÐÑбеÑиÑе пÑоекÑ"); 400 } 401 var width = number(input.widthCm == null ? preset.widthCm : input.widthCm, "ÐоÑÐ¾Ð²Ð°Ñ ÑиÑина", 3, 500); 402 var length = number(input.lengthCm == null ? preset.lengthCm : input.lengthCm, "ÐоÑÐ¾Ð²Ð°Ñ Ð´Ð»Ð¸Ð½Ð°", 3, 1000); 403 var count = integer(input.quantity == null ? preset.count : input.quantity, "ÐолиÑеÑÑво", 1, 1000); 404 var hem = number(input.hemCm == null ? preset.hemCm : input.hemCm, "Ðодгибка", 0, 30); 405 var seam = number(input.seamCm == null ? preset.seamCm : input.seamCm, "ÐÑипÑÑк на Ñов", 0, 10); 406 var directional = Boolean(input.directional); 407 var pieces = []; 408 var note = ""; 409 var metrics = {}; 410 411 if (preset.kind === "rectangle") { 412 pieces.push({ name: preset.label, widthCm: width + 2 * hem, lengthCm: length + 2 * hem, quantity: count, rotatable: !directional }); 413 note = "РаÑÑÑиÑÐ°Ð½Ñ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñе пÑÑмоÑголÑнÑе деÑали Ñ Ð¿Ð¾Ð´Ð³Ð¸Ð±ÐºÐ¾Ð¹ по ÑеÑÑÑÑм ÑÑоÑонам."; 414 } else if (preset.kind === "curtain") { 415 var fullness = number(input.fullness == null ? preset.fullness : input.fullness, "ÐоÑÑÑиÑÐ¸ÐµÐ½Ñ ÑбоÑки", 1, 4); 416 var top = number(input.topAllowanceCm == null ? preset.topAllowanceCm : input.topAllowanceCm, "ÐеÑÑ Ð½ÑÑ Ð¾Ð±ÑабоÑка", 0, 50); 417 var panelWidth = width * fullness / count; 418 var panelLength = length + top + hem; 419 var repeat = number(input.repeatCm == null ? 0 : input.repeatCm, "ÐеÑÑикалÑнÑй ÑаппоÑÑ", 0, 500); 420 if (repeat > 0) { 421 panelLength = roundUp(panelLength, repeat); 422 } 423 pieces.push({ name: "ÐолоÑно занавеÑки", widthCm: panelWidth + 2 * hem, lengthCm: panelLength, quantity: count, rotatable: false }); 424 metrics.panelWidthCm = panelWidth; 425 metrics.panelLengthCm = panelLength; 426 note = "ÐоÑÐ¾Ð²Ð°Ñ Ð¾Ð±ÑÐ°Ñ ÑиÑина Ñмножена на коÑÑÑиÑÐ¸ÐµÐ½Ñ ÑбоÑки и Ñазделена на ÑиÑло полоÑен. ÐеÑÑ Ð½ÑÑ Ð¸ нижнÑÑ Ð¾Ð±ÑабоÑки Ð·Ð°Ð´Ð°Ð½Ñ Ð¾ÑделÑно; веÑÑикалÑнÑй ÑаппоÑÑ Ð¿Ñи налиÑии окÑÑглÑн на каждом полоÑне."; 427 } else if (preset.kind === "pillow") { 428 var flap = number(input.flapCm == null ? preset.flapCm : input.flapCm, "Ðлапан", 0, 80); 429 pieces.push({ name: "ÐаволоÑка-конвеÑÑ", widthCm: width + 2 * seam, lengthCm: 2 * length + flap + 2 * seam, quantity: count, rotatable: !directional }); 430 note = "ÐÐ°Ð¶Ð´Ð°Ñ Ð½Ð°Ð²Ð¾Ð»Ð¾Ñка ÑаÑÑÑиÑана одной деÑалÑÑ Ñ ÐºÐ»Ð°Ð¿Ð°Ð½Ð¾Ð¼. ÐаÑÑÑжка, ÐºÐ°Ð½Ñ Ð¸ ÑовмеÑение ÑиÑÑнка ÑÑебÑÑÑ Ð¾ÑделÑной пÑовеÑки."; 431 } else if (preset.kind === "bag") { 432 var bagTop = number(input.topAllowanceCm == null ? preset.topAllowanceCm : input.topAllowanceCm, "ÐеÑÑ Ð½ÑÑ Ð¾Ð±ÑабоÑка", 0, 50); 433 var lining = input.lining == null ? Boolean(preset.lining) : Boolean(input.lining); 434 pieces.push({ name: "ÐÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð´ÐµÑÐ°Ð»Ñ Ð¼ÐµÑоÑка", widthCm: width + 2 * seam, lengthCm: length + bagTop + seam, quantity: count * 2, rotatable: !directional }); 435 if (lining) { 436 pieces.push({ name: "Ðодкладка меÑоÑка", widthCm: width + 2 * seam, lengthCm: length + bagTop + seam, quantity: count * 2, rotatable: !directional }); 437 } 438 note = "Ðа один меÑоÑек ÑÑÑÐµÐ½Ñ Ð´Ð²Ðµ оÑновнÑе деÑали" + (lining ? " и две деÑали подкладки" : " без подкладки") + ". ШнÑÑ Ð¸ кÑлиÑка ÑвеÑÑ Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð¹ веÑÑ Ð½ÐµÐ¹ обÑабоÑки не вклÑÑенÑ."; 439 } else if (preset.kind === "organizer") { 440 var pocketWidth = number(input.pocketWidthCm == null ? preset.pocketWidthCm : input.pocketWidthCm, "ШиÑина каÑмана", 3, 300); 441 var pocketLength = number(input.pocketLengthCm == null ? preset.pocketLengthCm : input.pocketLengthCm, "ÐÑÑоÑа каÑмана", 3, 300); 442 var pocketCount = integer(input.pocketCount == null ? preset.pocketCount : input.pocketCount, "ÐолиÑеÑÑво каÑманов", 1, 100); 443 pieces.push({ name: "ÐÑнова оÑганайзеÑа", widthCm: width + 2 * seam, lengthCm: length + 2 * seam, quantity: count, rotatable: !directional }); 444 pieces.push({ name: "ÐаÑман оÑганайзеÑа", widthCm: pocketWidth + 2 * seam, lengthCm: pocketLength + 2 * seam, quantity: count * pocketCount, rotatable: !directional }); 445 note = "УÑÑÐµÐ½Ñ Ð¾Ñнова и одинаковÑе накладнÑе каÑманÑ. Ðланки, подвеÑ, подкладка и ÑплоÑниÑÐµÐ»Ñ Ð² ÑаÑÑÑÑ Ð½Ðµ Ð²Ñ Ð¾Ð´ÑÑ."; 446 } else if (preset.kind === "patchwork") { 447 var module = number(input.moduleCm == null ? preset.moduleCm : input.moduleCm, "СÑоÑона гоÑового квадÑаÑа", 2, 100); 448 var columns = Math.ceil(width / module); 449 var rows = Math.ceil(length / module); 450 pieces.push({ name: "ÐоÑкÑÑнÑй квадÑаÑ", widthCm: module + 2 * seam, lengthCm: module + 2 * seam, quantity: count * columns * rows, rotatable: !directional }); 451 metrics.columns = columns; 452 metrics.rows = rows; 453 metrics.squareCount = count * columns * rows; 454 note = "РаÑÑÑиÑÐ°Ð½Ñ ÑолÑко квадÑаÑÑ Ð²ÐµÑÑ Ð° Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñм пÑипÑÑком. ÐоÑдÑÑÑ, подкладка, наполниÑелÑ, Ð¿Ð¾Ð´Ð±Ð¾Ñ Ð¸ ÑовмеÑение ÑиÑÑнка не вклÑÑенÑ."; 455 } 456
457 return { preset: preset, pieces: pieces, note: note, metrics: metrics }; 458 } 459 460 function calculateProject(input) { 461 var spec = projectPieces(input); 462 var width = effectiveFabricWidth(input, 20, 500); 463 var packed = packRectangles(spec.pieces, width.usableCm, input.directional); 464 var finished = finishLength(packed.layoutLengthCm, input); 465 return { 466 kind: "project", 467 preset: spec.preset.label, 468 pieces: spec.pieces, 469 layout: packed, 470 width: width, 471 metrics: spec.metrics, 472 result: finished, 473 disclaimer: spec.note + " ÐеÑед покÑпкой пÑовеÑÑÑе ÑÐµÑ Ð½Ð¾Ð»Ð¾Ð³Ð¸Ñ Ð¾Ð±ÑабоÑки и ÑделайÑе каÑÑÑ ÑаÑкÑоÑ." 474 }; 475 } 476 477 return { 478 beddingPieces: beddingPieces, 479 packRectangles: packRectangles, 480 calculateBedding: calculateBedding, 481 calculateGarment: calculateGarment, 482 calculateArea: calculateArea, 483 calculateRollWeight: calculateRollWeight, 484 calculateRollVolume: calculateRollVolume, 485 calculateRollOuterVolume: calculateRollOuterVolume, 486 calculateRollFabricVolume: calculateRollFabricVolume, 487 calculateNapkins: calculateNapkins, 488 calculateProject: calculateProject, 489 garmentPresets: GARMENT_DEFAULTS, 490 projectPresets: PROJECT_PRESETS, 491 effectiveFabricWidth: effectiveFabricWidth 492 }; 493}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.