Rename
This commit is contained in:
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
</module>
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="13">
|
||||
<item index="0" class="java.lang.String" itemvalue="scipy" />
|
||||
<item index="1" class="java.lang.String" itemvalue="transformers" />
|
||||
<item index="2" class="java.lang.String" itemvalue="torch" />
|
||||
<item index="3" class="java.lang.String" itemvalue="torchvision" />
|
||||
<item index="4" class="java.lang.String" itemvalue="psutil" />
|
||||
<item index="5" class="java.lang.String" itemvalue="torchsde" />
|
||||
<item index="6" class="java.lang.String" itemvalue="pyyaml" />
|
||||
<item index="7" class="java.lang.String" itemvalue="tqdm" />
|
||||
<item index="8" class="java.lang.String" itemvalue="safetensors" />
|
||||
<item index="9" class="java.lang.String" itemvalue="aiohttp" />
|
||||
<item index="10" class="java.lang.String" itemvalue="einops" />
|
||||
<item index="11" class="java.lang.String" itemvalue="Pillow" />
|
||||
<item index="12" class="java.lang.String" itemvalue="kornia" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N801" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="audioowl" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/ComfyUI_Fill-Nodes.iml" filepath="$PROJECT_DIR$/.idea/ComfyUI_Fill-Nodes.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
+26
-26
@@ -1,41 +1,41 @@
|
||||
from .nodes.fl_image_randomizer import FL_ImageRandomizer
|
||||
from .nodes.FL_Image_Randomizer import FL_ImageRandomizer
|
||||
from .nodes.FL_Image_Caption_Saver import FL_ImageCaptionSaver
|
||||
from .nodes.fl_image_dimension_display import FL_ImageDimensionDisplay
|
||||
from .nodes.FL_Image_Dimension_Display import FL_ImageDimensionDisplay
|
||||
from .nodes.FL_Code_Node import FL_CodeNode
|
||||
from .nodes.fl_image_pixelator import FL_ImagePixelator
|
||||
from .nodes.FL_Image_Pixelator import FL_ImagePixelator
|
||||
from .nodes.FL_DirectoryCrawl import FL_DirectoryCrawl
|
||||
from .nodes.FL_Ascii import FL_Ascii
|
||||
from .nodes.FL_Glitch import FL_Glitch
|
||||
from .nodes.fl_ripple import FL_Ripple
|
||||
from .nodes.fl_pixelsort import FL_PixelSort
|
||||
from .nodes.FL_Ripple import FL_Ripple
|
||||
from .nodes.FL_PixelSort import FL_PixelSort
|
||||
from .nodes.FL_HexagonalPattern import FL_HexagonalPattern
|
||||
from .nodes.fl_nftgenerator import FL_NFTGenerator
|
||||
from .nodes.FL_NFTGenerator import FL_NFTGenerator
|
||||
from .nodes.FL_HalfTone import FL_HalftonePattern
|
||||
from .nodes.fl_randomrange import FL_RandomNumber
|
||||
from .nodes.fl_promptselector import FL_PromptSelector
|
||||
from .nodes.fl_shader import FL_Shadertoy
|
||||
from .nodes.fl_pixelshader import FL_PixelArtShader
|
||||
from .nodes.fl_infinitezoom import FL_InfiniteZoom
|
||||
from .nodes.fl_paperdrawn import FL_PaperDrawn
|
||||
from .nodes.fl_imagenotes import FL_ImageNotes
|
||||
from .nodes.fl_imagecollage import FL_ImageCollage
|
||||
from .nodes.fl_ksamplersettings import FL_KsamplerSettings
|
||||
from .nodes.fl_retroeffect import FL_RetroEffect
|
||||
from .nodes.fl_td_ksampler import FL_TD_KSampler
|
||||
from .nodes.fl_inpaintcrop import FL_InpaintCrop
|
||||
from .nodes.fl_inpaintcrop import FL_Inpaint_Stitch
|
||||
from .nodes.fl_sdultimate_slices import FL_SDUltimate_Slices
|
||||
from .nodes.FL_RandomRange import FL_RandomNumber
|
||||
from .nodes.FL_PromptSelector import FL_PromptSelector
|
||||
from .nodes.FL_Shader import FL_Shadertoy
|
||||
from .nodes.FL_PixelShader import FL_PixelArtShader
|
||||
from .nodes.FL_InfiniteZoom import FL_InfiniteZoom
|
||||
from .nodes.FL_PaperDrawn import FL_PaperDrawn
|
||||
from .nodes.FL_ImageNotes import FL_ImageNotes
|
||||
from .nodes.FL_ImageCollage import FL_ImageCollage
|
||||
from .nodes.FL_KsamplerSettings import FL_KsamplerSettings
|
||||
from .nodes.FL_RetroEffect import FL_RetroEffect
|
||||
from .nodes.FL_TD_Ksampler import FL_TD_KSampler
|
||||
from .nodes.FL_InpaintCrop import FL_InpaintCrop
|
||||
from .nodes.FL_InpaintCrop import FL_Inpaint_Stitch
|
||||
from .nodes.FL_SD_Slices import FL_SDUltimate_Slices
|
||||
from .nodes.FL_BatchAligned import FL_BatchAlign
|
||||
from .nodes.fl_videocropnstitch import FL_VideoCropMask
|
||||
from .nodes.fl_videocropnstitch import FL_VideoRecompose
|
||||
from .nodes.fl_separatemasks import FL_SeparateMaskComponents
|
||||
from .nodes.fl_pasteoncanvas import FL_PasteOnCanvas
|
||||
from .nodes.FL_VideoCropNStitch import FL_VideoCropMask
|
||||
from .nodes.FL_VideoCropNStitch import FL_VideoRecompose
|
||||
from .nodes.FL_SeparateMasks import FL_SeparateMaskComponents
|
||||
from .nodes.FL_PasteOnCanvas import FL_PasteOnCanvas
|
||||
from .nodes.FL_BulletHellGame import FL_BulletHellGame
|
||||
from .nodes.fl_tetrisgame import FL_TetrisGame
|
||||
from .nodes.FL_TetrisGame import FL_TetrisGame
|
||||
from .nodes.FL_Dither import FL_Dither
|
||||
from .nodes.FL_SystemCheck import FL_SystemCheck
|
||||
from .nodes.FL_ColorPicker import FL_ColorPicker
|
||||
from .nodes.GradientImageGenerator import GradientImageGenerator
|
||||
from .nodes.FL_GradGen import GradientImageGenerator
|
||||
from .nodes.FL_MirrorAndAppendCaptions import FL_MirrorAndAppendCaptions
|
||||
from .nodes.FL_ImageCaptionLayout import FL_ImageCaptionLayout
|
||||
from .nodes.FL_HFHubModelUploader import FL_HFHubModelUploader
|
||||
|
||||
+25
-15
@@ -7,6 +7,7 @@ import logging
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import torch.nn.functional as F
|
||||
import latent_preview
|
||||
|
||||
|
||||
class FL_KsamplerPlus:
|
||||
@@ -33,7 +34,6 @@ class FL_KsamplerPlus:
|
||||
},
|
||||
"optional": {
|
||||
"latent_image": ("LATENT",),
|
||||
"vae": ("VAE",),
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
@@ -145,24 +145,28 @@ class FL_KsamplerPlus:
|
||||
return section, y_start, y_end, x_start, x_end, cropped_positive, cropped_negative
|
||||
|
||||
total_slices = x_slices * y_slices
|
||||
all_slices = [(y, x) for y in range(y_slices) for x in range(x_slices)]
|
||||
|
||||
for i in range(0, total_slices, batch_size):
|
||||
batch_slices = all_slices[i:min(i + batch_size, total_slices)]
|
||||
batch_slices = [(y, x) for y in range(y_slices) for x in range(x_slices)][
|
||||
i:min(i + batch_size, total_slices)]
|
||||
batch_sections = [process_slice(y, x) for y, x in batch_slices]
|
||||
|
||||
batch_latents = torch.cat([section for section, _, _, _, _, _, _ in batch_sections], dim=0)
|
||||
batch_positives = sum([pos for _, _, _, _, _, pos, _ in batch_sections], [])
|
||||
batch_negatives = sum([neg for _, _, _, _, _, _, neg in batch_sections], [])
|
||||
|
||||
processed_batch = common_ksampler(model, seed + i, steps, cfg, sampler_name, scheduler,
|
||||
batch_positives, batch_negatives,
|
||||
{"samples": batch_latents}, denoise=denoise)[0]
|
||||
if use_sliced_conditioning:
|
||||
batch_positive = [cond for _, _, _, _, _, cond, _ in batch_sections]
|
||||
batch_negative = [cond for _, _, _, _, _, _, cond in batch_sections]
|
||||
else:
|
||||
batch_positive = [positive] * len(batch_sections)
|
||||
batch_negative = [negative] * len(batch_sections)
|
||||
|
||||
processed_sections = torch.split(processed_batch["samples"], b, dim=0)
|
||||
# Process each slice in the batch individually
|
||||
for j, (slice_latent, slice_positive, slice_negative) in enumerate(
|
||||
zip(torch.split(batch_latents, 1), batch_positive, batch_negative)):
|
||||
processed_slice = common_ksampler(model, seed + i + j, steps, cfg, sampler_name, scheduler,
|
||||
slice_positive, slice_negative,
|
||||
{"samples": slice_latent}, denoise=denoise)[0]
|
||||
|
||||
for (_, y_start, y_end, x_start, x_end, _, _), processed_section in zip(batch_sections,
|
||||
processed_sections):
|
||||
_, y_start, y_end, x_start, x_end, _, _ = batch_sections[j]
|
||||
is_top = y_start == 0
|
||||
is_left = x_start == 0
|
||||
is_bottom = y_end == h
|
||||
@@ -170,14 +174,14 @@ class FL_KsamplerPlus:
|
||||
blend_mask = create_blend_mask(y_end - y_start, x_end - x_start,
|
||||
overlap_height, overlap_width,
|
||||
is_top, is_left, is_bottom, is_right)
|
||||
blend_mask = blend_mask.unsqueeze(0).unsqueeze(0).expand_as(processed_section)
|
||||
blend_mask = blend_mask.unsqueeze(0).unsqueeze(0).expand_as(processed_slice["samples"])
|
||||
|
||||
processed_section = processed_section.to(device=device)
|
||||
processed_slice = processed_slice["samples"].to(device=device)
|
||||
blend_mask = blend_mask.to(device=device)
|
||||
|
||||
samples[:, :, y_start:y_end, x_start:x_end] = (
|
||||
samples[:, :, y_start:y_end, x_start:x_end] * (1 - blend_mask) +
|
||||
processed_section * blend_mask
|
||||
processed_slice * blend_mask
|
||||
)
|
||||
|
||||
if device.type == 'cuda':
|
||||
@@ -193,3 +197,9 @@ class FL_KsamplerPlus:
|
||||
except Exception as e:
|
||||
logging.error(f"Error in FL_UltimateUpscale: {str(e)}")
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(s, model, positive, negative, x_slices, y_slices, overlap, batch_size, seed, steps, cfg,
|
||||
sampler_name, scheduler, denoise, input_type, use_sliced_conditioning, latent_image=None, image=None,
|
||||
vae=None):
|
||||
return float("NaN")
|
||||
+356
-60
@@ -8,65 +8,359 @@ app.registerExtension({
|
||||
];
|
||||
|
||||
if (animatedNodeClasses.includes(node.comfyClass)) {
|
||||
addDenoisingTextAnimatedDisplay(node);
|
||||
addModernSlotMachineAnimation(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function addDenoisingTextAnimatedDisplay(node) {
|
||||
const ANIMATION_WIDTH = 120;
|
||||
const ANIMATION_HEIGHT = 80;
|
||||
const PIXEL_SIZE = 1;
|
||||
const GRID_WIDTH = Math.floor(ANIMATION_WIDTH / PIXEL_SIZE);
|
||||
const GRID_HEIGHT = Math.floor(ANIMATION_HEIGHT / PIXEL_SIZE);
|
||||
function addModernSlotMachineAnimation(node) {
|
||||
// Easily adjustable parameters
|
||||
const PARAMS = {
|
||||
// Animation dimensions
|
||||
ANIMATION_WIDTH: 120,
|
||||
ANIMATION_HEIGHT: 80,
|
||||
X_OFFSET: 0,
|
||||
Y_OFFSET: 100,
|
||||
|
||||
let grid = createEmptyGrid(GRID_WIDTH, GRID_HEIGHT);
|
||||
let animationProgress = 0;
|
||||
let xOffset = 0;
|
||||
let yOffset = 90;
|
||||
// Reel settings
|
||||
REEL_WIDTH: 30,
|
||||
REEL_HEIGHT: 30,
|
||||
REEL_COUNT: 3,
|
||||
SYMBOL_SIZE: 24,
|
||||
SPIN_SPEED: 0.2,
|
||||
|
||||
function createEmptyGrid(width, height) {
|
||||
return Array.from({ length: height }, () =>
|
||||
Array.from({ length: width }, () => ({ value: 0, hue: 0 }))
|
||||
);
|
||||
}
|
||||
// Game logic
|
||||
WIN_PROBABILITY: 0.25,
|
||||
SYMBOLS: ['💎', '🔮', '🎭', '🎨', '🎬', '🏆'],
|
||||
|
||||
function drawText(ctx) {
|
||||
ctx.font = 'bold 20px Arial';
|
||||
// Button settings
|
||||
BUTTON_WIDTH: 50,
|
||||
BUTTON_HEIGHT: 20,
|
||||
BUTTON_Y_OFFSET: 25,
|
||||
|
||||
// Animation timings
|
||||
SPIN_DURATION: 1000,
|
||||
WIN_ANIMATION_SPEED: 0.02,
|
||||
|
||||
// Visual styles
|
||||
BACKGROUND_COLOR: '#4B0082',
|
||||
FRAME_COLOR: '#8A2BE2',
|
||||
REEL_COLOR: '#2E0854',
|
||||
BUTTON_COLORS: {
|
||||
NORMAL: ['#8A2BE2', '#6A0DAD'],
|
||||
SPINNING: ['#6A0DAD', '#4B0082']
|
||||
},
|
||||
TEXT_COLOR: '#FFFFFF',
|
||||
|
||||
// Particle system
|
||||
MAX_PARTICLES: 100,
|
||||
PARTICLE_DECAY_RATE: 0.02
|
||||
};
|
||||
|
||||
let reels = Array(PARAMS.REEL_COUNT).fill().map(() => ({
|
||||
symbols: [...PARAMS.SYMBOLS],
|
||||
position: 0,
|
||||
spinning: false,
|
||||
finalPosition: 0,
|
||||
offset: 0
|
||||
}));
|
||||
|
||||
let spinning = false;
|
||||
let winningSymbol = null;
|
||||
let winAnimation = null;
|
||||
let winAnimationProgress = 0;
|
||||
let particles = [];
|
||||
|
||||
function drawModernSlotMachine(ctx) {
|
||||
// Background
|
||||
ctx.fillStyle = PARAMS.BACKGROUND_COLOR;
|
||||
ctx.fillRect(0, 0, PARAMS.ANIMATION_WIDTH, PARAMS.ANIMATION_HEIGHT);
|
||||
|
||||
// Frame
|
||||
ctx.strokeStyle = PARAMS.FRAME_COLOR;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(2, 2, PARAMS.ANIMATION_WIDTH - 4, PARAMS.ANIMATION_HEIGHT - 4);
|
||||
|
||||
// Reels
|
||||
const reelStartX = (PARAMS.ANIMATION_WIDTH - (PARAMS.REEL_COUNT * PARAMS.REEL_WIDTH + (PARAMS.REEL_COUNT - 1) * 5)) / 2;
|
||||
reels.forEach((reel, i) => {
|
||||
ctx.fillStyle = PARAMS.REEL_COLOR;
|
||||
ctx.fillRect(reelStartX + i * (PARAMS.REEL_WIDTH + 5), 15, PARAMS.REEL_WIDTH, PARAMS.REEL_HEIGHT);
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.rect(reelStartX + i * (PARAMS.REEL_WIDTH + 5), 15, PARAMS.REEL_WIDTH, PARAMS.REEL_HEIGHT);
|
||||
ctx.clip();
|
||||
|
||||
const symbolIndex = Math.floor(reel.position) % PARAMS.SYMBOLS.length;
|
||||
const y = 30 - (reel.position % 1) * PARAMS.SYMBOL_SIZE + reel.offset;
|
||||
ctx.font = `${PARAMS.SYMBOL_SIZE}px Arial`;
|
||||
ctx.fillStyle = PARAMS.TEXT_COLOR;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(reel.symbols[symbolIndex], reelStartX + PARAMS.REEL_WIDTH / 2 + i * (PARAMS.REEL_WIDTH + 5), y);
|
||||
|
||||
ctx.restore();
|
||||
});
|
||||
|
||||
// Spin button
|
||||
const buttonGradient = ctx.createLinearGradient(0, PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET, 0, PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET + PARAMS.BUTTON_HEIGHT);
|
||||
const buttonColors = spinning ? PARAMS.BUTTON_COLORS.SPINNING : PARAMS.BUTTON_COLORS.NORMAL;
|
||||
buttonGradient.addColorStop(0, buttonColors[0]);
|
||||
buttonGradient.addColorStop(1, buttonColors[1]);
|
||||
ctx.fillStyle = buttonGradient;
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(PARAMS.ANIMATION_WIDTH / 2 - PARAMS.BUTTON_WIDTH / 2, PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET, PARAMS.BUTTON_WIDTH, PARAMS.BUTTON_HEIGHT, 5);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = PARAMS.TEXT_COLOR;
|
||||
ctx.font = 'bold 12px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText('CUT', ANIMATION_WIDTH / 2, ANIMATION_HEIGHT / 2 - 15);
|
||||
ctx.fillText('THE NOISE', ANIMATION_WIDTH / 2, ANIMATION_HEIGHT / 2 + 15);
|
||||
}
|
||||
ctx.fillText('SPIN', PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET + PARAMS.BUTTON_HEIGHT / 2);
|
||||
|
||||
function updateGrid() {
|
||||
animationProgress += 0.005; // Slightly faster animation
|
||||
if (animationProgress > 1) {
|
||||
animationProgress = 0;
|
||||
grid = createEmptyGrid(GRID_WIDTH, GRID_HEIGHT);
|
||||
// Win animation
|
||||
if (winAnimation) {
|
||||
winAnimation(ctx, winAnimationProgress);
|
||||
}
|
||||
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = ANIMATION_WIDTH;
|
||||
tempCanvas.height = ANIMATION_HEIGHT;
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
drawText(tempCtx);
|
||||
// Draw particles
|
||||
particles.forEach(p => {
|
||||
ctx.fillStyle = p.color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
});
|
||||
}
|
||||
|
||||
const imageData = tempCtx.getImageData(0, 0, ANIMATION_WIDTH, ANIMATION_HEIGHT);
|
||||
function spin() {
|
||||
if (spinning) return;
|
||||
spinning = true;
|
||||
winningSymbol = null;
|
||||
winAnimation = null;
|
||||
winAnimationProgress = 0;
|
||||
particles = [];
|
||||
|
||||
for (let y = 0; y < GRID_HEIGHT; y++) {
|
||||
for (let x = 0; x < GRID_WIDTH; x++) {
|
||||
const i = (y * ANIMATION_WIDTH + x) * 4;
|
||||
if (imageData.data[i] > 0) {
|
||||
if (grid[y][x].value === 0) {
|
||||
grid[y][x].hue = Math.random() * 360; // Assign a random hue
|
||||
}
|
||||
if (Math.random() < animationProgress * 0.2) {
|
||||
grid[y][x].value = Math.min(grid[y][x].value + 0.2, 1);
|
||||
}
|
||||
reels.forEach(reel => {
|
||||
reel.spinning = true;
|
||||
reel.spinDuration = 50 + Math.random() * 50;
|
||||
reel.offset = Math.random() * PARAMS.SYMBOL_SIZE;
|
||||
});
|
||||
|
||||
const isWin = Math.random() < PARAMS.WIN_PROBABILITY;
|
||||
if (isWin) {
|
||||
const winSymbolIndex = Math.floor(Math.random() * PARAMS.SYMBOLS.length);
|
||||
reels.forEach(reel => reel.finalPosition = winSymbolIndex);
|
||||
winningSymbol = PARAMS.SYMBOLS[winSymbolIndex];
|
||||
} else {
|
||||
reels.forEach(reel => reel.finalPosition = Math.floor(Math.random() * PARAMS.SYMBOLS.length));
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
reels.forEach(reel => {
|
||||
reel.spinning = false;
|
||||
reel.position = reel.finalPosition;
|
||||
reel.offset = 0;
|
||||
});
|
||||
spinning = false;
|
||||
if (isWin) triggerWin();
|
||||
}, PARAMS.SPIN_DURATION);
|
||||
}
|
||||
|
||||
function triggerWin() {
|
||||
switch (winningSymbol) {
|
||||
case '💎': winAnimation = diamondWinAnimation; break;
|
||||
case '🔮': winAnimation = crystalBallWinAnimation; break;
|
||||
case '🎭': winAnimation = masksWinAnimation; break;
|
||||
case '🎨': winAnimation = paletteWinAnimation; break;
|
||||
case '🎬': winAnimation = clapperboardWinAnimation; break;
|
||||
case '🏆': winAnimation = trophyWinAnimation; break;
|
||||
}
|
||||
}
|
||||
|
||||
function diamondWinAnimation(ctx, progress) {
|
||||
const size = 40 * Math.sin(progress * Math.PI);
|
||||
ctx.font = `${size}px Arial`;
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${1 - progress})`;
|
||||
ctx.fillText('💎', PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2);
|
||||
|
||||
// Sparkling effect
|
||||
if (Math.random() < 0.3) {
|
||||
particles.push({
|
||||
x: Math.random() * PARAMS.ANIMATION_WIDTH,
|
||||
y: Math.random() * PARAMS.ANIMATION_HEIGHT,
|
||||
size: Math.random() * 3,
|
||||
color: `hsl(${Math.random() * 360}, 100%, 50%)`,
|
||||
life: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function crystalBallWinAnimation(ctx, progress) {
|
||||
ctx.fillStyle = `rgba(128, 0, 128, ${0.5 - progress * 0.5})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2, 30 * progress, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Swirling mist effect
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const angle = progress * Math.PI * 2 + i * Math.PI / 2.5;
|
||||
const x = PARAMS.ANIMATION_WIDTH / 2 + Math.cos(angle) * 20 * progress;
|
||||
const y = PARAMS.ANIMATION_HEIGHT / 2 + Math.sin(angle) * 20 * progress;
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${1 - progress})`;
|
||||
ctx.fillText('✨', x, y);
|
||||
}
|
||||
|
||||
ctx.font = '24px Arial';
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText('🔮', PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2);
|
||||
}
|
||||
|
||||
function masksWinAnimation(ctx, progress) {
|
||||
const angle = progress * Math.PI * 4;
|
||||
ctx.save();
|
||||
ctx.translate(PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2);
|
||||
|
||||
// Comedy mask
|
||||
ctx.rotate(angle);
|
||||
ctx.font = '24px Arial';
|
||||
ctx.fillStyle = `rgba(255, 255, 0, ${1 - progress})`;
|
||||
ctx.fillText('😂', 0, -20);
|
||||
|
||||
// Tragedy mask
|
||||
ctx.rotate(Math.PI);
|
||||
ctx.fillStyle = `rgba(0, 0, 255, ${1 - progress})`;
|
||||
ctx.fillText('😭', 0, -20);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
// Confetti effect
|
||||
if (Math.random() < 0.2) {
|
||||
particles.push({
|
||||
x: Math.random() * PARAMS.ANIMATION_WIDTH,
|
||||
y: -5,
|
||||
size: Math.random() * 5 + 2,
|
||||
color: Math.random() < 0.5 ? 'yellow' : 'blue',
|
||||
life: 1,
|
||||
vy: Math.random() * 2 + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function paletteWinAnimation(ctx, progress) {
|
||||
const colors = ['red', 'blue', 'green', 'yellow', 'purple'];
|
||||
colors.forEach((color, i) => {
|
||||
const angle = (i / colors.length + progress) * Math.PI * 4;
|
||||
const x = PARAMS.ANIMATION_WIDTH / 2 + Math.cos(angle) * 30 * Math.sin(progress * Math.PI);
|
||||
const y = PARAMS.ANIMATION_HEIGHT / 2 + Math.sin(angle) * 30 * Math.sin(progress * Math.PI);
|
||||
ctx.fillStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Paint splatter effect
|
||||
if (Math.random() < 0.1) {
|
||||
particles.push({
|
||||
x: x,
|
||||
y: y,
|
||||
size: Math.random() * 3 + 1,
|
||||
color: color,
|
||||
life: 1,
|
||||
vx: (Math.random() - 0.5) * 2,
|
||||
vy: (Math.random() - 0.5) * 2
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ctx.font = '24px Arial';
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText('🎨', PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2);
|
||||
}
|
||||
|
||||
function clapperboardWinAnimation(ctx, progress) {
|
||||
ctx.save();
|
||||
ctx.translate(PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2);
|
||||
|
||||
// Clapping effect
|
||||
const clapAngle = Math.sin(progress * Math.PI * 8) * 0.3;
|
||||
ctx.rotate(clapAngle);
|
||||
ctx.font = '24px Arial';
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText('🎬', 0, 0);
|
||||
|
||||
// Starburst effect
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const angle = i * Math.PI / 4 + progress * Math.PI * 2;
|
||||
const length = 20 + Math.sin(progress * Math.PI) * 10;
|
||||
ctx.strokeStyle = `rgba(255, 255, 0, ${1 - progress})`;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(Math.cos(angle) * length, Math.sin(angle) * length);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function trophyWinAnimation(ctx, progress) {
|
||||
const y = PARAMS.ANIMATION_HEIGHT / 2 + Math.sin(progress * Math.PI * 2) * 10;
|
||||
ctx.font = '24px Arial';
|
||||
ctx.fillStyle = `rgba(255, 215, 0, ${1 - progress})`;
|
||||
ctx.fillText('🏆', PARAMS.ANIMATION_WIDTH / 2, y);
|
||||
|
||||
// Fireworks effect
|
||||
if (Math.random() < 0.2) {
|
||||
const x = Math.random() * PARAMS.ANIMATION_WIDTH;
|
||||
const y = PARAMS.ANIMATION_HEIGHT;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const speed = Math.random() * 2 + 1;
|
||||
particles.push({
|
||||
x: x,
|
||||
y: y,
|
||||
size: Math.random() * 2 + 1,
|
||||
color: `hsl(${Math.random() * 360}, 100%, 50%)`,
|
||||
life: 1,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed - 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.font = '16px Arial';
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${progress})`;
|
||||
ctx.fillText('JACKPOT!', PARAMS.ANIMATION_WIDTH / 2, PARAMS.ANIMATION_HEIGHT / 2 + 30);
|
||||
}
|
||||
|
||||
function updateAnimation() {
|
||||
reels.forEach(reel => {
|
||||
if (reel.spinning) {
|
||||
reel.position += PARAMS.SPIN_SPEED;
|
||||
if (reel.offset > 0) {
|
||||
reel.offset -= 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (winAnimation) {
|
||||
winAnimationProgress += PARAMS.WIN_ANIMATION_SPEED;
|
||||
if (winAnimationProgress >= 1) {
|
||||
winAnimation = null;
|
||||
winAnimationProgress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update particles
|
||||
particles = particles.filter(p => {
|
||||
p.x += p.vx || 0;
|
||||
p.y += p.vy || 0;
|
||||
p.life -= PARAMS.PARTICLE_DECAY_RATE;
|
||||
return p.life > 0;
|
||||
});
|
||||
|
||||
// Limit the number of particles
|
||||
if (particles.length > PARAMS.MAX_PARTICLES) {
|
||||
particles.splice(0, particles.length - PARAMS.MAX_PARTICLES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,32 +369,34 @@ function addDenoisingTextAnimatedDisplay(node) {
|
||||
ctx.save();
|
||||
|
||||
const nodeWidth = this.size[0];
|
||||
const baseXOffset = (nodeWidth - ANIMATION_WIDTH) / 2;
|
||||
ctx.translate(baseXOffset + xOffset, -ANIMATION_HEIGHT + yOffset);
|
||||
const baseXOffset = (nodeWidth - PARAMS.ANIMATION_WIDTH) / 2;
|
||||
ctx.translate(baseXOffset + PARAMS.X_OFFSET, -PARAMS.ANIMATION_HEIGHT + PARAMS.Y_OFFSET);
|
||||
|
||||
ctx.clearRect(0, 0, ANIMATION_WIDTH, ANIMATION_HEIGHT);
|
||||
|
||||
// Draw appearing pixels with color and pulse effect
|
||||
for (let y = 0; y < GRID_HEIGHT; y++) {
|
||||
for (let x = 0; x < GRID_WIDTH; x++) {
|
||||
const { value, hue } = grid[y][x];
|
||||
if (value > 0) {
|
||||
const pulseEffect = 0.7 + 0.3 * Math.sin(animationProgress * 10 + x * 0.1 + y * 0.1);
|
||||
const alpha = value * pulseEffect;
|
||||
ctx.fillStyle = `hsla(${hue}, 100%, 50%, ${alpha})`;
|
||||
ctx.fillRect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
drawModernSlotMachine(ctx);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
updateGrid();
|
||||
updateAnimation();
|
||||
|
||||
this.setDirtyCanvas(true);
|
||||
requestAnimationFrame(() => this.setDirtyCanvas(true));
|
||||
}
|
||||
};
|
||||
|
||||
node.onMouseDown = function(event) {
|
||||
const nodeWidth = this.size[0];
|
||||
const baseXOffset = (nodeWidth - PARAMS.ANIMATION_WIDTH) / 2;
|
||||
const localX = event.canvasX - this.pos[0] - baseXOffset - PARAMS.X_OFFSET;
|
||||
const localY = event.canvasY - this.pos[1] + PARAMS.ANIMATION_HEIGHT - PARAMS.Y_OFFSET;
|
||||
|
||||
if (localX >= PARAMS.ANIMATION_WIDTH / 2 - PARAMS.BUTTON_WIDTH / 2 &&
|
||||
localX <= PARAMS.ANIMATION_WIDTH / 2 + PARAMS.BUTTON_WIDTH / 2 &&
|
||||
localY >= PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET &&
|
||||
localY <= PARAMS.ANIMATION_HEIGHT - PARAMS.BUTTON_Y_OFFSET + PARAMS.BUTTON_HEIGHT) {
|
||||
spin();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
node.setDirtyCanvas(true);
|
||||
}
|
||||
Reference in New Issue
Block a user