From 75815dc7810f75d64bd76f88fbbf5ebb657bab2e Mon Sep 17 00:00:00 2001 From: tritant Date: Sat, 2 Aug 2025 14:18:30 +0200 Subject: [PATCH] Add files via upload --- js/relight_node.js | 286 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 js/relight_node.js diff --git a/js/relight_node.js b/js/relight_node.js new file mode 100644 index 0000000..e2c0803 --- /dev/null +++ b/js/relight_node.js @@ -0,0 +1,286 @@ +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "AdvancedRelightNode.UI.FinalComplete", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData.name === "RelightNode") { + + const onExecuted = nodeType.prototype.onExecuted; + nodeType.prototype.onExecuted = function(message) { + onExecuted?.apply(this, arguments); + + if (message?.previews) { + const imageUrl = message.previews[0]; + + const img = new Image(); + img.src = imageUrl + `?t=${new Date().getTime()}`; + + img.onload = () => { + const anchorWidget = this.widgets.find(w => w.name === "ui_anchor"); + + this.previewImage = img; + + this.applyAnchorResize(); + const newSize = this.computeSize(); + this.size[1] = newSize[1]; + this.setDirtyCanvas(true, true); + delete anchorWidget.computeSize; + this.drawSlider(); + }; + + img.onerror = () => { + console.error(`[Relight Node] ERREUR: Impossible de charger l'image depuis l'URL directe: ${imageUrl}`); + }; + } + }; + + const onNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + onNodeCreated?.apply(this, arguments); + + const node = this; + + this.lights = [{}, {}]; + this.draggedLightIndex = null; + node._customFlags ??= {}; + if (node._customFlags.hasImageResizeRatio === undefined) + node._customFlags.hasImageResizeRatio = false; + node._onetimeFlags ??= {}; + if (node._onetimeFlags.haspassed === undefined) + node._onetimeFlags.haspassed = false; + node._onetimeFlags.counter = 0; + this.previewImage = null; + this.imageRect = { x: 0, y: 0, w: 0, h: 0 }; + + requestAnimationFrame(() => { + if (this.sliderInitialized) return; + this.sliderInitialized = true; + this.lights[0].xWidget = this.widgets.find((w) => w.name === "light_x_1"); + this.lights[0].yWidget = this.widgets.find((w) => w.name === "light_y_1"); + this.lights[0].typeWidget = this.widgets.find(w => w.name === "light_type_1"); + this.lights[0].pointSizeWidget = this.widgets.find(w => w.name === "point_size_1"); + this.lights[0].intensityWidget = this.widgets.find(w => w.name === "intensity_1"); + this.lights[0].neonWidgets = this.widgets.filter(w => ["neon_length_1", "neon_angle_1"].includes(w.name)); + const enableLight2Widget = this.widgets.find(w => w.name === "enable_light_2"); + this.lights[1].xWidget = this.widgets.find((w) => w.name === "light_x_2"); + this.lights[1].yWidget = this.widgets.find((w) => w.name === "light_y_2"); + this.lights[1].zWidget = this.widgets.find(w => w.name === "light_z_2"); + this.lights[1].typeWidget = this.widgets.find(w => w.name === "light_type_2"); + this.lights[1].pointSizeWidget = this.widgets.find(w => w.name === "point_size_2"); + this.lights[1].intensityWidget = this.widgets.find(w => w.name === "intensity_2"); + this.lights[1].neonWidgets = this.widgets.filter(w => ["neon_length_2", "neon_angle_2"].includes(w.name)); + const anchorWidget = this.widgets.find((w) => w.name === "ui_anchor"); + const maskEnabledWidget = this.widgets.find(w => w.name === "mask_enabled"); + const maskWidgets = this.widgets.filter(w => ["brush_intensity", "brush_softness", "mask_intensity_mult", "mask_gamma_mult"].includes(w.name)); + const spacer1 = this.widgets.find((w) => w.name === "spacer_1"); + const spacer2 = this.widgets.find((w) => w.name === "spacer_2"); + const lightX1 = this.widgets.find(w => w.name === "light_x_1"); + const lightY1 = this.widgets.find(w => w.name === "light_y_1"); + const lightX2 = this.widgets.find(w => w.name === "light_x_2"); + const lightY2 = this.widgets.find(w => w.name === "light_y_2"); + + if (!anchorWidget || !anchorWidget.inputEl) { return; } + + lightX1.hidden = true; + lightX1.computeSize = () => [0, -4]; // Supprime l'espace vertical + lightY1.hidden = true; + lightY1.computeSize = () => [0, -4]; // Supprime l'espace vertical + lightX2.hidden = true; + lightX2.computeSize = () => [0, -4]; // Supprime l'espace vertical + lightY2.hidden = true; + lightY2.computeSize = () => [0, -4]; // Supprime l'espace vertical + spacer1.hidden = true; + spacer2.hidden = true; + + anchorWidget.computeSize = (width) => [width, 256]; + + const sliderCanvas = document.createElement("canvas"); + sliderCanvas.style.width = "100%"; + sliderCanvas.style.height = "100%"; + sliderCanvas.style.cursor = "pointer"; + const container = anchorWidget.inputEl.parentElement; + container.replaceChild(sliderCanvas, anchorWidget.inputEl); + container.style.padding = "0"; + const ctx = sliderCanvas.getContext("2d"); + + const drawSlider = () => { + const rect = sliderCanvas.getBoundingClientRect(); + if (sliderCanvas.width !== rect.width || sliderCanvas.height !== rect.height) { + sliderCanvas.width = rect.width; + sliderCanvas.height = rect.height; + } + const width = sliderCanvas.width; + const height = sliderCanvas.height; + ctx.fillStyle = "rgba(0,0,0,1)"; + ctx.fillRect(0, 0, width, height); + if (node.previewImage && node.previewImage.complete) { + const imgRatio = node.previewImage.naturalWidth / node.previewImage.naturalHeight; + const canvasRatio = width / height; + let destWidth, destHeight, destX, destY; + if (imgRatio > canvasRatio) { + destWidth = width; destHeight = width / imgRatio; + destX = 0; destY = (height - destHeight) / 2; + } else { + destHeight = height; destWidth = height * imgRatio; + destY = 0; destX = (width - destWidth) / 2; + } + ctx.drawImage(node.previewImage, destX, destY, destWidth, destHeight); + node.imageRect = { x: destX, y: destY, w: destWidth, h: destHeight }; + } else { + node.imageRect = { x: 0, y: 0, w: width, h: height }; + } + node.lights.forEach((light, index) => { + if (index > 0 && !enableLight2Widget.value) return; + const lightCenterX = node.imageRect.x + (light.xWidget.value * node.imageRect.w); + const lightCenterY = node.imageRect.y + (light.yWidget.value * node.imageRect.h); + ctx.save(); + ctx.globalCompositeOperation = 'overlay'; + if (light.typeWidget.value === "Point") { + let radius; + + if (index === 0) { + radius = (light.pointSizeWidget.value * 100) + (light.intensityWidget.value * 3); + } else { + radius = (light.pointSizeWidget.value * 500) + (light.intensityWidget.value * 3); + } + + const gradient = ctx.createRadialGradient(lightCenterX, lightCenterY, 0, lightCenterX, lightCenterY, radius); + gradient.addColorStop(0, "rgba(255, 255, 240, 0.9)"); + gradient.addColorStop(1, "rgba(255, 255, 240, 0)"); + ctx.fillStyle = gradient; + ctx.beginPath(); + ctx.arc(lightCenterX, lightCenterY, radius, 0, 2 * Math.PI); + ctx.fill(); + } + ctx.restore(); + Object.assign(ctx, { fillStyle: "rgba(255,255,255,0.8)", strokeStyle: "black", lineWidth: 2 }); + ctx.beginPath(); + ctx.arc(lightCenterX, lightCenterY, 7, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + }); + }; + this.drawSlider = drawSlider; + + const applyAnchorResize = () => { + node.canvasHeight = sliderCanvas.getBoundingClientRect().height; + const nodeContentWidth = node.size[0] - 20; + if (node.previewImage) { + node.aspectRatio = node.previewImage.naturalHeight / node.previewImage.naturalWidth; + const newHeight = nodeContentWidth * node.aspectRatio + anchorWidget.computeSize = (w) => [w, newHeight]; + } else { + //if (!node._onetimeFlags.haspassed && (node._onetimeFlags.counter === 1 || node._onetimeFlags.counter === 2)) { + node._onetimeFlags.goodratio = 256 / node.canvasHeight; + node._onetimeFlags.haspassed = true; + //} + node._onetimeFlags.counter++; + const ratio = node._onetimeFlags.goodratio; + anchorWidget.computeSize = (width) => [width, node.canvasHeight * ratio]; + } + }; + this.applyAnchorResize = applyAnchorResize; + + const onDragMove = (event) => { + if (!isDragging || node.draggedLightIndex === null) return; + const rect = sliderCanvas.getBoundingClientRect(); + const mouseX = event.clientX - rect.left; + const mouseY = event.clientY - rect.top; + let valueX = (mouseX - node.imageRect.x) / node.imageRect.w; + let valueY = (mouseY - node.imageRect.y) / node.imageRect.h; + const activeLight = node.lights[node.draggedLightIndex]; + activeLight.xWidget.value = Math.max(0, Math.min(1, valueX)); + activeLight.yWidget.value = Math.max(0, Math.min(1, valueY)); + drawSlider(); + }; + let isDragging = false; + const onDragEnd = () => { + if (!isDragging) return; + isDragging = false; + if (node.draggedLightIndex !== null) { + const activeLight = node.lights[node.draggedLightIndex]; + activeLight.xWidget.setValue(activeLight.xWidget.value); + activeLight.yWidget.setValue(activeLight.yWidget.value); + node.draggedLightIndex = null; + } + window.removeEventListener("mousemove", onDragMove); + window.removeEventListener("mouseup", onDragEnd); + node.setDirtyCanvas(true); + }; + sliderCanvas.addEventListener("mousedown", (event) => { + const rect = sliderCanvas.getBoundingClientRect(); + const mouseX = event.clientX - rect.left; + const mouseY = event.clientY - rect.top; + let closestIndex = 0; + let minDistance = Infinity; + node.lights.forEach((light, index) => { + if (index > 0 && !enableLight2Widget.value) return; + const lightX = node.imageRect.x + light.xWidget.value * node.imageRect.w; + const lightY = node.imageRect.y + light.yWidget.value * node.imageRect.h; + const distance = Math.sqrt(Math.pow(mouseX - lightX, 2) + Math.pow(mouseY - lightY, 2)); + if (distance < minDistance) { + minDistance = distance; + closestIndex = index; + } + }); + node.draggedLightIndex = closestIndex; + isDragging = true; + window.addEventListener("mousemove", onDragMove); + window.addEventListener("mouseup", onDragEnd); + event.preventDefault(); + event.stopPropagation(); + onDragMove(event); + }); + new ResizeObserver(drawSlider).observe(container); + const setWidgetVisibility = (widget, isHidden) => { + if (!widget) return; + widget.hidden = isHidden; + if (isHidden) { + widget.computeSize = () => [0, -4]; + } else { + delete widget.computeSize; + } + }; + const toggleLightWidgets = (light, shouldHide) => { + if (!light.typeWidget) return; + const isNeon = light.typeWidget.value === "Neon"; + light.neonWidgets.forEach(w => setWidgetVisibility(w, shouldHide || !isNeon)); + setWidgetVisibility(light.pointSizeWidget, shouldHide || isNeon); + //setWidgetVisibility(light.xWidget, shouldHide); + //setWidgetVisibility(light.yWidget, shouldHide); + setWidgetVisibility(light.zWidget, shouldHide); + setWidgetVisibility(light.typeWidget, shouldHide); + setWidgetVisibility(light.intensityWidget, shouldHide); + }; + const toggleMaskWidgets = () => { + const isEnabled = maskEnabledWidget.value; + maskWidgets.forEach(w => setWidgetVisibility(w, !isEnabled)); + applyAnchorResize(); + const newSize = node.computeSize(); + node.size[1] = newSize[1]; + node.setDirtyCanvas(true, true); + delete anchorWidget.computeSize; + }; + const updateAllWidgetsVisibility = () => { + toggleLightWidgets(node.lights[0], false); + toggleLightWidgets(node.lights[1], !enableLight2Widget.value); + applyAnchorResize(); + const newSize = node.computeSize(); + node.size[1] = newSize[1]; + node.setDirtyCanvas(true, true); + delete anchorWidget.computeSize; + }; + enableLight2Widget.callback = () => { updateAllWidgetsVisibility(); drawSlider(); }; + if(maskEnabledWidget) maskEnabledWidget.callback = toggleMaskWidgets; + this.lights[0].typeWidget.callback = updateAllWidgetsVisibility; + this.lights[1].typeWidget.callback = updateAllWidgetsVisibility; + setTimeout(() => { + updateAllWidgetsVisibility(); + toggleMaskWidgets(); + drawSlider(); + }, 50); + }); + }; + } + }, +}); \ No newline at end of file