(function() {
"use strict"
///////////////////// Function section
function smoothstep(x) {
return x * x * (3 - 2 * x)
}
function smootherstep(x) {
return x * x * x * (x * (x * 6 - 15) + 10)
}
function smootheststep(x) {
let y = -20 * Math.pow(x, 7)
y += 70 * Math.pow(x, 6)
y -= 84 * Math.pow(x, 5)
y += 35 * Math.pow(x, 4)
return y
}
function getCurrentTime() {
const now = new Date();
let hours = now.getHours();
let minutes = now.getMinutes();
let seconds = now.getSeconds();
hours = hours < 10 ? `0${hours}` : hours;
minutes = minutes < 10 ? `0${minutes}` : minutes;
seconds = seconds < 10 ? `0${seconds}` : seconds;
return `${hours}:${minutes}:${seconds}`;
}
function addLogMessage(message) {
const logContainer = document.getElementById('merge-log');
logContainer.innerHTML += `${getCurrentTime()} ${message}
`;
// Scroll to the bottom of the log
logContainer.scrollTop = logContainer.scrollHeight;
}
function addLogSeparator() {
const logContainer = document.getElementById('merge-log');
logContainer.innerHTML += '
'
logContainer.scrollTop = logContainer.scrollHeight;
}
function drawDiagram(fn) {
const canvas = document.getElementById('merge-canvas');
canvas.width=canvas.width
const ctx = canvas.getContext('2d');
// Draw coordinate system
ctx.scale(1, -1);
ctx.translate(0, -canvas.height);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.strokeStyle = 'white'
ctx.moveTo(0,0); ctx.lineTo(0,400); ctx.lineTo(400,400); ctx.lineTo(400,0); ctx.lineTo(0,0); ctx.lineTo(400,400);
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([1,2])
for (let i=40; i<400; i+=40) {
ctx.moveTo(0,i)
ctx.lineTo(400,i)
ctx.moveTo(i,0)
ctx.lineTo(i,400)
}
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([])
ctx.beginPath();
ctx.strokeStyle = 'black'
ctx.lineWidth = 3;
// Plot function
const numSamples = 20;
for (let i = 0; i <= numSamples; i++) {
const x = i / numSamples;
const y = fn(x);
const canvasX = x * 400;
const canvasY = y * 400;
if (i === 0) {
ctx.moveTo(canvasX, canvasY);
} else {
ctx.lineTo(canvasX, canvasY);
}
}
ctx.stroke()
// Plot alpha values (yellow boxes)
let start = parseFloat( document.querySelector('#merge-start').value )
let step = parseFloat( document.querySelector('#merge-step').value )
let iterations = document.querySelector('#merge-count').value>>0
ctx.beginPath()
ctx.fillStyle = "yellow"
for (let i=0; i< iterations; i++) {
const alpha = ( start + i * step ) / 100
const x = alpha*400
const y = fn(alpha) * 400
if (x <= 400) {
ctx.rect(x-3,y-3,6,6)
ctx.fill()
} else {
ctx.strokeStyle = 'red'
ctx.moveTo(0,0); ctx.lineTo(0,400); ctx.lineTo(400,400); ctx.lineTo(400,0); ctx.lineTo(0,0); ctx.lineTo(400,400);
ctx.stroke()
addLogMessage('Warning: maximum ratio is ≥ 100%')
}
}
}
function updateChart() {
let fn = (x) => x
switch (document.querySelector('#merge-interpolation').value) {
case 'SmoothStep':
fn = smoothstep
break
case 'SmootherStep':
fn = smootherstep
break
case 'SmoothestStep':
fn = smootheststep
break
}
drawDiagram(fn)
}
/////////////////////// Tab implementation
document.querySelector('#tab-container')?.insertAdjacentHTML('beforeend', `
Merge models
`)
document.querySelector('#tab-content-wrapper')?.insertAdjacentHTML('beforeend', `
`)
const tabMerge = document.querySelector('#tab-merge')
if (tabMerge) {
linkTabContents(tabMerge)
}
const merge = document.querySelector('#merge')
if (!merge) {
// merge tab not found, dont exec plugin code.
return
}
document.querySelector('body').insertAdjacentHTML('beforeend', `
`)
merge.innerHTML = `
`
/////////////////////// Event Listener
document.addEventListener('tabClick', (e) => {
if (e.detail.name == 'merge') {
console.log('Activate')
let modelList = stableDiffusionModelField.cloneNode(true)
modelList.id = "mergeModelA"
modelList.size = 8
document.querySelector("#mergeModelA").replaceWith(modelList)
modelList = stableDiffusionModelField.cloneNode(true)
modelList.id = "mergeModelB"
modelList.size = 8
document.querySelector("#mergeModelB").replaceWith(modelList)
updateChart()
}
})
document.querySelector('.merge-config').addEventListener('change', updateChart)
document.querySelector('#merge-button').addEventListener('click', async function(e) {
// Build request template
let model0 = document.querySelector('#mergeModelA').value
let model1 = document.querySelector('#mergeModelB').value
let request = { model0: model0, model1: model1 }
request['use_fp16'] = document.querySelector('#merge-fp').value == 'fp16'
let iterations = document.querySelector('#merge-count').value>>0
let start = parseFloat( document.querySelector('#merge-start').value )
let step = parseFloat( document.querySelector('#merge-step').value )
addLogMessage(`start = ${start}%`)
addLogMessage(`step = ${step}%`)
if (start + iterations * (step-1) >= 100) {
addLogMessage('Aborting: maximum ratio is ≥ 100%')
addLogMessage('Reduce the number of steps or the step size')
addLogSeparator()
document.querySelector('#merge-count').focus()
return
}
if (document.querySelector('#merge-filename').value == "") {
addLogMessage('Aborting: No output file name specified')
addLogSeparator()
document.querySelector('#merge-filename').focus()
return
}
// Disable merge button
e.target.disabled=true
e.target.classList.add('disabled')
let cursor = $("body").css("cursor");
let label = document.querySelector('#merge-button').innerHTML
$("body").css("cursor", "progress");
document.querySelector('#merge-button').innerHTML = 'Merging models ...'
addLogMessage("Merging models")
addLogMessage("Model A: "+model0)
addLogMessage("Model B: "+model1)
// Batch main loop
for (let i=0; iDone. The models have been saved to your models/stable-diffusion folder.")
addLogSeparator()
// Re-enable merge button
$("body").css("cursor", cursor);
document.querySelector('#merge-button').innerHTML = label
e.target.disabled=false
e.target.classList.remove('disabled')
// Update model list
stableDiffusionModelField.innerHTML = ''
await getModels()
})
})()