From 899e2cea97b486f6bedc4fa65df093d18f1c3069 Mon Sep 17 00:00:00 2001 From: dindang Date: Mon, 27 Jul 2026 16:13:52 +0800 Subject: [PATCH] =?UTF-8?q?case=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/floor_handler.go | 93 +++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 10 deletions(-) diff --git a/internal/handler/floor_handler.go b/internal/handler/floor_handler.go index ad8d8d0..c2b6f99 100644 --- a/internal/handler/floor_handler.go +++ b/internal/handler/floor_handler.go @@ -9,6 +9,8 @@ import ( "net/http" "os" "path/filepath" + "regexp" + "strconv" "strings" "time" @@ -50,8 +52,8 @@ type RoomOption struct { var woodPatterns = []PatternOption{ {Code: 1, Name: "直铺(工字拼)Straight Lay", Category: "wood", Pattern: "planks running from bottom-left toward top-right of the image, joints staggered randomly, at least 6 inch offset between rows"}, {Code: 2, Name: "横铺 Horizontal Lay", Category: "wood", Pattern: "planks running horizontally left to right, parallel to the bottom edge of the image, joints staggered randomly, at least 6 inch offset"}, - {Code: 3, Name: "人字铺 Herringbone", Category: "wood", Pattern: "ALL planks must be IDENTICAL width - no piece may appear narrower. Planks at 90 degrees forming continuous zigzag V-lines from bottom-left to top-right of the image. At far distance, planks remain distinct with visible seams - NOT merging into a solid mass."}, - {Code: 4, Name: "鱼骨铺 Chevron", Category: "wood", Pattern: "planks with 45 degree mitered ends forming continuous V-points aligned from bottom-left to top-right of the image"}, + {Code: 3, Name: "人字拼 Herringbone", Category: "wood", Pattern: "ALL planks must be IDENTICAL width - no piece may appear narrower. Planks at 90° forming continuous zigzag V-lines from bottom-left to top-right of the image. At far distance, planks remain distinct with visible seams - NOT merging into a solid mass."}, + {Code: 4, Name: "鱼骨拼 Chevron", Category: "wood", Pattern: "planks with 45° mitered ends forming continuous V-points aligned from bottom-left to top-right of the image"}, } var tilePatterns = []PatternOption{ @@ -59,7 +61,7 @@ var tilePatterns = []PatternOption{ {Code: 6, Name: "工字铺(1/2错缝)Running Bond", Category: "tile", Pattern: "rows parallel to bottom edge of image, each row offset 50% from previous, horizontal grout lines continuous, vertical grout lines staggered"}, {Code: 7, Name: "三七错铺 1/3 Offset", Category: "tile", Pattern: "rows parallel to bottom edge of image, each row offset exactly 33% of tile length, every third row aligns"}, {Code: 8, Name: "六边形 Hexagonal", Category: "tile", Pattern: "six-sided tiles interlocked in honeycomb mesh, flat edges horizontal (parallel to bottom edge of image), grout follows hexagonal edges"}, - {Code: 9, Name: "斜铺(60度对角)Diagonal", Category: "tile", Pattern: "tiles rotated 60 degrees to the image frame, grout lines run diagonally at 60 and 150 degrees to the bottom edge, no lines parallel to image edges"}, + {Code: 9, Name: "斜铺(60度对角)Diagonal", Category: "tile", Pattern: "tiles rotated 60° to the image frame, grout lines run diagonally at 60 and 150 degrees to the bottom edge, no lines parallel to image edges"}, } var roomOptions = []RoomOption{ @@ -522,8 +524,8 @@ func buildCompactFloorPrompt(floor FloorOption, pattern PatternOption, room *Roo if floor.FloorDescription != "" { details = append(details, "目录材质描述:"+floor.FloorDescription) } - if floor.SizeLabel != "" || (floor.WidthIn > 0 && floor.LengthIn > 0) { - details = append(details, compactSizeInstruction(floor)) + if sizeRule := compactSizeInstruction(floor, pattern); sizeRule != "" { + details = append(details, sizeRule) } if floor.ImageURL != "" { details = append(details, "产品图是唯一材质参考:锁定基础颜色、纹理、纹路、光泽、材质类型;不要重新美化或改色。") @@ -544,14 +546,85 @@ func buildCompactFloorPrompt(floor FloorOption, pattern PatternOption, room *Roo ) } -func compactSizeInstruction(floor FloorOption) string { - if floor.SizeLabel != "" { - return "尺寸规则:使用所选尺寸 " + floor.SizeLabel + ";只调整板/砖大小和缝距,不改变材质颜色和纹理。" +func compactSizeInstruction(floor FloorOption, pattern PatternOption) string { + width, length, ok := floorDimensions(floor) + if !ok { + if pattern.Code == 8 { + return "尺寸规则:这是特殊形状砖,按产品图保持真实特殊形状和缝隙结构;不要改成方砖或普通长方形砖。" + } + if floor.SizeLabel != "" { + return "尺寸规则:使用所选尺寸 " + floor.SizeLabel + ";只调整板/砖大小和缝距,不改变材质颜色和纹理。" + } + return "" } + + shortSide, longSide := width, length + if shortSide > longSide { + shortSide, longSide = longSide, shortSide + } + ratio := longSide / shortSide + sizeText := formatDimensionPair(width, length) + + if ratio <= 1.08 { + return fmt.Sprintf("尺寸规则:每块砖必须是 %s 正方形,真实长宽比 %s。砖缝网格应形成接近 1:1 的方砖;只调整板/砖大小和缝距,不改变材质颜色和纹理。", sizeText, aspectRatioText(shortSide, longSide)) + } + + return fmt.Sprintf("尺寸规则:每块砖必须是 %s 长方形,真实长宽比 %s。近处可见的完整砖块也必须明显是长方形,长边约为短边的 %.2g 倍,不得变成接近正方形;砖缝网格必须形成同样比例的长方形砖块。只调整板/砖大小和缝距,不改变材质颜色和纹理。", sizeText, aspectRatioText(shortSide, longSide), ratio) +} + +func floorDimensions(floor FloorOption) (float64, float64, bool) { if floor.WidthIn > 0 && floor.LengthIn > 0 { - return fmt.Sprintf("尺寸规则:每片 %.1f\" x %.1f\";只调整板/砖大小和缝距,不改变材质颜色和纹理。", floor.WidthIn, floor.LengthIn) + return floor.WidthIn, floor.LengthIn, true } - return "" + + text := strings.Join([]string{floor.SizeLabel, floor.Name}, " ") + re := regexp.MustCompile(`(?i)(\d+(?:\.\d+)?)\s*(?:x|×|-|by)\s*(\d+(?:\.\d+)?)`) + match := re.FindStringSubmatch(text) + if len(match) != 3 { + return 0, 0, false + } + width, err1 := strconv.ParseFloat(match[1], 64) + length, err2 := strconv.ParseFloat(match[2], 64) + if err1 != nil || err2 != nil || width <= 0 || length <= 0 { + return 0, 0, false + } + return width, length, true +} + +func formatDimensionPair(width, length float64) string { + return fmt.Sprintf("%s x %s 英寸", formatDimension(width), formatDimension(length)) +} + +func formatDimension(v float64) string { + if v == float64(int(v)) { + return fmt.Sprintf("%d", int(v)) + } + return fmt.Sprintf("%.1f", v) +} + +func aspectRatioText(shortSide, longSide float64) string { + ratio := longSide / shortSide + bestNumer, bestDenom := 1, 1 + bestErr := ratio - 1 + if bestErr < 0 { + bestErr = -bestErr + } + for denom := 1; denom <= 12; denom++ { + numer := int(ratio*float64(denom) + 0.5) + if numer < 1 { + numer = 1 + } + err := ratio - float64(numer)/float64(denom) + if err < 0 { + err = -err + } + if err < bestErr { + bestErr = err + bestNumer = numer + bestDenom = denom + } + } + return fmt.Sprintf("%d:%d", bestDenom, bestNumer) } func compactProductNameGuard(floor FloorOption) string {