Compare commits

...

2 Commits

Author SHA1 Message Date
5f8a1ae535 Add helper methods 2025-04-18 16:47:08 +08:00
db271eedf7 Add more themes and styles 2025-04-18 11:59:55 +08:00
19 changed files with 608 additions and 3 deletions

54
cache/cache.go vendored
View File

@@ -311,3 +311,57 @@ func (c *PNCache) UpdateOptions(options CacheOptions) {
}
}
}
// GetAllItems 获取所有缓存项
func (c *PNCache) GetAllItems() map[CacheKey]*CacheItem {
c.Mutex.RLock()
defer c.Mutex.RUnlock()
// 创建一个副本避免直接暴露内部map
items := make(map[CacheKey]*CacheItem, len(c.Items))
// 从EvictionList获取所有缓存项
for e := c.EvictionList.Front(); e != nil; e = e.Next() {
item := e.Value.(*CacheItem)
// 查找对应的key
for k, v := range c.Items {
if v == e {
// 创建项的副本
itemCopy := *item
items[k] = &itemCopy
break
}
}
}
return items
}
// GetMonitor 获取缓存监控器
func (c *PNCache) GetMonitor() *Monitor {
c.Mutex.RLock()
defer c.Mutex.RUnlock()
return c.Monitor
}
// DeleteItem 删除指定的缓存项
func (c *PNCache) DeleteItem(key CacheKey) bool {
c.Mutex.Lock()
defer c.Mutex.Unlock()
// 查找对应的缓存项
element, found := c.Items[key]
if !found {
return false
}
// 从链表中移除
c.EvictionList.Remove(element)
// 从映射中删除
delete(c.Items, key)
return true
}

45
cache/monitor.go vendored
View File

@@ -99,6 +99,18 @@ func (m *Monitor) GetStats() CacheStats {
return m.stats
}
// GetSampleHistory 获取采样历史
func (m *Monitor) GetSampleHistory() []CacheStats {
m.mutex.RLock()
defer m.mutex.RUnlock()
// 创建一个副本
result := make([]CacheStats, len(m.sampleHistory))
copy(result, m.sampleHistory)
return result
}
// monitorRoutine 监控例程
func (m *Monitor) monitorRoutine() {
sampleTicker := time.NewTicker(m.options.SampleInterval)
@@ -128,8 +140,37 @@ func (m *Monitor) collectSample() {
hits, misses, hitRate := m.cache.Stats()
size := m.cache.Size()
// 估算内存使用量(简化计算,实际应用中可能需要更精确的方法)
memoryUsage := int64(size * 1024) // 假设每个缓存项平均占用1KB
// 估算内存使用量
var memoryUsage int64
// 获取所有缓存项
items := m.cache.GetAllItems()
for _, item := range items {
itemSize := int64(0)
// 计算SVG字符串占用的内存
if !item.IsCompressed {
itemSize += int64(len(item.SVG) * 2) // Go中字符串以UTF-16编码存储每个字符占2字节
}
// 计算压缩数据占用的内存
if item.IsCompressed {
itemSize += int64(len(item.Compressed))
}
// 添加对象结构体本身的大小估计
// CacheItem结构体的基本大小约为40字节2个时间戳、1个布尔值、2个引用
itemSize += 40
memoryUsage += itemSize
}
// 添加缓存管理结构的开销估计 (映射表、双向链表等)
// 每个map项约24字节开销每个链表节点约16字节
mapOverhead := int64(size * 24)
listOverhead := int64(size * 16)
memoryUsage += mapOverhead + listOverhead
// 创建新的统计样本
newStat := CacheStats{

View File

@@ -10,6 +10,7 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/landaiqing/go-pixelnebula/animation"
"github.com/landaiqing/go-pixelnebula/cache"
@@ -184,12 +185,22 @@ func (pn *PixelNebula) calcKey(hash []string, opts *PNOptions) [2]int {
// WithCache 设置缓存选项
func (pn *PixelNebula) WithCache(options cache.CacheOptions) *PixelNebula {
pn.cache = cache.NewCache(options)
// 确保启动监控器
if pn.cache != nil && options.Monitoring.Enabled && pn.cache.GetMonitor() == nil {
pn.cache.Monitor = cache.NewMonitor(pn.cache, options.Monitoring)
pn.cache.Monitor.Start()
}
return pn
}
// WithDefaultCache 设置默认缓存选项
func (pn *PixelNebula) WithDefaultCache() *PixelNebula {
pn.cache = cache.NewDefaultCache()
// 确保启动监控器
if pn.cache != nil && pn.cache.GetOptions().Monitoring.Enabled && pn.cache.GetMonitor() == nil {
pn.cache.Monitor = cache.NewMonitor(pn.cache, pn.cache.GetOptions().Monitoring)
pn.cache.Monitor.Start()
}
return pn
}
@@ -739,3 +750,171 @@ func (pn *PixelNebula) generateSVG(id string, sansEnv bool, opts *PNOptions) (sv
return svg, nil
}
// GetCacheStats 获取缓存统计信息
func (pn *PixelNebula) GetCacheStats() (size, hits, misses int, hitRate float64, enabled bool, maxSize int, expiration time.Duration, evictionType string) {
if pn.cache == nil {
log.Println("pixelnebula: 缓存未初始化请先调用WithCache或WithDefaultCache")
return 0, 0, 0, 0, false, 0, 0, ""
}
hits, misses, hitRate = pn.cache.Stats()
options := pn.cache.GetOptions()
return pn.cache.Size(), hits, misses, hitRate, options.Enabled, options.Size, options.Expiration, options.EvictionType
}
// CacheItemInfo 缓存项信息结构体
type CacheItemInfo struct {
Key cache.CacheKey
SVG string
Compressed []byte
IsCompressed bool
CreatedAt time.Time
LastUsed time.Time
}
// GetCacheItems 获取所有缓存项
func (pn *PixelNebula) GetCacheItems() []CacheItemInfo {
result := []CacheItemInfo{}
if pn.cache == nil {
log.Println("pixelnebula: 缓存未初始化请先调用WithCache或WithDefaultCache")
return result
}
// 获取内部缓存项
items := pn.cache.GetAllItems()
if len(items) == 0 {
log.Println("pixelnebula: 缓存中没有数据请先生成一些SVG")
}
for key, item := range items {
cacheItem := CacheItemInfo{
Key: key,
SVG: item.SVG,
Compressed: item.Compressed,
IsCompressed: item.IsCompressed,
CreatedAt: item.CreatedAt,
LastUsed: item.LastUsed,
}
result = append(result, cacheItem)
}
return result
}
// MonitorSampleInfo 监控样本信息
type MonitorSampleInfo struct {
Timestamp time.Time
Size int
Hits int
Misses int
HitRate float64
MemoryUsage int64
}
// GetMonitorStats 获取监控统计信息
func (pn *PixelNebula) GetMonitorStats() (enabled bool, sampleInterval, adjustInterval time.Duration,
targetHitRate float64, lastAdjusted time.Time, samples []MonitorSampleInfo) {
if pn.cache == nil {
log.Println("pixelnebula: 缓存未初始化请先调用WithCache或WithDefaultCache")
return false, 0, 0, 0, time.Time{}, nil
}
options := pn.cache.GetOptions().Monitoring
// 确保监控器已启用并初始化
if !options.Enabled {
log.Println("pixelnebula: 监控未启用请设置Monitoring.Enabled=true")
return options.Enabled, options.SampleInterval, options.AdjustInterval, options.TargetHitRate, time.Time{}, nil
}
if pn.cache.GetMonitor() == nil {
log.Println("pixelnebula: 监控器未初始化,正在初始化...")
pn.cache.Monitor = cache.NewMonitor(pn.cache, options)
pn.cache.Monitor.Start()
}
monitor := pn.cache.GetMonitor()
stats := monitor.GetStats()
sampleHistory := monitor.GetSampleHistory()
if len(sampleHistory) == 0 {
log.Println("pixelnebula: 监控样本为空,请等待采样完成")
}
// 转换样本历史
samplesInfo := make([]MonitorSampleInfo, 0, len(sampleHistory))
for _, sample := range sampleHistory {
sampleInfo := MonitorSampleInfo{
Timestamp: sample.LastAdjusted,
Size: sample.Size,
Hits: sample.Hits,
Misses: sample.Misses,
HitRate: sample.HitRate,
MemoryUsage: sample.MemoryUsage,
}
samplesInfo = append(samplesInfo, sampleInfo)
}
return options.Enabled, options.SampleInterval, options.AdjustInterval,
options.TargetHitRate, stats.LastAdjusted, samplesInfo
}
// DeleteCacheItem 删除指定的缓存项
func (pn *PixelNebula) DeleteCacheItem(key string) bool {
if pn.cache == nil {
log.Println("pixelnebula: 缓存未初始化请先调用WithCache或WithDefaultCache")
return false
}
// 解析key字符串格式为"id_sansEnv_theme_part"
parts := strings.Split(key, "_")
if len(parts) < 4 {
log.Printf("pixelnebula: 无效的key格式: %s应为'id_sansEnv_theme_part'", key)
return false
}
id := parts[0]
sansEnv := parts[1] == "true"
theme, err := strconv.Atoi(parts[2])
if err != nil {
log.Printf("pixelnebula: 解析theme失败: %v", err)
return false
}
part, err := strconv.Atoi(parts[3])
if err != nil {
log.Printf("pixelnebula: 解析part失败: %v", err)
return false
}
// 构造CacheKey
cacheKey := cache.CacheKey{
Id: id,
SansEnv: sansEnv,
Theme: theme,
Part: part,
}
result := pn.cache.DeleteItem(cacheKey)
if !result {
log.Printf("pixelnebula: 未找到缓存项: %s", key)
}
return result
}
// ClearCache 清空缓存
func (pn *PixelNebula) ClearCache() {
if pn.cache == nil {
log.Println("pixelnebula: 缓存未初始化请先调用WithCache或WithDefaultCache")
return
}
pn.cache.Clear()
log.Println("pixelnebula: 缓存已清空")
}

View File

@@ -144,7 +144,8 @@ func TestDemo(t *testing.T) {
pn := NewPixelNebula()
// 设置风格和尺寸
pn.WithStyle(style.GirlStyle)
pn.WithStyle(style.MechStyle)
pn.WithTheme(1)
pn.WithSize(231, 231)
// 生成 SVG 并保存到文件

14
style/cosmic_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// CosmicStyle 宇宙风格类型
const CosmicStyle StyleType = "cosmic"
// CosmicStyleShapes 宇宙风格形状集合
var CosmicStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l15.71 15.75h21z\" style=\"fill:#000;\"/><path d=\"m89.29 201a10 10 0 0 1 10-10 10 10 0 0 1 10 10 10 10 0 0 1-10 10 10 10 0 0 1-10-10zm45 7a5 5 0 0 1 5-5 5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5zm-25 10a3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3zm-30-5a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2zm70-10a4 4 0 0 1 4-4 4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4z\" style=\"fill:#8426c7;\"/>",
TypeMouth: "<path id='mouth' d=\"m104 147a12 12 0 0 0 24 0h-24z\" style=\"fill:#ae00ff;\"/>",
TypeEyes: "<path id='eyes' d=\"m75 95a10 10 0 0 1 10 10 10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10 10zm80 0a10 10 0 0 1 10 10 10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10 10z\" style=\"fill:#5500ff;\"/><path d=\"m75 95a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5 5zm80 0a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5 5z\" style=\"fill:#00ffff;\"/>",
TypeTop: "<path id='top' d=\"m115.5 30c-27.5 0-50 22.5-50 50h100c0-27.5-22.5-50-50-50z\" style=\"fill:#000;\"/><path d=\"m83 40a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm-10 15a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2zm65 0a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2zm-20-10a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5zm-70 25a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4zm100 5a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3z\" style=\"fill:#8426c7;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

14
style/ghost_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// GhostStyle 幽灵风格类型
const GhostStyle StyleType = "ghost"
// GhostStyleShapes 幽灵风格形状集合
var GhostStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5c0 0 12.21 15.75 26.25 15.75s26.25-15.75 26.25-15.75z\" style=\"fill:#fff;fill-opacity:0.3;\"/><path d=\"m84.29 205c5-10 9-15 15-20 5 5 9 10 13 20-3 5-8 8-13 8s-11-3-15-8z\" style=\"fill:#fff;fill-opacity:0.2;\"/>",
TypeMouth: "<path id='mouth' d=\"m115.5 145c-5 0-10 5-10 10 0-5 5-5 10-5s10 0 10 5c0-5-5-10-10-10z\" style=\"fill:#000;fill-opacity:0.3;\"/>",
TypeEyes: "<path id='eyes' d=\"m85 95c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10zm60 0c-5 0-10 5-10 10s5 10 10 10 10-5 10-10-5-10-10-10z\" style=\"fill:#000;fill-opacity:0.3;\"/>",
TypeTop: "<path id='top' d=\"m65.5 70c0-20 20-40 50-40s50 20 50 40c0 0-10-10-50-10s-50 10-50 10z\" style=\"fill:#fff;fill-opacity:0.3;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#fff;fill-opacity:0.5;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

View File

@@ -166,6 +166,24 @@ var defaultStyleSet = map[StyleType]StyleSet{
TypeHead: "<path id='head' d=\"m50 50h130v130h-130z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"m30 30h170v170h-170z\" style=\"fill:#01;\"/>",
},
// NeonStyle 风格
NeonStyle: NeonStyleShapes,
// PixelStyle 风格
PixelStyle: PixelStyleShapes,
// WatercolorStyle 风格
WatercolorStyle: WatercolorStyleShapes,
// MechStyle 风格
MechStyle: MechStyleShapes,
// CosmicStyle 风格
CosmicStyle: CosmicStyleShapes,
// GhostStyle 风格
GhostStyle: GhostStyleShapes,
}
// initShapes 初始化形状数据
@@ -188,6 +206,12 @@ func (m *Manager) initShapes() {
RastaStyle,
MetaStyle,
SquareStyle,
NeonStyle, // 霓虹风格
PixelStyle, // 像素风格
WatercolorStyle, // 水彩风格
MechStyle, // 机械风格
CosmicStyle, // 宇宙风格
GhostStyle, // 幽灵风格
} {
if styleSet, exists := defaultStyleSet[style]; exists {
m.AddStyleSet(styleSet)

14
style/mech_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// MechStyle 机械风格类型
const MechStyle StyleType = "mech"
// MechStyleShapes 机械风格形状集合
var MechStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l10 10h32.46l10-10z\" style=\"fill:#555;\"/><path d=\"m80 205h70v10h-70zm-10 10h90v5h-90zm5-15h10v5h-10zm60 0h10v5h-10zm-45 0h10v5h-10zm20 0h10v5h-10z\" style=\"fill:#777;\"/><path d=\"m115.5 205a5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5zm20 0a5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5zm-40 0a5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5z\" style=\"fill:#333;\"/>",
TypeMouth: "<path id='mouth' d=\"m95 140h40a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-40a5 5 0 0 1-5-5 5 5 0 0 1 5-5z\" style=\"fill:#222;\"/><path d=\"m100 140v10m30-10v10m-20-10v10m10-10v10\" style=\"fill:none;stroke:#555;stroke-width:1;\"/>",
TypeEyes: "<path id='eyes' d=\"m85 95a10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10 10 10 0 0 1 10 10zm80 0a10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10 10 10 0 0 1 10 10z\" style=\"fill:#333;\"/><path d=\"m77 95a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm8 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm72 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm8 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2z\" style=\"fill:#73e8ff;\"/>",
TypeTop: "<path id='top' d=\"m70 65c5-5 85-5 90 0 5 5 10 15 10 20h-110c0-5 5-15 10-20z\" style=\"fill:#444;\"/><path d=\"m75 65h10a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-10a5 5 0 0 1-5-5 5 5 0 0 1 5-5zm60 0h10a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-10a5 5 0 0 1-5-5 5 5 0 0 1 5-5z\" style=\"fill:#777;\"/><path d=\"m95 70h50a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-50a5 5 0 0 1-5-5 5 5 0 0 1 5-5z\" style=\"fill:#222;\"/><path d=\"m100 70v10m40-10v10m-30-10v10m20-10v10m-10-10v10\" style=\"fill:none;stroke:#555;stroke-width:1;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

14
style/neon_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// NeonStyle 霓虹风格类型
const NeonStyle StyleType = "neon"
// NeonStyleShapes 霓虹风格形状集合
var NeonStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l20.71 20.75h11z\" style=\"fill:#1e1e1e;\"/><path d=\"m118.35 208.83-11.66 11.66 16.9 6.64 16.9-6.64-11.66-11.66z\" style=\"fill:#5c5c5c;\"/><path d=\"m119.94 227a3.23 3.23 0 0 1-1.73-0.5l-16.9-6.64a3.25 3.25 0 0 1-1.35-4.4 3.25 3.25 0 0 1 0.44-0.65l9.99-10a3.24 3.24 0 0 1 4.59-0.08 3.24 3.24 0 0 1 0.08 4.59l-7.17 7.17 11.05 4.35 11.05-4.35-7.17-7.17a3.24 3.24 0 0 1 0.08-4.59 3.24 3.24 0 0 1 4.59 0.08l9.99 10a3.25 3.25 0 0 1-0.91 5.05l-16.9 6.64a3.23 3.23 0 0 1-1.73 0.5z\" style=\"fill:#00f2ff;\"/>",
TypeMouth: "<path id='mouth' d=\"m105.5 147.75a10 10 0 0 0 20 0z\" style=\"fill:#ff00d4;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px;stroke:#00f2ff;\"/>",
TypeEyes: "<path id='eyes' d=\"m94.5 104.75a10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10 10 10 0 0 1 10 10zm52 0a10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10 10 10 0 0 1 10 10z\" style=\"fill:#ff00d4;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;stroke:#00f2ff;\"/><path d=\"m83.5 104.75a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm10 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm42 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2zm10 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2z\" style=\"fill:#ffffff;\"/>",
TypeTop: "<path id='top' d=\"m115.5 47.75c-15.4 0-23.45 7.03-27.55 12.6-6.55 8.91-8.45 19.4-8.45 19.4h72c0 0-1.9-10.49-8.45-19.4-4.1-5.57-12.15-12.6-27.55-12.6z\" style=\"fill:#1e1e1e;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;stroke:#00f2ff;\"/><path d=\"m62.5 72.75 5 15m91-15-5 15\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px;stroke:#00f2ff;\"/><path d=\"m115.5 15.75v15m-20 5 5-15m35 15-5-15\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px;stroke:#ff00d4;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

14
style/pixel_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// PixelStyle 像素风格类型
const PixelStyle StyleType = "pixel"
// PixelStyleShapes 像素风格形状集合
var PixelStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m60 180h120v20h-120zm0 20h20v10h-20zm100 0h20v10h-20zm-80 0h20v10h-20zm20 0h20v10h-20zm20 0h20v10h-20z\" style=\"fill:#333;\"/>",
TypeMouth: "<path id='mouth' d=\"m100 150h30v5h-30zm5-5h20v5h-20z\" style=\"fill:#1a1a1a;\"/>",
TypeEyes: "<path id='eyes' d=\"m80 100h10v10h-10zm60 0h10v10h-10z\" style=\"fill:#1a1a1a;\"/>",
TypeTop: "<path id='top' d=\"m90 40h50v10h-50zm-10 10h70v10h-70zm-10 10h90v10h-90zm0 10h90v10h-90z\" style=\"fill:#333;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

View File

@@ -106,6 +106,12 @@ func (m *Manager) GetStyleIndex(style StyleType) (int, error) {
RastaStyle,
MetaStyle,
SquareStyle,
NeonStyle, // 霓虹风格
PixelStyle, // 像素风格
WatercolorStyle, // 水彩风格
MechStyle, // 机械风格
CosmicStyle, // 宇宙风格
GhostStyle, // 幽灵风格
} {
if s == style {
return i, nil

14
style/watercolor_style.go Normal file
View File

@@ -0,0 +1,14 @@
package style
// WatercolorStyle 水彩风格类型
const WatercolorStyle StyleType = "watercolor"
// WatercolorStyleShapes 水彩风格形状集合
var WatercolorStyleShapes = StyleSet{
TypeClo: "<path id='clo' d=\"m141.75 195c4.11 1.1 7.96 2.31 11.52 3.6 8.47 4.73 16.22 9.77 22.52 15.11 2.47 1.58 4.67 3.23 6.58 4.93-9.89 7.44-21.04 13.65-33.05 18.33-19.46 6.15-41.01 9.53-63.33 9.53-22.32 0-43.87-3.38-63.33-9.53-12.01-4.68-23.16-10.89-33.05-18.33 1.91-1.7 4.11-3.35 6.58-4.93 6.3-5.34 14.05-10.38 22.52-15.11 3.56-1.29 7.41-2.5 11.52-3.6 1.61 4.09 7.22 16.21 20.88 22.8 14.2 4.45 23.57 3.17 34.87 3.17 11.3 0 20.67 1.28 34.87-3.17 13.66-6.59 19.27-18.71 20.88-22.8z\" style=\"fill:#f7f7f7;stroke-width:1px;stroke:#ddd;\"/>",
TypeMouth: "<path id='mouth' d=\"m115.5 155c-5 0-10-2.5-10-5s5-5 10-5 10 2.5 10 5-5 5-10 5z\" style=\"fill:#f07;stroke-linecap:round;stroke-linejoin:round;stroke-width:1px;stroke:#f07;\"/>",
TypeEyes: "<path id='eyes' d=\"m85 105c-3.866 0-7-3.134-7-7s3.134-7 7-7 7 3.134 7 7-3.134 7-7 7zm60 0c-3.866 0-7-3.134-7-7s3.134-7 7-7 7 3.134 7 7-3.134 7-7 7z\" style=\"fill:#40a;stroke-linecap:round;stroke-linejoin:round;stroke-width:1px;stroke:#40a;\"/>",
TypeTop: "<path id='top' d=\"m65.5 60c-5 5-10 10-10 20 0 5 0 15 5 20 5-15 15-25 55-25s50 10 55 25c5-5 5-15 5-20 0-10-5-15-10-20-20-10-80-10-100 0z\" style=\"fill:#ddd;stroke-linecap:round;stroke-linejoin:round;stroke-width:1px;stroke:#ccc;\"/>",
TypeHead: "<path id='head' d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>",
TypeEnv: "<path id='env' d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>",
}

32
theme/cosmic_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// CosmicTheme 宇宙风格主题
var CosmicTheme = Theme{
// 第一部分 - 星空风格
ThemePart{
"env": {"000033"},
"clo": {"000000", "8426c7"},
"head": {"000000"},
"mouth": {"ae00ff"},
"eyes": {"5500ff", "00ffff"},
"top": {"000000", "8426c7"},
},
// 第二部分 - 银河风格
ThemePart{
"env": {"0a001a"},
"clo": {"000000", "00ffaa"},
"head": {"000000"},
"mouth": {"00ffaa"},
"eyes": {"00aaff", "ffffff"},
"top": {"000000", "00ffaa"},
},
// 第三部分 - 星云风格
ThemePart{
"env": {"330033"},
"clo": {"000000", "ff00ff"},
"head": {"000000"},
"mouth": {"ff00ff"},
"eyes": {"ff00aa", "ffffff"},
"top": {"000000", "ff00ff"},
},
}

32
theme/ghost_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// GhostTheme 幽灵风格主题
var GhostTheme = Theme{
// 第一部分 - 经典幽灵风格
ThemePart{
"env": {"1a1a1a"},
"clo": {"ffffff", "ffffff"},
"head": {"ffffff"},
"mouth": {"000000"},
"eyes": {"000000"},
"top": {"ffffff"},
},
// 第二部分 - 蓝色幽灵风格
ThemePart{
"env": {"000033"},
"clo": {"aaddff", "aaddff"},
"head": {"aaddff"},
"mouth": {"000000"},
"eyes": {"000000"},
"top": {"aaddff"},
},
// 第三部分 - 绿色幽灵风格
ThemePart{
"env": {"002211"},
"clo": {"aaeecc", "aaeecc"},
"head": {"aaeecc"},
"mouth": {"000000"},
"eyes": {"000000"},
"top": {"aaeecc"},
},
}

View File

@@ -484,6 +484,24 @@ var defaultThemeSet = map[style.StyleType]Theme{
"top": {"#00fffd", "none", "none", "none", "none"},
},
},
// 添加NeonStyle主题
style.NeonStyle: NeonTheme,
// 添加PixelStyle主题
style.PixelStyle: PixelTheme,
// 添加WatercolorStyle主题
style.WatercolorStyle: WatercolorTheme,
// 添加MechStyle主题
style.MechStyle: MechTheme,
// 添加CosmicStyle主题
style.CosmicStyle: CosmicTheme,
// 添加GhostStyle主题
style.GhostStyle: GhostTheme,
}
// initThemes 初始化主题数据
@@ -507,6 +525,12 @@ func (m *Manager) initThemes() {
style.RastaStyle,
style.MetaStyle,
style.SquareStyle,
style.NeonStyle, // 霓虹风格主题
style.PixelStyle, // 像素风格主题
style.WatercolorStyle, // 水彩风格主题
style.MechStyle, // 机械风格主题
style.CosmicStyle, // 宇宙风格主题
style.GhostStyle, // 幽灵风格主题
} {
if themeSet, exists := defaultThemeSet[theme]; exists {
m.AddTheme(themeSet)

32
theme/mech_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// MechTheme 机械风格主题
var MechTheme = Theme{
// 第一部分 - 钢铁机械风格
ThemePart{
"env": {"333333"},
"clo": {"555555", "777777", "333333"},
"head": {"888888"},
"mouth": {"222222", "555555"},
"eyes": {"333333", "73e8ff"},
"top": {"444444", "777777", "222222", "555555"},
},
// 第二部分 - 铜色机械风格
ThemePart{
"env": {"251607"},
"clo": {"805723", "ab752f", "553211"},
"head": {"a87e45"},
"mouth": {"302013", "604020"},
"eyes": {"553211", "73e8ff"},
"top": {"805723", "ab752f", "302013", "604020"},
},
// 第三部分 - 未来机械风格
ThemePart{
"env": {"002244"},
"clo": {"1a1a1a", "333333", "007acc"},
"head": {"444444"},
"mouth": {"1a1a1a", "007acc"},
"eyes": {"1a1a1a", "00ffff"},
"top": {"1a1a1a", "333333", "007acc", "00aaff"},
},
}

32
theme/neon_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// NeonTheme 霓虹风格主题
var NeonTheme = Theme{
// 第一部分 - 赛博朋克风格
ThemePart{
"env": {"000000"},
"clo": {"1e1e1e", "5c5c5c", "00f2ff"},
"head": {"000000"},
"mouth": {"ff00d4", "00f2ff"},
"eyes": {"ff00d4", "00f2ff", "ffffff", "ffffff"},
"top": {"1e1e1e", "00f2ff", "ff00d4"},
},
// 第二部分 - 霓虹黄风格
ThemePart{
"env": {"0d0030"},
"clo": {"1e1e1e", "5c5c5c", "ffdd00"},
"head": {"000000"},
"mouth": {"ffdd00", "00f2ff"},
"eyes": {"ffdd00", "00f2ff", "ffffff", "ffffff"},
"top": {"1e1e1e", "00f2ff", "ffdd00"},
},
// 第三部分 - 霓虹绿风格
ThemePart{
"env": {"100a00"},
"clo": {"1e1e1e", "5c5c5c", "0cff6f"},
"head": {"000000"},
"mouth": {"0cff6f", "ff00d4"},
"eyes": {"0cff6f", "ff00d4", "ffffff", "ffffff"},
"top": {"1e1e1e", "ff00d4", "0cff6f"},
},
}

32
theme/pixel_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// PixelTheme 像素风格主题
var PixelTheme = Theme{
// 第一部分 - 经典像素风格
ThemePart{
"env": {"333333"},
"clo": {"3F51B5"},
"head": {"ffcc99"},
"mouth": {"cc0000"},
"eyes": {"000000"},
"top": {"663300"},
},
// 第二部分 - 像素游戏风格
ThemePart{
"env": {"4CAF50"},
"clo": {"607D8B"},
"head": {"ffbb77"},
"mouth": {"333333"},
"eyes": {"333333"},
"top": {"ff9900"},
},
// 第三部分 - 复古像素风格
ThemePart{
"env": {"C62828"},
"clo": {"9C27B0"},
"head": {"CDDC39"},
"mouth": {"000000"},
"eyes": {"000000"},
"top": {"FF5722"},
},
}

32
theme/watercolor_theme.go Normal file
View File

@@ -0,0 +1,32 @@
package theme
// WatercolorTheme 水彩风格主题
var WatercolorTheme = Theme{
// 第一部分 - 粉色水彩风格
ThemePart{
"env": {"ffeeff"},
"clo": {"f7f7f7", "dddddd"},
"head": {"fff0e8"},
"mouth": {"ff5599", "ff5599"},
"eyes": {"5544aa", "5544aa"},
"top": {"dddddd", "cccccc"},
},
// 第二部分 - 蓝色水彩风格
ThemePart{
"env": {"eeffff"},
"clo": {"f7f7f7", "dddddd"},
"head": {"fff0f0"},
"mouth": {"00aabb", "00aabb"},
"eyes": {"3355dd", "3355dd"},
"top": {"ccddff", "bbccee"},
},
// 第三部分 - 绿色水彩风格
ThemePart{
"env": {"eeffee"},
"clo": {"f7f7f7", "dddddd"},
"head": {"fffcf0"},
"mouth": {"66aa66", "66aa66"},
"eyes": {"006600", "006600"},
"top": {"ddffdd", "cceecc"},
},
}