Compare commits
2 Commits
0f60721162
...
2af1ef9292
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2af1ef9292 | ||
|
|
6715544952 |
Binary file not shown.
@@ -45,10 +45,16 @@
|
|||||||
<AIImages.Settings.ApiEndpoint>API Endpoint</AIImages.Settings.ApiEndpoint>
|
<AIImages.Settings.ApiEndpoint>API Endpoint</AIImages.Settings.ApiEndpoint>
|
||||||
<AIImages.Settings.TestConnection>Test Connection</AIImages.Settings.TestConnection>
|
<AIImages.Settings.TestConnection>Test Connection</AIImages.Settings.TestConnection>
|
||||||
<AIImages.Settings.LoadModels>Load Available Models</AIImages.Settings.LoadModels>
|
<AIImages.Settings.LoadModels>Load Available Models</AIImages.Settings.LoadModels>
|
||||||
|
<AIImages.Settings.Model>Model</AIImages.Settings.Model>
|
||||||
|
<AIImages.Settings.NoModelSelected>No model selected</AIImages.Settings.NoModelSelected>
|
||||||
|
<AIImages.Settings.LoadModelsFirst>Load models first</AIImages.Settings.LoadModelsFirst>
|
||||||
|
<AIImages.Settings.LoadSamplersSchedulers>Load Samplers & Schedulers</AIImages.Settings.LoadSamplersSchedulers>
|
||||||
<AIImages.Settings.ConnectionSuccess>Successfully connected to API!</AIImages.Settings.ConnectionSuccess>
|
<AIImages.Settings.ConnectionSuccess>Successfully connected to API!</AIImages.Settings.ConnectionSuccess>
|
||||||
<AIImages.Settings.ConnectionFailed>Failed to connect to API. Check endpoint and ensure Stable Diffusion WebUI is running.</AIImages.Settings.ConnectionFailed>
|
<AIImages.Settings.ConnectionFailed>Failed to connect to API. Check endpoint and ensure Stable Diffusion WebUI is running.</AIImages.Settings.ConnectionFailed>
|
||||||
<AIImages.Settings.ModelsLoaded>Loaded {0} models from API</AIImages.Settings.ModelsLoaded>
|
<AIImages.Settings.ModelsLoaded>Loaded {0} models from API</AIImages.Settings.ModelsLoaded>
|
||||||
<AIImages.Settings.NoModelsFound>No models found. Check API connection.</AIImages.Settings.NoModelsFound>
|
<AIImages.Settings.NoModelsFound>No models found. Check API connection.</AIImages.Settings.NoModelsFound>
|
||||||
|
<AIImages.Settings.SamplersSchedulersLoaded>Loaded {0} samplers and {1} schedulers from API</AIImages.Settings.SamplersSchedulersLoaded>
|
||||||
|
<AIImages.Settings.NoSamplersSchedulersFound>No samplers or schedulers found. Check API connection.</AIImages.Settings.NoSamplersSchedulersFound>
|
||||||
<AIImages.Settings.GenerationSection>Generation Settings</AIImages.Settings.GenerationSection>
|
<AIImages.Settings.GenerationSection>Generation Settings</AIImages.Settings.GenerationSection>
|
||||||
<AIImages.Settings.GenerationSectionTooltip>Configure image generation parameters</AIImages.Settings.GenerationSectionTooltip>
|
<AIImages.Settings.GenerationSectionTooltip>Configure image generation parameters</AIImages.Settings.GenerationSectionTooltip>
|
||||||
<AIImages.Settings.ArtStyle>Art Style</AIImages.Settings.ArtStyle>
|
<AIImages.Settings.ArtStyle>Art Style</AIImages.Settings.ArtStyle>
|
||||||
@@ -58,6 +64,7 @@
|
|||||||
<AIImages.Settings.Width>Width</AIImages.Settings.Width>
|
<AIImages.Settings.Width>Width</AIImages.Settings.Width>
|
||||||
<AIImages.Settings.Height>Height</AIImages.Settings.Height>
|
<AIImages.Settings.Height>Height</AIImages.Settings.Height>
|
||||||
<AIImages.Settings.Sampler>Sampler</AIImages.Settings.Sampler>
|
<AIImages.Settings.Sampler>Sampler</AIImages.Settings.Sampler>
|
||||||
|
<AIImages.Settings.Scheduler>Schedule Type</AIImages.Settings.Scheduler>
|
||||||
<AIImages.Settings.PromptsSection>Prompts</AIImages.Settings.PromptsSection>
|
<AIImages.Settings.PromptsSection>Prompts</AIImages.Settings.PromptsSection>
|
||||||
<AIImages.Settings.PromptsSectionTooltip>Base prompts that will be added to all generations</AIImages.Settings.PromptsSectionTooltip>
|
<AIImages.Settings.PromptsSectionTooltip>Base prompts that will be added to all generations</AIImages.Settings.PromptsSectionTooltip>
|
||||||
<AIImages.Settings.BasePositivePrompt>Base Positive Prompt</AIImages.Settings.BasePositivePrompt>
|
<AIImages.Settings.BasePositivePrompt>Base Positive Prompt</AIImages.Settings.BasePositivePrompt>
|
||||||
|
|||||||
@@ -45,10 +45,16 @@
|
|||||||
<AIImages.Settings.ApiEndpoint>Адрес API</AIImages.Settings.ApiEndpoint>
|
<AIImages.Settings.ApiEndpoint>Адрес API</AIImages.Settings.ApiEndpoint>
|
||||||
<AIImages.Settings.TestConnection>Проверить соединение</AIImages.Settings.TestConnection>
|
<AIImages.Settings.TestConnection>Проверить соединение</AIImages.Settings.TestConnection>
|
||||||
<AIImages.Settings.LoadModels>Загрузить доступные модели</AIImages.Settings.LoadModels>
|
<AIImages.Settings.LoadModels>Загрузить доступные модели</AIImages.Settings.LoadModels>
|
||||||
|
<AIImages.Settings.Model>Модель</AIImages.Settings.Model>
|
||||||
|
<AIImages.Settings.NoModelSelected>Модель не выбрана</AIImages.Settings.NoModelSelected>
|
||||||
|
<AIImages.Settings.LoadModelsFirst>Сначала загрузите модели</AIImages.Settings.LoadModelsFirst>
|
||||||
|
<AIImages.Settings.LoadSamplersSchedulers>Загрузить сэмплеры и планировщики</AIImages.Settings.LoadSamplersSchedulers>
|
||||||
<AIImages.Settings.ConnectionSuccess>Успешное подключение к API!</AIImages.Settings.ConnectionSuccess>
|
<AIImages.Settings.ConnectionSuccess>Успешное подключение к API!</AIImages.Settings.ConnectionSuccess>
|
||||||
<AIImages.Settings.ConnectionFailed>Не удалось подключиться к API. Проверьте адрес и убедитесь, что Stable Diffusion WebUI запущен.</AIImages.Settings.ConnectionFailed>
|
<AIImages.Settings.ConnectionFailed>Не удалось подключиться к API. Проверьте адрес и убедитесь, что Stable Diffusion WebUI запущен.</AIImages.Settings.ConnectionFailed>
|
||||||
<AIImages.Settings.ModelsLoaded>Загружено {0} моделей из API</AIImages.Settings.ModelsLoaded>
|
<AIImages.Settings.ModelsLoaded>Загружено {0} моделей из API</AIImages.Settings.ModelsLoaded>
|
||||||
<AIImages.Settings.NoModelsFound>Модели не найдены. Проверьте подключение к API.</AIImages.Settings.NoModelsFound>
|
<AIImages.Settings.NoModelsFound>Модели не найдены. Проверьте подключение к API.</AIImages.Settings.NoModelsFound>
|
||||||
|
<AIImages.Settings.SamplersSchedulersLoaded>Загружено {0} сэмплеров и {1} планировщиков из API</AIImages.Settings.SamplersSchedulersLoaded>
|
||||||
|
<AIImages.Settings.NoSamplersSchedulersFound>Сэмплеры и планировщики не найдены. Проверьте подключение к API.</AIImages.Settings.NoSamplersSchedulersFound>
|
||||||
<AIImages.Settings.GenerationSection>Настройки генерации</AIImages.Settings.GenerationSection>
|
<AIImages.Settings.GenerationSection>Настройки генерации</AIImages.Settings.GenerationSection>
|
||||||
<AIImages.Settings.GenerationSectionTooltip>Настройка параметров генерации изображений</AIImages.Settings.GenerationSectionTooltip>
|
<AIImages.Settings.GenerationSectionTooltip>Настройка параметров генерации изображений</AIImages.Settings.GenerationSectionTooltip>
|
||||||
<AIImages.Settings.ArtStyle>Художественный стиль</AIImages.Settings.ArtStyle>
|
<AIImages.Settings.ArtStyle>Художественный стиль</AIImages.Settings.ArtStyle>
|
||||||
@@ -58,6 +64,7 @@
|
|||||||
<AIImages.Settings.Width>Ширина</AIImages.Settings.Width>
|
<AIImages.Settings.Width>Ширина</AIImages.Settings.Width>
|
||||||
<AIImages.Settings.Height>Высота</AIImages.Settings.Height>
|
<AIImages.Settings.Height>Высота</AIImages.Settings.Height>
|
||||||
<AIImages.Settings.Sampler>Сэмплер</AIImages.Settings.Sampler>
|
<AIImages.Settings.Sampler>Сэмплер</AIImages.Settings.Sampler>
|
||||||
|
<AIImages.Settings.Scheduler>Тип планировщика</AIImages.Settings.Scheduler>
|
||||||
<AIImages.Settings.PromptsSection>Промпты</AIImages.Settings.PromptsSection>
|
<AIImages.Settings.PromptsSection>Промпты</AIImages.Settings.PromptsSection>
|
||||||
<AIImages.Settings.PromptsSectionTooltip>Базовые промпты, которые будут добавлены ко всем генерациям</AIImages.Settings.PromptsSectionTooltip>
|
<AIImages.Settings.PromptsSectionTooltip>Базовые промпты, которые будут добавлены ко всем генерациям</AIImages.Settings.PromptsSectionTooltip>
|
||||||
<AIImages.Settings.BasePositivePrompt>Базовый позитивный промпт</AIImages.Settings.BasePositivePrompt>
|
<AIImages.Settings.BasePositivePrompt>Базовый позитивный промпт</AIImages.Settings.BasePositivePrompt>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace AIImages.Models
|
|||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
public int Height { get; set; }
|
public int Height { get; set; }
|
||||||
public string Sampler { get; set; }
|
public string Sampler { get; set; }
|
||||||
|
public string Scheduler { get; set; }
|
||||||
public int Seed { get; set; }
|
public int Seed { get; set; }
|
||||||
public string Model { get; set; }
|
public string Model { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace AIImages.Models
|
|||||||
public string BodyType { get; set; }
|
public string BodyType { get; set; }
|
||||||
public Color SkinColor { get; set; }
|
public Color SkinColor { get; set; }
|
||||||
public string HairStyle { get; set; }
|
public string HairStyle { get; set; }
|
||||||
|
public string HairDefName { get; set; }
|
||||||
public Color HairColor { get; set; }
|
public Color HairColor { get; set; }
|
||||||
public List<Trait> Traits { get; set; }
|
public List<Trait> Traits { get; set; }
|
||||||
public List<ApparelData> Apparel { get; set; }
|
public List<ApparelData> Apparel { get; set; }
|
||||||
@@ -33,7 +34,9 @@ namespace AIImages.Models
|
|||||||
public class ApparelData
|
public class ApparelData
|
||||||
{
|
{
|
||||||
public string Label { get; set; }
|
public string Label { get; set; }
|
||||||
|
public string DefName { get; set; }
|
||||||
public string Material { get; set; }
|
public string Material { get; set; }
|
||||||
|
public string MaterialDefName { get; set; }
|
||||||
public QualityCategory? Quality { get; set; }
|
public QualityCategory? Quality { get; set; }
|
||||||
public Color Color { get; set; }
|
public Color Color { get; set; }
|
||||||
public string LayerType { get; set; }
|
public string LayerType { get; set; }
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ namespace AIImages.Models
|
|||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
public int Height { get; set; }
|
public int Height { get; set; }
|
||||||
public string Sampler { get; set; }
|
public string Sampler { get; set; }
|
||||||
|
public string Scheduler { get; set; }
|
||||||
public int Seed { get; set; }
|
public int Seed { get; set; }
|
||||||
public string Model { get; set; }
|
public string Model { get; set; }
|
||||||
public ArtStyle ArtStyle { get; set; }
|
public ArtStyle ArtStyle { get; set; }
|
||||||
public ShotType ShotType { get; set; }
|
|
||||||
|
|
||||||
public StableDiffusionSettings()
|
public StableDiffusionSettings()
|
||||||
{
|
{
|
||||||
@@ -25,9 +25,9 @@ namespace AIImages.Models
|
|||||||
Width = 512;
|
Width = 512;
|
||||||
Height = 768;
|
Height = 768;
|
||||||
Sampler = "Euler a";
|
Sampler = "Euler a";
|
||||||
|
Scheduler = "Automatic";
|
||||||
Seed = -1; // Случайный seed
|
Seed = -1; // Случайный seed
|
||||||
ArtStyle = ArtStyle.Realistic;
|
ArtStyle = ArtStyle.Realistic;
|
||||||
ShotType = ShotType.Portrait;
|
|
||||||
PositivePrompt = "";
|
PositivePrompt = "";
|
||||||
NegativePrompt = "ugly, deformed, low quality, blurry, bad anatomy, worst quality";
|
NegativePrompt = "ugly, deformed, low quality, blurry, bad anatomy, worst quality";
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@ namespace AIImages.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ArtStyle
|
public enum ArtStyle
|
||||||
{
|
{
|
||||||
|
None, // Без стиля
|
||||||
Realistic,
|
Realistic,
|
||||||
SemiRealistic,
|
SemiRealistic,
|
||||||
Anime,
|
Anime,
|
||||||
@@ -47,16 +48,4 @@ namespace AIImages.Models
|
|||||||
Sketch,
|
Sketch,
|
||||||
CellShaded,
|
CellShaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тип кадра/композиции
|
|
||||||
/// </summary>
|
|
||||||
public enum ShotType
|
|
||||||
{
|
|
||||||
Portrait, // Портрет (голова и плечи)
|
|
||||||
HalfBody, // Половина тела
|
|
||||||
FullBody, // Полное тело
|
|
||||||
CloseUp, // Крупный план
|
|
||||||
ThreeQuarter, // Три четверти
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace AIImages.Services
|
|||||||
string
|
string
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
{ ArtStyle.None, "" },
|
||||||
{ ArtStyle.Realistic, "photorealistic, hyperrealistic, realistic photo, photography" },
|
{ ArtStyle.Realistic, "photorealistic, hyperrealistic, realistic photo, photography" },
|
||||||
{ ArtStyle.SemiRealistic, "semi-realistic, detailed illustration, realistic art" },
|
{ ArtStyle.SemiRealistic, "semi-realistic, detailed illustration, realistic art" },
|
||||||
{ ArtStyle.Anime, "anime style, manga style, anime character" },
|
{ ArtStyle.Anime, "anime style, manga style, anime character" },
|
||||||
@@ -52,18 +53,6 @@ namespace AIImages.Services
|
|||||||
{ ArtStyle.CellShaded, "cell shaded, flat colors, toon shading, stylized" },
|
{ ArtStyle.CellShaded, "cell shaded, flat colors, toon shading, stylized" },
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<ShotType, string> ShotTypePrompts = new Dictionary<
|
|
||||||
ShotType,
|
|
||||||
string
|
|
||||||
>
|
|
||||||
{
|
|
||||||
{ ShotType.Portrait, "portrait, head and shoulders" },
|
|
||||||
{ ShotType.HalfBody, "half body shot, waist up" },
|
|
||||||
{ ShotType.FullBody, "full body, full length" },
|
|
||||||
{ ShotType.CloseUp, "close up, face focus, detailed face" },
|
|
||||||
{ ShotType.ThreeQuarter, "three-quarter view, 3/4 view" },
|
|
||||||
};
|
|
||||||
|
|
||||||
public string GeneratePositivePrompt(
|
public string GeneratePositivePrompt(
|
||||||
PawnAppearanceData appearanceData,
|
PawnAppearanceData appearanceData,
|
||||||
StableDiffusionSettings settings
|
StableDiffusionSettings settings
|
||||||
@@ -75,18 +64,17 @@ namespace AIImages.Services
|
|||||||
StringBuilder prompt = new StringBuilder();
|
StringBuilder prompt = new StringBuilder();
|
||||||
|
|
||||||
// 1. Художественный стиль
|
// 1. Художественный стиль
|
||||||
if (ArtStylePrompts.TryGetValue(settings.ArtStyle, out string stylePrompt))
|
if (
|
||||||
|
ArtStylePrompts.TryGetValue(settings.ArtStyle, out string stylePrompt)
|
||||||
|
&& !string.IsNullOrEmpty(stylePrompt)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
prompt.Append(stylePrompt);
|
prompt.Append(stylePrompt);
|
||||||
prompt.Append(", ");
|
prompt.Append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Тип кадра
|
// 2. Тип кадра - автоматически добавляем "portrait" для генерации персонажей
|
||||||
if (ShotTypePrompts.TryGetValue(settings.ShotType, out string shotPrompt))
|
prompt.Append("portrait, head and shoulders of ");
|
||||||
{
|
|
||||||
prompt.Append(shotPrompt);
|
|
||||||
prompt.Append(" of ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Базовое описание (возраст и пол)
|
// 3. Базовое описание (возраст и пол)
|
||||||
prompt.Append(GetAgeAndGenderDescription(appearanceData));
|
prompt.Append(GetAgeAndGenderDescription(appearanceData));
|
||||||
@@ -157,16 +145,18 @@ namespace AIImages.Services
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Специфичные для стиля негативы
|
// Специфичные для стиля негативы
|
||||||
if (
|
switch (settings.ArtStyle)
|
||||||
settings.ArtStyle == ArtStyle.Realistic
|
|
||||||
|| settings.ArtStyle == ArtStyle.SemiRealistic
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
|
case ArtStyle.Realistic:
|
||||||
|
case ArtStyle.SemiRealistic:
|
||||||
negativePrompt.Append("cartoon, anime, painting, drawing, illustration, ");
|
negativePrompt.Append("cartoon, anime, painting, drawing, illustration, ");
|
||||||
}
|
break;
|
||||||
else if (settings.ArtStyle == ArtStyle.Anime)
|
case ArtStyle.Anime:
|
||||||
{
|
|
||||||
negativePrompt.Append("realistic, photo, photography, 3d, ");
|
negativePrompt.Append("realistic, photo, photography, 3d, ");
|
||||||
|
break;
|
||||||
|
case ArtStyle.None:
|
||||||
|
// Без дополнительных негативных промптов для стиля None
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пользовательский негативный промпт
|
// Пользовательский негативный промпт
|
||||||
@@ -244,7 +234,7 @@ namespace AIImages.Services
|
|||||||
|
|
||||||
private string GetHairDescription(PawnAppearanceData data)
|
private string GetHairDescription(PawnAppearanceData data)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(data.HairStyle))
|
if (string.IsNullOrEmpty(data.HairDefName))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
StringBuilder hair = new StringBuilder();
|
StringBuilder hair = new StringBuilder();
|
||||||
@@ -254,10 +244,8 @@ namespace AIImages.Services
|
|||||||
hair.Append(hairColor);
|
hair.Append(hairColor);
|
||||||
hair.Append(" ");
|
hair.Append(" ");
|
||||||
|
|
||||||
// Стиль прически (упрощаем сложные названия)
|
// Стиль прически - используем DefName для английского названия
|
||||||
string style = data
|
string style = CleanDefName(data.HairDefName)
|
||||||
.HairStyle.ToLower()
|
|
||||||
.Replace("_", " ")
|
|
||||||
.Replace("shaved", "very short")
|
.Replace("shaved", "very short")
|
||||||
.Replace("mohawk", "mohawk hairstyle");
|
.Replace("mohawk", "mohawk hairstyle");
|
||||||
|
|
||||||
@@ -310,15 +298,15 @@ namespace AIImages.Services
|
|||||||
itemDesc.Append(" ");
|
itemDesc.Append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Материал
|
// Материал - используем DefName для английского названия
|
||||||
if (!string.IsNullOrEmpty(item.Material))
|
if (!string.IsNullOrEmpty(item.MaterialDefName))
|
||||||
{
|
{
|
||||||
itemDesc.Append(item.Material.ToLower());
|
itemDesc.Append(CleanDefName(item.MaterialDefName));
|
||||||
itemDesc.Append(" ");
|
itemDesc.Append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Название предмета
|
// Название предмета - используем DefName для английского названия
|
||||||
itemDesc.Append(item.Label.ToLower());
|
itemDesc.Append(CleanDefName(item.DefName));
|
||||||
|
|
||||||
items.Add(itemDesc.ToString());
|
items.Add(itemDesc.ToString());
|
||||||
}
|
}
|
||||||
@@ -332,22 +320,56 @@ namespace AIImages.Services
|
|||||||
{
|
{
|
||||||
var baseTags = "highly detailed, professional, masterpiece, best quality";
|
var baseTags = "highly detailed, professional, masterpiece, best quality";
|
||||||
|
|
||||||
if (style == ArtStyle.Realistic || style == ArtStyle.SemiRealistic)
|
switch (style)
|
||||||
{
|
{
|
||||||
|
case ArtStyle.None:
|
||||||
|
return baseTags;
|
||||||
|
case ArtStyle.Realistic:
|
||||||
|
case ArtStyle.SemiRealistic:
|
||||||
return $"{baseTags}, professional photography, 8k uhd, dslr, high quality, sharp focus";
|
return $"{baseTags}, professional photography, 8k uhd, dslr, high quality, sharp focus";
|
||||||
}
|
case ArtStyle.Anime:
|
||||||
else if (style == ArtStyle.Anime)
|
|
||||||
{
|
|
||||||
return $"{baseTags}, anime masterpiece, high resolution, vibrant colors";
|
return $"{baseTags}, anime masterpiece, high resolution, vibrant colors";
|
||||||
}
|
case ArtStyle.ConceptArt:
|
||||||
else if (style == ArtStyle.ConceptArt)
|
|
||||||
{
|
|
||||||
return $"{baseTags}, trending on artstation, professional digital art";
|
return $"{baseTags}, trending on artstation, professional digital art";
|
||||||
}
|
default:
|
||||||
else
|
|
||||||
{
|
|
||||||
return baseTags;
|
return baseTags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Преобразует defName в читаемый английский текст для промпта
|
||||||
|
/// Пример: "Apparel_Pants" -> "pants", "Synthread" -> "synthread"
|
||||||
|
/// </summary>
|
||||||
|
private string CleanDefName(string defName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(defName))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
string cleaned = defName;
|
||||||
|
|
||||||
|
// Убираем распространенные префиксы RimWorld
|
||||||
|
cleaned = System.Text.RegularExpressions.Regex.Replace(
|
||||||
|
cleaned,
|
||||||
|
"^(Apparel_|Armor_|Weapon_|Thing_)",
|
||||||
|
"",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
// Разделяем CamelCase на слова
|
||||||
|
cleaned = System.Text.RegularExpressions.Regex.Replace(
|
||||||
|
cleaned,
|
||||||
|
"([a-z])([A-Z])",
|
||||||
|
"$1 $2"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Заменяем подчеркивания на пробелы
|
||||||
|
cleaned = cleaned.Replace("_", " ");
|
||||||
|
|
||||||
|
// Убираем множественные пробелы
|
||||||
|
cleaned = System.Text.RegularExpressions.Regex.Replace(cleaned, @"\s+", " ");
|
||||||
|
|
||||||
|
// Приводим к нижнему регистру и убираем лишние пробелы
|
||||||
|
return cleaned.Trim().ToLower();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,5 +28,10 @@ namespace AIImages.Services
|
|||||||
/// Получает список доступных сэмплеров
|
/// Получает список доступных сэмплеров
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<List<string>> GetAvailableSamplers(string apiEndpoint);
|
Task<List<string>> GetAvailableSamplers(string apiEndpoint);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает список доступных schedulers
|
||||||
|
/// </summary>
|
||||||
|
Task<List<string>> GetAvailableSchedulers(string apiEndpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace AIImages.Services
|
|||||||
BodyType = pawn.story.bodyType?.defName,
|
BodyType = pawn.story.bodyType?.defName,
|
||||||
SkinColor = pawn.story.SkinColor,
|
SkinColor = pawn.story.SkinColor,
|
||||||
HairStyle = pawn.story.hairDef?.label,
|
HairStyle = pawn.story.hairDef?.label,
|
||||||
|
HairDefName = pawn.story.hairDef?.defName,
|
||||||
HairColor = pawn.story.HairColor,
|
HairColor = pawn.story.HairColor,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,7 +43,9 @@ namespace AIImages.Services
|
|||||||
var apparelData = new ApparelData
|
var apparelData = new ApparelData
|
||||||
{
|
{
|
||||||
Label = apparel.def.label,
|
Label = apparel.def.label,
|
||||||
|
DefName = apparel.def.defName,
|
||||||
Material = apparel.Stuff?.label,
|
Material = apparel.Stuff?.label,
|
||||||
|
MaterialDefName = apparel.Stuff?.defName,
|
||||||
Color = apparel.DrawColor,
|
Color = apparel.DrawColor,
|
||||||
LayerType = apparel.def.apparel?.LastLayer.ToString(),
|
LayerType = apparel.def.apparel?.LastLayer.ToString(),
|
||||||
Durability = apparel.HitPoints,
|
Durability = apparel.HitPoints,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ namespace AIImages.Services
|
|||||||
width = request.Width,
|
width = request.Width,
|
||||||
height = request.Height,
|
height = request.Height,
|
||||||
sampler_name = request.Sampler,
|
sampler_name = request.Sampler,
|
||||||
|
scheduler = request.Scheduler,
|
||||||
seed = request.Seed,
|
seed = request.Seed,
|
||||||
save_images = false,
|
save_images = false,
|
||||||
send_images = true,
|
send_images = true,
|
||||||
@@ -186,6 +187,38 @@ namespace AIImages.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<string>> GetAvailableSchedulers(string apiEndpoint)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string endpoint = $"{apiEndpoint}/sdapi/v1/schedulers";
|
||||||
|
HttpResponseMessage response = await httpClient.GetAsync(endpoint);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return GetDefaultSchedulers();
|
||||||
|
|
||||||
|
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
var schedulers = JsonConvert.DeserializeObject<List<SdScheduler>>(jsonResponse);
|
||||||
|
|
||||||
|
var schedulerNames = new List<string>();
|
||||||
|
if (schedulers != null)
|
||||||
|
{
|
||||||
|
foreach (var scheduler in schedulers)
|
||||||
|
{
|
||||||
|
schedulerNames.Add(scheduler.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Message($"[AI Images] Found {schedulerNames.Count} schedulers");
|
||||||
|
return schedulerNames;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning($"[AI Images] Failed to load schedulers: {ex.Message}");
|
||||||
|
return GetDefaultSchedulers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<string> GetDefaultSamplers()
|
private List<string> GetDefaultSamplers()
|
||||||
{
|
{
|
||||||
return new List<string>
|
return new List<string>
|
||||||
@@ -212,6 +245,19 @@ namespace AIImages.Services
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<string> GetDefaultSchedulers()
|
||||||
|
{
|
||||||
|
return new List<string>
|
||||||
|
{
|
||||||
|
"Automatic",
|
||||||
|
"Uniform",
|
||||||
|
"Karras",
|
||||||
|
"Exponential",
|
||||||
|
"Polyexponential",
|
||||||
|
"SGM Uniform",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Вспомогательные классы для десериализации JSON ответов
|
// Вспомогательные классы для десериализации JSON ответов
|
||||||
#pragma warning disable S3459, S1144 // Properties set by JSON deserializer
|
#pragma warning disable S3459, S1144 // Properties set by JSON deserializer
|
||||||
private sealed class Txt2ImgResponse
|
private sealed class Txt2ImgResponse
|
||||||
@@ -229,6 +275,11 @@ namespace AIImages.Services
|
|||||||
{
|
{
|
||||||
public string name { get; set; }
|
public string name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class SdScheduler
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
#pragma warning restore S3459, S1144
|
#pragma warning restore S3459, S1144
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using AIImages.Models;
|
using AIImages.Models;
|
||||||
using Verse;
|
using Verse;
|
||||||
|
|
||||||
@@ -13,6 +14,17 @@ namespace AIImages.Settings
|
|||||||
public string apiEndpoint = "http://127.0.0.1:7860";
|
public string apiEndpoint = "http://127.0.0.1:7860";
|
||||||
public string selectedModel = "";
|
public string selectedModel = "";
|
||||||
public string selectedSampler = "Euler a";
|
public string selectedSampler = "Euler a";
|
||||||
|
public string selectedScheduler = "Automatic";
|
||||||
|
|
||||||
|
// Кэшированные списки из API (не сохраняются)
|
||||||
|
[Unsaved]
|
||||||
|
public List<string> availableModels = new List<string>();
|
||||||
|
|
||||||
|
[Unsaved]
|
||||||
|
public List<string> availableSamplers = new List<string>();
|
||||||
|
|
||||||
|
[Unsaved]
|
||||||
|
public List<string> availableSchedulers = new List<string>();
|
||||||
|
|
||||||
// Настройки генерации
|
// Настройки генерации
|
||||||
public int steps = 30;
|
public int steps = 30;
|
||||||
@@ -28,7 +40,6 @@ namespace AIImages.Settings
|
|||||||
|
|
||||||
// Художественный стиль
|
// Художественный стиль
|
||||||
public ArtStyle artStyle = ArtStyle.Realistic;
|
public ArtStyle artStyle = ArtStyle.Realistic;
|
||||||
public ShotType shotType = ShotType.Portrait;
|
|
||||||
|
|
||||||
// Путь для сохранения
|
// Путь для сохранения
|
||||||
public string savePath = "AIImages/Generated";
|
public string savePath = "AIImages/Generated";
|
||||||
@@ -43,6 +54,7 @@ namespace AIImages.Settings
|
|||||||
Scribe_Values.Look(ref apiEndpoint, "apiEndpoint", "http://127.0.0.1:7860");
|
Scribe_Values.Look(ref apiEndpoint, "apiEndpoint", "http://127.0.0.1:7860");
|
||||||
Scribe_Values.Look(ref selectedModel, "selectedModel", "");
|
Scribe_Values.Look(ref selectedModel, "selectedModel", "");
|
||||||
Scribe_Values.Look(ref selectedSampler, "selectedSampler", "Euler a");
|
Scribe_Values.Look(ref selectedSampler, "selectedSampler", "Euler a");
|
||||||
|
Scribe_Values.Look(ref selectedScheduler, "selectedScheduler", "Automatic");
|
||||||
|
|
||||||
Scribe_Values.Look(ref steps, "steps", 30);
|
Scribe_Values.Look(ref steps, "steps", 30);
|
||||||
Scribe_Values.Look(ref cfgScale, "cfgScale", 7.5f);
|
Scribe_Values.Look(ref cfgScale, "cfgScale", 7.5f);
|
||||||
@@ -58,7 +70,6 @@ namespace AIImages.Settings
|
|||||||
);
|
);
|
||||||
|
|
||||||
Scribe_Values.Look(ref artStyle, "artStyle", ArtStyle.Realistic);
|
Scribe_Values.Look(ref artStyle, "artStyle", ArtStyle.Realistic);
|
||||||
Scribe_Values.Look(ref shotType, "shotType", ShotType.Portrait);
|
|
||||||
|
|
||||||
Scribe_Values.Look(ref savePath, "savePath", "AIImages/Generated");
|
Scribe_Values.Look(ref savePath, "savePath", "AIImages/Generated");
|
||||||
|
|
||||||
@@ -81,10 +92,10 @@ namespace AIImages.Settings
|
|||||||
Width = width,
|
Width = width,
|
||||||
Height = height,
|
Height = height,
|
||||||
Sampler = selectedSampler,
|
Sampler = selectedSampler,
|
||||||
|
Scheduler = selectedScheduler,
|
||||||
Seed = seed,
|
Seed = seed,
|
||||||
Model = selectedModel,
|
Model = selectedModel,
|
||||||
ArtStyle = artStyle,
|
ArtStyle = artStyle,
|
||||||
ShotType = shotType,
|
|
||||||
PositivePrompt = basePositivePrompt,
|
PositivePrompt = basePositivePrompt,
|
||||||
NegativePrompt = baseNegativePrompt,
|
NegativePrompt = baseNegativePrompt,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,13 +21,7 @@ namespace AIImages
|
|||||||
|
|
||||||
public static void DoSettingsWindowContents(Rect inRect, AIImagesModSettings settings)
|
public static void DoSettingsWindowContents(Rect inRect, AIImagesModSettings settings)
|
||||||
{
|
{
|
||||||
// Инициализируем буферы при первом вызове
|
InitializeBuffers(settings);
|
||||||
if (string.IsNullOrEmpty(stepsBuffer))
|
|
||||||
{
|
|
||||||
stepsBuffer = settings.steps.ToString();
|
|
||||||
widthBuffer = settings.width.ToString();
|
|
||||||
heightBuffer = settings.height.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
Listing_Standard listingStandard = new Listing_Standard();
|
Listing_Standard listingStandard = new Listing_Standard();
|
||||||
Rect viewRect = new Rect(0f, 0f, inRect.width - 20f, 1200f);
|
Rect viewRect = new Rect(0f, 0f, inRect.width - 20f, 1200f);
|
||||||
@@ -35,7 +29,31 @@ namespace AIImages
|
|||||||
Widgets.BeginScrollView(inRect, ref scrollPosition, viewRect);
|
Widgets.BeginScrollView(inRect, ref scrollPosition, viewRect);
|
||||||
listingStandard.Begin(viewRect);
|
listingStandard.Begin(viewRect);
|
||||||
|
|
||||||
// === API Settings ===
|
DrawApiSettings(listingStandard, settings);
|
||||||
|
DrawGenerationSettings(listingStandard, settings);
|
||||||
|
DrawSamplerSchedulerSettings(listingStandard, settings);
|
||||||
|
DrawPromptsSettings(listingStandard, settings);
|
||||||
|
DrawOptionsSettings(listingStandard, settings);
|
||||||
|
|
||||||
|
listingStandard.End();
|
||||||
|
Widgets.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InitializeBuffers(AIImagesModSettings settings)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(stepsBuffer))
|
||||||
|
{
|
||||||
|
stepsBuffer = settings.steps.ToString();
|
||||||
|
widthBuffer = settings.width.ToString();
|
||||||
|
heightBuffer = settings.height.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawApiSettings(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
listingStandard.Label(
|
listingStandard.Label(
|
||||||
"AIImages.Settings.ApiSection".Translate(),
|
"AIImages.Settings.ApiSection".Translate(),
|
||||||
-1f,
|
-1f,
|
||||||
@@ -47,21 +65,66 @@ namespace AIImages
|
|||||||
settings.apiEndpoint = listingStandard.TextEntry(settings.apiEndpoint);
|
settings.apiEndpoint = listingStandard.TextEntry(settings.apiEndpoint);
|
||||||
listingStandard.Gap(8f);
|
listingStandard.Gap(8f);
|
||||||
|
|
||||||
// Кнопка проверки подключения
|
|
||||||
if (listingStandard.ButtonText("AIImages.Settings.TestConnection".Translate()))
|
if (listingStandard.ButtonText("AIImages.Settings.TestConnection".Translate()))
|
||||||
{
|
{
|
||||||
_ = TestApiConnection(settings.apiEndpoint);
|
_ = TestApiConnection(settings.apiEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кнопка загрузки моделей
|
|
||||||
if (listingStandard.ButtonText("AIImages.Settings.LoadModels".Translate()))
|
if (listingStandard.ButtonText("AIImages.Settings.LoadModels".Translate()))
|
||||||
{
|
{
|
||||||
_ = LoadModelsFromApi(settings);
|
_ = LoadModelsFromApi(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
listingStandard.Gap(12f);
|
DrawModelDropdown(listingStandard, settings);
|
||||||
|
|
||||||
// === Generation Settings ===
|
if (listingStandard.ButtonText("AIImages.Settings.LoadSamplersSchedulers".Translate()))
|
||||||
|
{
|
||||||
|
_ = LoadSamplersAndSchedulers(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
listingStandard.Gap(12f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawModelDropdown(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
listingStandard.ButtonTextLabeled(
|
||||||
|
"AIImages.Settings.Model".Translate(),
|
||||||
|
string.IsNullOrEmpty(settings.selectedModel)
|
||||||
|
? "AIImages.Settings.NoModelSelected".Translate()
|
||||||
|
: settings.selectedModel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
List<FloatMenuOption> modelOptions = new List<FloatMenuOption>();
|
||||||
|
if (!settings.availableModels.Any())
|
||||||
|
{
|
||||||
|
modelOptions.Add(
|
||||||
|
new FloatMenuOption("AIImages.Settings.LoadModelsFirst".Translate(), null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (string model in settings.availableModels)
|
||||||
|
{
|
||||||
|
string localModel = model;
|
||||||
|
modelOptions.Add(
|
||||||
|
new FloatMenuOption(model, () => settings.selectedModel = localModel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Find.WindowStack.Add(new FloatMenu(modelOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawGenerationSettings(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
listingStandard.Label(
|
listingStandard.Label(
|
||||||
"AIImages.Settings.GenerationSection".Translate(),
|
"AIImages.Settings.GenerationSection".Translate(),
|
||||||
-1f,
|
-1f,
|
||||||
@@ -69,7 +132,28 @@ namespace AIImages
|
|||||||
);
|
);
|
||||||
listingStandard.GapLine();
|
listingStandard.GapLine();
|
||||||
|
|
||||||
// Art Style
|
DrawArtStyleDropdown(listingStandard, settings);
|
||||||
|
listingStandard.Gap(8f);
|
||||||
|
|
||||||
|
listingStandard.Label("AIImages.Settings.Steps".Translate() + $": {settings.steps}");
|
||||||
|
settings.steps = (int)listingStandard.Slider(settings.steps, 1, 150);
|
||||||
|
listingStandard.Gap(8f);
|
||||||
|
|
||||||
|
listingStandard.Label(
|
||||||
|
"AIImages.Settings.CfgScale".Translate() + $": {settings.cfgScale:F1}"
|
||||||
|
);
|
||||||
|
settings.cfgScale = listingStandard.Slider(settings.cfgScale, 1f, 30f);
|
||||||
|
listingStandard.Gap(8f);
|
||||||
|
|
||||||
|
DrawSizeSettings(listingStandard, settings);
|
||||||
|
listingStandard.Gap(12f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawArtStyleDropdown(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
if (
|
if (
|
||||||
listingStandard.ButtonTextLabeled(
|
listingStandard.ButtonTextLabeled(
|
||||||
"AIImages.Settings.ArtStyle".Translate(),
|
"AIImages.Settings.ArtStyle".Translate(),
|
||||||
@@ -87,41 +171,13 @@ namespace AIImages
|
|||||||
}
|
}
|
||||||
Find.WindowStack.Add(new FloatMenu(styleOptions));
|
Find.WindowStack.Add(new FloatMenu(styleOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shot Type
|
|
||||||
if (
|
|
||||||
listingStandard.ButtonTextLabeled(
|
|
||||||
"AIImages.Settings.ShotType".Translate(),
|
|
||||||
settings.shotType.ToString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
List<FloatMenuOption> shotOptions = new List<FloatMenuOption>();
|
|
||||||
foreach (ShotType shot in Enum.GetValues(typeof(ShotType)))
|
|
||||||
{
|
|
||||||
ShotType localShot = shot;
|
|
||||||
shotOptions.Add(
|
|
||||||
new FloatMenuOption(shot.ToString(), () => settings.shotType = localShot)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Find.WindowStack.Add(new FloatMenu(shotOptions));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listingStandard.Gap(8f);
|
private static void DrawSizeSettings(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
// Steps
|
AIImagesModSettings settings
|
||||||
listingStandard.Label("AIImages.Settings.Steps".Translate() + $": {settings.steps}");
|
)
|
||||||
settings.steps = (int)listingStandard.Slider(settings.steps, 1, 150);
|
{
|
||||||
listingStandard.Gap(8f);
|
|
||||||
|
|
||||||
// CFG Scale
|
|
||||||
listingStandard.Label(
|
|
||||||
"AIImages.Settings.CfgScale".Translate() + $": {settings.cfgScale:F1}"
|
|
||||||
);
|
|
||||||
settings.cfgScale = listingStandard.Slider(settings.cfgScale, 1f, 30f);
|
|
||||||
listingStandard.Gap(8f);
|
|
||||||
|
|
||||||
// Width
|
|
||||||
listingStandard.Label("AIImages.Settings.Width".Translate() + ":");
|
listingStandard.Label("AIImages.Settings.Width".Translate() + ":");
|
||||||
widthBuffer = listingStandard.TextEntry(widthBuffer);
|
widthBuffer = listingStandard.TextEntry(widthBuffer);
|
||||||
if (int.TryParse(widthBuffer, out int width))
|
if (int.TryParse(widthBuffer, out int width))
|
||||||
@@ -129,7 +185,6 @@ namespace AIImages
|
|||||||
settings.width = Mathf.Clamp(width, 64, 2048);
|
settings.width = Mathf.Clamp(width, 64, 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Height
|
|
||||||
listingStandard.Label("AIImages.Settings.Height".Translate() + ":");
|
listingStandard.Label("AIImages.Settings.Height".Translate() + ":");
|
||||||
heightBuffer = listingStandard.TextEntry(heightBuffer);
|
heightBuffer = listingStandard.TextEntry(heightBuffer);
|
||||||
if (int.TryParse(heightBuffer, out int height))
|
if (int.TryParse(heightBuffer, out int height))
|
||||||
@@ -137,41 +192,119 @@ namespace AIImages
|
|||||||
settings.height = Mathf.Clamp(height, 64, 2048);
|
settings.height = Mathf.Clamp(height, 64, 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common size presets
|
DrawSizePresets(listingStandard, settings);
|
||||||
listingStandard.Gap(4f);
|
|
||||||
Rect presetRect = listingStandard.GetRect(30f);
|
|
||||||
if (Widgets.ButtonText(new Rect(presetRect.x, presetRect.y, 80f, 30f), "512x512"))
|
|
||||||
{
|
|
||||||
settings.width = 512;
|
|
||||||
settings.height = 512;
|
|
||||||
widthBuffer = "512";
|
|
||||||
heightBuffer = "512";
|
|
||||||
}
|
}
|
||||||
if (Widgets.ButtonText(new Rect(presetRect.x + 85f, presetRect.y, 80f, 30f), "512x768"))
|
|
||||||
{
|
private static void DrawSizePresets(
|
||||||
settings.width = 512;
|
Listing_Standard listingStandard,
|
||||||
settings.height = 768;
|
AIImagesModSettings settings
|
||||||
widthBuffer = "512";
|
|
||||||
heightBuffer = "768";
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
Widgets.ButtonText(new Rect(presetRect.x + 170f, presetRect.y, 80f, 30f), "768x768")
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
settings.width = 768;
|
listingStandard.Gap(4f);
|
||||||
settings.height = 768;
|
Rect presetRect1 = listingStandard.GetRect(30f);
|
||||||
widthBuffer = "768";
|
DrawPresetButton(presetRect1, 0f, "512x512", 512, 512, settings);
|
||||||
heightBuffer = "768";
|
DrawPresetButton(presetRect1, 85f, "512x768", 512, 768, settings);
|
||||||
|
DrawPresetButton(presetRect1, 170f, "768x768", 768, 768, settings);
|
||||||
|
|
||||||
|
listingStandard.Gap(4f);
|
||||||
|
Rect presetRect2 = listingStandard.GetRect(30f);
|
||||||
|
DrawPresetButton(presetRect2, 0f, "896x1152", 896, 1152, settings, 90f);
|
||||||
|
DrawPresetButton(presetRect2, 95f, "1024x1024", 1024, 1024, settings, 90f);
|
||||||
}
|
}
|
||||||
|
|
||||||
listingStandard.Gap(12f);
|
private static void DrawPresetButton(
|
||||||
|
Rect rect,
|
||||||
|
float xOffset,
|
||||||
|
string label,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
AIImagesModSettings settings,
|
||||||
|
float buttonWidth = 80f
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (Widgets.ButtonText(new Rect(rect.x + xOffset, rect.y, buttonWidth, 30f), label))
|
||||||
|
{
|
||||||
|
settings.width = width;
|
||||||
|
settings.height = height;
|
||||||
|
widthBuffer = width.ToString();
|
||||||
|
heightBuffer = height.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sampler
|
private static void DrawSamplerSchedulerSettings(
|
||||||
listingStandard.Label("AIImages.Settings.Sampler".Translate() + ":");
|
Listing_Standard listingStandard,
|
||||||
settings.selectedSampler = listingStandard.TextEntry(settings.selectedSampler);
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DrawSamplerDropdown(listingStandard, settings);
|
||||||
|
DrawSchedulerDropdown(listingStandard, settings);
|
||||||
listingStandard.Gap(12f);
|
listingStandard.Gap(12f);
|
||||||
|
}
|
||||||
|
|
||||||
// === Prompts ===
|
private static void DrawSamplerDropdown(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
listingStandard.ButtonTextLabeled(
|
||||||
|
"AIImages.Settings.Sampler".Translate(),
|
||||||
|
settings.selectedSampler
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
List<FloatMenuOption> samplerOptions = new List<FloatMenuOption>();
|
||||||
|
var availableSamplers = settings.availableSamplers.Any()
|
||||||
|
? settings.availableSamplers
|
||||||
|
: new List<string> { settings.selectedSampler };
|
||||||
|
|
||||||
|
foreach (string sampler in availableSamplers)
|
||||||
|
{
|
||||||
|
string localSampler = sampler;
|
||||||
|
samplerOptions.Add(
|
||||||
|
new FloatMenuOption(sampler, () => settings.selectedSampler = localSampler)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Find.WindowStack.Add(new FloatMenu(samplerOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawSchedulerDropdown(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
listingStandard.ButtonTextLabeled(
|
||||||
|
"AIImages.Settings.Scheduler".Translate(),
|
||||||
|
settings.selectedScheduler
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
List<FloatMenuOption> schedulerOptions = new List<FloatMenuOption>();
|
||||||
|
var availableSchedulers = settings.availableSchedulers.Any()
|
||||||
|
? settings.availableSchedulers
|
||||||
|
: new List<string> { settings.selectedScheduler };
|
||||||
|
|
||||||
|
foreach (string scheduler in availableSchedulers)
|
||||||
|
{
|
||||||
|
string localScheduler = scheduler;
|
||||||
|
schedulerOptions.Add(
|
||||||
|
new FloatMenuOption(
|
||||||
|
scheduler,
|
||||||
|
() => settings.selectedScheduler = localScheduler
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Find.WindowStack.Add(new FloatMenu(schedulerOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawPromptsSettings(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
listingStandard.Label(
|
listingStandard.Label(
|
||||||
"AIImages.Settings.PromptsSection".Translate(),
|
"AIImages.Settings.PromptsSection".Translate(),
|
||||||
-1f,
|
-1f,
|
||||||
@@ -186,8 +319,13 @@ namespace AIImages
|
|||||||
listingStandard.Label("AIImages.Settings.BaseNegativePrompt".Translate() + ":");
|
listingStandard.Label("AIImages.Settings.BaseNegativePrompt".Translate() + ":");
|
||||||
settings.baseNegativePrompt = listingStandard.TextEntry(settings.baseNegativePrompt, 3);
|
settings.baseNegativePrompt = listingStandard.TextEntry(settings.baseNegativePrompt, 3);
|
||||||
listingStandard.Gap(12f);
|
listingStandard.Gap(12f);
|
||||||
|
}
|
||||||
|
|
||||||
// === Options ===
|
private static void DrawOptionsSettings(
|
||||||
|
Listing_Standard listingStandard,
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
listingStandard.Label("AIImages.Settings.OptionsSection".Translate());
|
listingStandard.Label("AIImages.Settings.OptionsSection".Translate());
|
||||||
listingStandard.GapLine();
|
listingStandard.GapLine();
|
||||||
|
|
||||||
@@ -206,12 +344,8 @@ namespace AIImages
|
|||||||
|
|
||||||
listingStandard.Gap(12f);
|
listingStandard.Gap(12f);
|
||||||
|
|
||||||
// Save path
|
|
||||||
listingStandard.Label("AIImages.Settings.SavePath".Translate() + ":");
|
listingStandard.Label("AIImages.Settings.SavePath".Translate() + ":");
|
||||||
settings.savePath = listingStandard.TextEntry(settings.savePath);
|
settings.savePath = listingStandard.TextEntry(settings.savePath);
|
||||||
|
|
||||||
listingStandard.End();
|
|
||||||
Widgets.EndScrollView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async System.Threading.Tasks.Task TestApiConnection(string endpoint)
|
private static async System.Threading.Tasks.Task TestApiConnection(string endpoint)
|
||||||
@@ -250,6 +384,7 @@ namespace AIImages
|
|||||||
{
|
{
|
||||||
Log.Message("[AI Images] Loading models from API...");
|
Log.Message("[AI Images] Loading models from API...");
|
||||||
var models = await AIImagesMod.ApiService.GetAvailableModels(settings.apiEndpoint);
|
var models = await AIImagesMod.ApiService.GetAvailableModels(settings.apiEndpoint);
|
||||||
|
settings.availableModels = models;
|
||||||
|
|
||||||
if (models.Count > 0)
|
if (models.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -258,8 +393,13 @@ namespace AIImages
|
|||||||
MessageTypeDefOf.PositiveEvent
|
MessageTypeDefOf.PositiveEvent
|
||||||
);
|
);
|
||||||
|
|
||||||
// Если модель не выбрана, выбираем первую
|
if (
|
||||||
if (string.IsNullOrEmpty(settings.selectedModel) && models.Count > 0)
|
(
|
||||||
|
string.IsNullOrEmpty(settings.selectedModel)
|
||||||
|
|| !models.Contains(settings.selectedModel)
|
||||||
|
)
|
||||||
|
&& models.Count > 0
|
||||||
|
)
|
||||||
{
|
{
|
||||||
settings.selectedModel = models[0];
|
settings.selectedModel = models[0];
|
||||||
}
|
}
|
||||||
@@ -280,5 +420,73 @@ namespace AIImages
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async System.Threading.Tasks.Task LoadSamplersAndSchedulers(
|
||||||
|
AIImagesModSettings settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Message("[AI Images] Loading samplers and schedulers from API...");
|
||||||
|
|
||||||
|
var samplers = await AIImagesMod.ApiService.GetAvailableSamplers(
|
||||||
|
settings.apiEndpoint
|
||||||
|
);
|
||||||
|
settings.availableSamplers = samplers;
|
||||||
|
|
||||||
|
var schedulers = await AIImagesMod.ApiService.GetAvailableSchedulers(
|
||||||
|
settings.apiEndpoint
|
||||||
|
);
|
||||||
|
settings.availableSchedulers = schedulers;
|
||||||
|
|
||||||
|
int totalCount = samplers.Count + schedulers.Count;
|
||||||
|
if (totalCount > 0)
|
||||||
|
{
|
||||||
|
Messages.Message(
|
||||||
|
"AIImages.Settings.SamplersSchedulersLoaded".Translate(
|
||||||
|
samplers.Count,
|
||||||
|
schedulers.Count
|
||||||
|
),
|
||||||
|
MessageTypeDefOf.PositiveEvent
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
string.IsNullOrEmpty(settings.selectedSampler)
|
||||||
|
|| !samplers.Contains(settings.selectedSampler)
|
||||||
|
)
|
||||||
|
&& samplers.Count > 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
settings.selectedSampler = samplers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
string.IsNullOrEmpty(settings.selectedScheduler)
|
||||||
|
|| !schedulers.Contains(settings.selectedScheduler)
|
||||||
|
)
|
||||||
|
&& schedulers.Count > 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
settings.selectedScheduler = schedulers[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Message(
|
||||||
|
"AIImages.Settings.NoSamplersSchedulersFound".Translate(),
|
||||||
|
MessageTypeDefOf.RejectInput
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Messages.Message(
|
||||||
|
$"Error loading samplers/schedulers: {ex.Message}",
|
||||||
|
MessageTypeDefOf.RejectInput
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ namespace AIImages
|
|||||||
Width = generationSettings.Width,
|
Width = generationSettings.Width,
|
||||||
Height = generationSettings.Height,
|
Height = generationSettings.Height,
|
||||||
Sampler = generationSettings.Sampler,
|
Sampler = generationSettings.Sampler,
|
||||||
|
Scheduler = generationSettings.Scheduler,
|
||||||
Seed = generationSettings.Seed,
|
Seed = generationSettings.Seed,
|
||||||
Model = AIImagesMod.Settings.apiEndpoint,
|
Model = AIImagesMod.Settings.apiEndpoint,
|
||||||
};
|
};
|
||||||
@@ -269,8 +270,8 @@ namespace AIImages
|
|||||||
{
|
{
|
||||||
float imageSize = Mathf.Min(rect.width, 400f);
|
float imageSize = Mathf.Min(rect.width, 400f);
|
||||||
Rect imageRect = new Rect(
|
Rect imageRect = new Rect(
|
||||||
(rect.width - imageSize) / 2f,
|
rect.x + (rect.width - imageSize) / 2f,
|
||||||
curY,
|
rect.y + curY,
|
||||||
imageSize,
|
imageSize,
|
||||||
imageSize
|
imageSize
|
||||||
);
|
);
|
||||||
@@ -282,8 +283,8 @@ namespace AIImages
|
|||||||
// Placeholder для изображения
|
// Placeholder для изображения
|
||||||
float placeholderSize = Mathf.Min(rect.width, 300f);
|
float placeholderSize = Mathf.Min(rect.width, 300f);
|
||||||
Rect placeholderRect = new Rect(
|
Rect placeholderRect = new Rect(
|
||||||
(rect.width - placeholderSize) / 2f,
|
rect.x + (rect.width - placeholderSize) / 2f,
|
||||||
curY,
|
rect.y + curY,
|
||||||
placeholderSize,
|
placeholderSize,
|
||||||
placeholderSize
|
placeholderSize
|
||||||
);
|
);
|
||||||
@@ -299,7 +300,10 @@ namespace AIImages
|
|||||||
{
|
{
|
||||||
Text.Font = GameFont.Small;
|
Text.Font = GameFont.Small;
|
||||||
float statusHeight = Text.CalcHeight(generationStatus, rect.width);
|
float statusHeight = Text.CalcHeight(generationStatus, rect.width);
|
||||||
Widgets.Label(new Rect(0f, curY, rect.width, statusHeight), generationStatus);
|
Widgets.Label(
|
||||||
|
new Rect(rect.x, rect.y + curY, rect.width, statusHeight),
|
||||||
|
generationStatus
|
||||||
|
);
|
||||||
curY += statusHeight + 10f;
|
curY += statusHeight + 10f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +311,7 @@ namespace AIImages
|
|||||||
Text.Font = GameFont.Small;
|
Text.Font = GameFont.Small;
|
||||||
if (
|
if (
|
||||||
Widgets.ButtonText(
|
Widgets.ButtonText(
|
||||||
new Rect(0f, curY, rect.width, 35f),
|
new Rect(rect.x, rect.y + curY, rect.width, 35f),
|
||||||
isGenerating
|
isGenerating
|
||||||
? "AIImages.Generation.Generating".Translate()
|
? "AIImages.Generation.Generating".Translate()
|
||||||
: "AIImages.Generation.Generate".Translate()
|
: "AIImages.Generation.Generate".Translate()
|
||||||
@@ -321,7 +325,7 @@ namespace AIImages
|
|||||||
// Промпт секция
|
// Промпт секция
|
||||||
Text.Font = GameFont.Medium;
|
Text.Font = GameFont.Medium;
|
||||||
Widgets.Label(
|
Widgets.Label(
|
||||||
new Rect(0f, curY, rect.width, 30f),
|
new Rect(rect.x, rect.y + curY, rect.width, 30f),
|
||||||
"AIImages.Prompt.SectionTitle".Translate()
|
"AIImages.Prompt.SectionTitle".Translate()
|
||||||
);
|
);
|
||||||
curY += 35f;
|
curY += 35f;
|
||||||
@@ -334,7 +338,7 @@ namespace AIImages
|
|||||||
);
|
);
|
||||||
|
|
||||||
float promptHeight = Mathf.Min(Text.CalcHeight(promptText, rect.width), 150f);
|
float promptHeight = Mathf.Min(Text.CalcHeight(promptText, rect.width), 150f);
|
||||||
Rect promptRect = new Rect(0f, curY, rect.width, promptHeight);
|
Rect promptRect = new Rect(rect.x, rect.y + curY, rect.width, promptHeight);
|
||||||
|
|
||||||
// Рисуем промпт в скроллируемой области если он длинный
|
// Рисуем промпт в скроллируемой области если он длинный
|
||||||
Widgets.DrawBoxSolid(promptRect, new Color(0.1f, 0.1f, 0.1f, 0.5f));
|
Widgets.DrawBoxSolid(promptRect, new Color(0.1f, 0.1f, 0.1f, 0.5f));
|
||||||
@@ -344,7 +348,7 @@ namespace AIImages
|
|||||||
// Кнопка копирования промпта
|
// Кнопка копирования промпта
|
||||||
if (
|
if (
|
||||||
Widgets.ButtonText(
|
Widgets.ButtonText(
|
||||||
new Rect(0f, curY, rect.width / 2f - 5f, 30f),
|
new Rect(rect.x, rect.y + curY, rect.width / 2f - 5f, 30f),
|
||||||
"AIImages.Prompt.CopyButton".Translate()
|
"AIImages.Prompt.CopyButton".Translate()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -356,7 +360,12 @@ namespace AIImages
|
|||||||
// Кнопка обновления данных
|
// Кнопка обновления данных
|
||||||
if (
|
if (
|
||||||
Widgets.ButtonText(
|
Widgets.ButtonText(
|
||||||
new Rect(rect.width / 2f + 5f, curY, rect.width / 2f - 5f, 30f),
|
new Rect(
|
||||||
|
rect.x + rect.width / 2f + 5f,
|
||||||
|
rect.y + curY,
|
||||||
|
rect.width / 2f - 5f,
|
||||||
|
30f
|
||||||
|
),
|
||||||
"AIImages.Window.Refresh".Translate()
|
"AIImages.Window.Refresh".Translate()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -370,7 +379,7 @@ namespace AIImages
|
|||||||
curY += 35f;
|
curY += 35f;
|
||||||
GUI.color = new Color(0f, 1f, 0f, copiedMessageTime / 2f);
|
GUI.color = new Color(0f, 1f, 0f, copiedMessageTime / 2f);
|
||||||
Widgets.Label(
|
Widgets.Label(
|
||||||
new Rect(0f, curY, rect.width, 25f),
|
new Rect(rect.x, rect.y + curY, rect.width, 25f),
|
||||||
"AIImages.Prompt.Copied".Translate()
|
"AIImages.Prompt.Copied".Translate()
|
||||||
);
|
);
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
|
|||||||
Reference in New Issue
Block a user