UNPKG

kmap

Version:

Karnagh Map Editor

418 lines (345 loc) 10.9 kB
import {Expression} from "../Logic/Expression"; import {QuineMcCluskeySolver} from "../Logic/QuineMcCluskeySolver"; import {Ajax} from "./Utility/Ajax" import {JsonAPI} from "./Utility/JsonAPI"; import '../Vendor/Blob.js'; import saveAs from '../Vendor/FileSaver.js'; import html2canvas from 'html2canvas'; /** * Practice page solution form * @param main Main object * @param element Parent element * @constructor */ export const Solution = function(main, element) { this.main = main; // The various section elements let div=null, expressionInput=null, result=null; const initialize = () => { // We only create the div is the user wants the solution // interface or is a solution is presented. if(main.options.solution || main.options.solved) { div = document.createElement('div'); div.classList.add('kmap-cl-solution'); element.append(div); // If a solution is presented, we don't need the interface if(main.options.solution && !main.options.solved) { addDiv(); } // Results div result = document.createElement('div'); div.appendChild(result); } } // Add the <div> to main const addDiv = () => { // Heading let h3 = document.createElement('h3'); h3.innerText = 'Solution'; div.appendChild(h3); let form = document.createElement('form'); div.appendChild(form); // // Enter expression // '<p><label>Enter Expression<br><input type="text" spellcheck="false" class="expression"></label></p>' // let p = document.createElement('p'); form.appendChild(p); let label = document.createElement('label'); p.appendChild(label); label.innerText = 'Enter Expression'; label.appendChild(document.createElement('br')); expressionInput = document.createElement('input'); expressionInput.setAttribute('type', 'text'); expressionInput.setAttribute('spellcheck', 'false'); label.appendChild(expressionInput); // // Check button // p = document.createElement('p'); form.appendChild(p); let check = document.createElement('button'); check.classList.add('check'); check.innerText = 'Check'; p.appendChild(check); check.addEventListener('click', (event) => { event.preventDefault(); this.check(); }); // // Clear button (input) // p.appendChild(document.createTextNode(' ')); let clear = document.createElement('a'); clear.classList.add('clear'); p.appendChild(clear); clear.addEventListener('click', (event) => { event.preventDefault(); this.clear(); }); // // Optional solve button // if(main.options.solve) { let solve = document.createElement('button'); solve.classList.add('kmap-cl-solve'); solve.innerHTML = 'Solve'; p.appendChild(solve); solve.addEventListener('click', (event) => { event.preventDefault(); this.solve(); }) } // // Optional To PNG button // if(main.options.toPNG) { const toPNG = document.createElement('button'); toPNG.classList.add('kmap-cl-topng'); toPNG.innerHTML = 'To PNG'; p.appendChild(toPNG); toPNG.addEventListener('click', (event) => { event.preventDefault(); this.toPNG(); }) } } /** * Clear the map */ this.clear = function() { if(expressionInput !== null) { expressionInput.value = ''; } if(result !== null) { result.innerHTML = ''; } } this.check = function() { var mt = ''; var first=true; for(let i in main.options.minterms) { if(first) { mt += ","; } else { first = false; } mt += main.options.minterms[i]; } result.innerHTML = ''; var expression = new Expression(); expression.labels = main.options.labels; expression.set_size(main.options.size); let str = expressionInput.value; if(main.options.expressionSel !== null) { const elements = document.querySelectorAll(main.options.expressionSel); for(let element of elements) { element.value = str.replace(/(<([^>]+)>)/ig,""); } } try { expression.parse(str); } catch(msg) { fail('<p>Your expression failed to parse and must not be ' + 'in the correct form.</p>' + '<p>' + msg + '</p>'); return; } // Is this result minimal? var qm = new QuineMcCluskeySolver(); qm.labels = main.options.labels; qm.init(main.options.size); for(var i in main.options.minterms) { qm.set(main.options.minterms[i], true); } for(i in main.options.dontcare) { qm.set(main.options.dontcare[i], undefined); } qm.compute(); var qmExpression = new Expression(); qmExpression.labels = main.options.labels; qmExpression.set_size(main.options.size); qmExpression.parse(qm.expression()); var valid = true; // // Test that every minterm supplied is either // a valid minterm or a don't care // for(var i in expression.minterms.minterms) { var m = expression.minterms.minterms[i]; var inlist = false; for(var j in main.options.minterms) { if(main.options.minterms[j] == m) { inlist = true; break; } } for(j in main.options.dontcare) { if(main.options.dontcare[j] == m) { inlist = true; break; } } if(!inlist) { // If we got to here, this term is extraneous! valid = false; break; } } // // Test that every minterm required is in the supplied list // for(i in main.options.minterms) { m = main.options.minterms[i]; inlist = false; for(var j in expression.minterms.minterms) { if(expression.minterms.minterms[j] == m) { inlist = true; break; } } if(!inlist) { // If we got to here, this term is extraneous! valid = false; break; } } if(!valid) { if(main.options.verbose) { var html = '<p>Your expression is not a valid solution for this ' + 'set of minterms. The minterms for your expression have been ' + 'computed as: </p>'; if(expression.minterms.minterms.length > 0) { html += '<p class="center small">' + expression.minterms.list() + '</p>'; } else { html += '<p class="center small">none</p>'; } } else { var html = '<p>Your expression is not a valid solution for this ' + 'set of minterms.</p>'; } fail(html); return; } if(!expression.as_good_as(qmExpression)) { if(main.options.verbose) { fail('<p>Your expression is not minimal. A minimal solution ' + 'for this problem is:</p>' + '<p class="small">' + qm.expression() + '</p>'); } else { fail('<p>Your expression is not minimal.</p>'); } return; } // // Success // result.innerHTML = '<p>Your expression is correct and minimal.</p>'; if(main.options.resultSel !== null) { const elements = document.querySelectorAll(main.options.resultSel); for(let element of elements) { element.value = main.options.success; } } if(main.options.testAPI !== null) { const config = { minterms: main.options.minterms, dontcares: main.options.dontcares, answer: expressionInput.value } let data = {cmd: "test", result: main.options.success, data: JSON.stringify(config), type: 'application/json' }; if(main.options.name !== null) { data['name'] = main.options.name; } if(main.options.appTag !== null) { data['appTag'] = main.options.appTag; } Ajax.do({ url: main.options.testAPI, data: data, method: "POST", dataType: 'json', contentType: main.options.contentType, success: (data) => { var json = new JsonAPI(data); result.innerHTML = '<p>Your expression is correct and minimal.<br><em>Test result successfully sent to server.</em></p>'; }, error: (xhr, status, error) => { // Failure // main.toast.message('Unable to communicate with server: ' + error); result.innerHTML = '<p>Your expression is correct and minimal.<br><em>Unable to communicate with server: ' + error + '</em></p>'; } }); } } this.solve = function() { const qm = this.minimumExpression(); if(result !== null) { result.innerHTML = '<p class="center">' + qm.expression() + "</p>"; } main.work.map.clear(); var groups = main.work.groups; groups.clear(); var terms = qm.expression().split('+'); var exp = new Expression(); exp.labels = main.options.labels; exp.set_size(main.options.size); for(var i=0; i<terms.length; i++) { var term = terms[i]; exp.parse(term); groups.add_group(exp.minterms.minterms); } groups.drawGroups(); } const fail = (msg) => { result.innerHTML = msg; if(main.options.resultSel !== null) { const elements = document.querySelectorAll(main.options.resultSel); for(let element of elements) { element.value = 'fail'; } } } this.minimumExpression = function() { // Is this result minimal? var qm = new QuineMcCluskeySolver(); qm.labels = this.main.options.labels; qm.init(this.main.options.size); for(var i in this.main.options.minterms) { qm.set(this.main.options.minterms[i], true); } for(i in this.main.options.dontcare) { qm.set(this.main.options.dontcare[i], undefined); } qm.compute(); return qm; } this.toPNG = function() { html2canvas(main.element).then(canvas => { const options = this.main.options; let filename = 'k' + options.size; if(main.minterms.dontcare.length > 0) { filename += 'x'; } filename += '-'; for(let term of main.minterms.minterms) { filename += new Number(term).toString(16); } if(main.minterms.dontcare.length > 0) { filename += '-'; for(let term of main.minterms.dontcare) { filename += new Number(term).toString(16); } } filename += '.png'; canvas.toBlob((blob) => { saveAs(blob, filename); }); }); } initialize(); }