Skip to content

Commit

Permalink
feat: optimize layout
Browse files Browse the repository at this point in the history
Signed-off-by: Yorling <[email protected]>
  • Loading branch information
shallowclouds committed May 21, 2023
1 parent f772d17 commit 63cab9e
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 67 deletions.
Binary file added assets/moon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/sun.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions cards.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import (
"sync"

"github.com/disintegration/imaging"
"github.com/fogleman/gg"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
)

const (
Expand Down Expand Up @@ -51,6 +53,9 @@ type Assets struct {
Cards []Card
BackgroundImg image.Image
Font *truetype.Font
FontFace font.Face
AskerImg image.Image
ReaderImg image.Image
}

var (
Expand Down Expand Up @@ -176,11 +181,47 @@ func initFonts() {
assets.Font = font
}

const (
defaultIconSize = 30
)

func initIcon() {
askerImg := mustReadImg("assets/sun.png")
readerImg := mustReadImg("assets/moon.png")

askerImg = imaging.Resize(askerImg, defaultIconSize, defaultIconSize, imaging.Lanczos)
readerImg = imaging.Resize(readerImg, defaultIconSize, defaultIconSize, imaging.Lanczos)

dc := gg.NewContextForImage(askerImg)
dc.SetFillRule(gg.FillRuleWinding)
dc.DrawCircle(float64(defaultIconSize/2), float64(defaultIconSize/2), float64(defaultIconSize/2))
dc.Clip()
dc.InvertMask()
dc.DrawRectangle(0, 0, float64(defaultIconSize), float64(defaultIconSize))
dc.SetColor(color.Black)
dc.Fill()
askerImg = dc.Image()

dc = gg.NewContextForImage(readerImg)
dc.SetFillRule(gg.FillRuleWinding)
dc.DrawCircle(float64(defaultIconSize/2), float64(defaultIconSize/2), float64(defaultIconSize/2))
dc.Clip()
dc.InvertMask()
dc.DrawRectangle(0, 0, float64(defaultIconSize), float64(defaultIconSize))
dc.SetColor(color.Black)
dc.Fill()
readerImg = dc.Image()

assets.AskerImg = askerImg
assets.ReaderImg = readerImg
}

func GetDefaultAssets() Assets {
initAssetsOnce.Do(func() {
initCards()
initBgImg()
initFonts()
initIcon()
})

return assets
Expand Down
13 changes: 10 additions & 3 deletions cmd/divinetest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,24 @@ func main() {
panic(err)
}

_, img, res, err := reader.DivineSync(context.Background(), *thingArg)
res, err := reader.DivineWithOption(context.Background(), tarot.DivineOption{
Question: *thingArg,
Asker: "岳云翎",
AskerImg: nil,
Reader: "",
ReaderImg: nil,
Callback: nil,
})
if err != nil {
panic(err)
}

fmt.Printf("%s\n", res)
fmt.Printf("%v\n", res)

// fmt.Println(reader.Prompt(cards, *thingArg, ""))

// err = SavePng(img, "divine_results.png")
err = SaveJpg(img, "dev/divine_results.jpg")
err = SaveJpg(res.Img, "dev/divine_results.jpg")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion gpt_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type DumbGPTReader struct{}

func (r *DumbGPTReader) Chat(ctx context.Context, systemMsg, userMsg string) (string, error) {
// return "你的未来诡谲难测,我看不到任何信息。", nil
return `根据三张牌的含义和您所问的问题,我的解读如下
return `根据三张牌的含义和您所问的问题,解读如下
首先,逆位的月亮牌表示您目前处于一种迷茫不定的状态,有些心理上的困扰和身体上的疑惑。您可能感到不安和焦虑,对您的身体状况也缺乏清晰的判断力。
Expand Down
133 changes: 70 additions & 63 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (r *Reader) Choose() ([3]Card, error) {
}

const (
defaultSystemPrompt = `你是一位神秘的塔罗牌占卜师,请根据三张牌面和这件具体事情进行客观解读,语言简练精辟客观,不准使用“虽然...但是...”这样模棱两可的话,不用提醒用户占卜的局限性或者意义。`
defaultSystemPrompt = `你是一位神秘的塔罗牌占卜师,请根据三张牌面和用户占卜的具体事情进行客观解读,语言简练精辟客观,不准回答模棱两可的话,不用提醒用户占卜的局限性或者意义,要明确指出预兆是好的还是坏的。`
defaultUserPrompt = `我抽到的三张塔罗牌分别是:{{card1}},{{card2}},{{card3}}。我想占卜的事情是:“{{thing}}”,请解读。`
defaultPrompt = `
假如你是一位神秘的塔罗牌占卜师,我想要占卜事情是 “{{thing}}”,我抽到的三张牌分别是:{{card1}},{{card2}},{{card3}}。
Expand Down Expand Up @@ -102,68 +102,62 @@ func (r *Reader) Read(ctx context.Context, cards [3]Card, thing string) (string,
return resp, nil
}

func (r *Reader) DivineSync(ctx context.Context, thing string) ([3]Card, image.Image, string, error) {
cards, _ := r.Choose()
logger := logrus.StandardLogger()
logger.Infof("chosen cards: %s, %s, %s", cards[0].ZhString(), cards[1].ZhString(), cards[2].ZhString())
type DivineOption struct {
Question string
Asker string
AskerImg image.Image
Reader string
ReaderImg image.Image
Callback func(result *DivineResult, err error)
}

logger.Infof("start to call chat gpt reader")
start := time.Now()
res, err := r.Read(ctx, cards, thing)
logger.Infof("call gpt reader cost %.2f s", time.Since(start).Seconds())
if err != nil {
logger.WithError(err).Warn("failed to call chat gpt")
return cards, nil, "", errors.WithMessage(err, "failed to read cards")
}
type DivineResult struct {
Cards [3]Card
Img image.Image
Result string
}

img, err := r.Render(cards, thing, res)
func (r *Reader) DivineWithOption(ctx context.Context, opt DivineOption) (*DivineResult, error) {
cards, err := r.Choose()
if err != nil {
return cards, nil, "", errors.WithMessage(err, "failed to render img")
return nil, errors.WithMessage(err, "failed to choose cards")
}

return cards, img, res, nil
}

func (r *Reader) Divine(ctx context.Context, thing string, callback func(err error, res string)) ([3]Card, image.Image, error) {
cards, _ := r.Choose()
logger := logrus.StandardLogger()
logger.Infof("chosen cards: %s, %s, %s", cards[0].ZhString(), cards[1].ZhString(), cards[2].ZhString())

go func() {
logger.Infof("start to call chat gpt reader")
readAndRender := func() (*DivineResult, error) {
start := time.Now()
res, err := r.Read(ctx, cards, thing)
res, err := r.Read(ctx, cards, opt.Question)
logger.Infof("call gpt reader cost %.2f s", time.Since(start).Seconds())
if err != nil {
logger.WithError(err).Warn("failed to call chat gpt")
return nil, err
}
if callback != nil {
callback(err, res)

img, err := r.Render(cards, opt.Question, res, opt)
if err != nil {
return nil, err
}
}()

img, err := r.Render(cards, thing, "")
if err != nil {
return cards, nil, errors.WithMessage(err, "failed to render img")
return &DivineResult{Cards: cards, Img: img, Result: res}, nil
}

return cards, img, nil
if opt.Callback != nil {
go func() {
res, err := readAndRender()
opt.Callback(res, err)
}()

return &DivineResult{Cards: cards}, nil
}

return readAndRender()
}

const (
fontSize = 26.0
)

func (r *Reader) getTextSize(s string) (int, int) {
face := truetype.NewFace(r.assets.Font, &truetype.Options{
Size: fontSize,
})
width := font.MeasureString(face, s).Ceil()
height := face.Metrics().Ascent.Ceil()

return width, height
}

func wrapText(text string, maxWidth int, face font.Face) []string {
var lines []string
var line string
Expand Down Expand Up @@ -228,8 +222,9 @@ func wrapText(text string, maxWidth int, face font.Face) []string {
// DrawStringWrapped word-wraps the specified string to the given max width
// and then draws it at the specified anchor point using the given line
// spacing and text alignment.
func DrawStringWrapped(dc *gg.Context, ff font.Face, s string, x, y, ax, ay, width, lineSpacing float64, align gg.Align) {
func DrawStringWrapped(dc *gg.Context, ff font.Face, s string, x, y, ax, ay, width, lineSpacing float64, align gg.Align) float64 {
lines := wrapText(s, int(width), ff)
// originalY := y

// sync h formula with MeasureMultilineString
h := float64(len(lines)) * dc.FontHeight() * lineSpacing
Expand All @@ -252,9 +247,30 @@ func DrawStringWrapped(dc *gg.Context, ff font.Face, s string, x, y, ax, ay, wid
dc.DrawStringAnchored(line, x, y, ax, ay)
y += dc.FontHeight() * lineSpacing
}

return y
}

func (r *Reader) Render(cards [3]Card, Q, A string) (image.Image, error) {
func (r *Reader) Render(cards [3]Card, Q, A string, opt DivineOption) (image.Image, error) {
if opt.Asker == "" {
opt.Asker = "Anonymous"
}
if opt.AskerImg == nil {
opt.AskerImg = r.assets.AskerImg
}
if opt.Reader == "" {
opt.Reader = "Fortuneteller"
}
if opt.ReaderImg == nil {
opt.ReaderImg = r.assets.ReaderImg
}
if b := opt.AskerImg.Bounds(); b.Dx() != defaultIconSize || b.Dy() != defaultIconSize {
opt.AskerImg = imaging.Resize(opt.AskerImg, defaultIconSize, defaultIconSize, imaging.Lanczos)
}
if b := opt.ReaderImg.Bounds(); b.Dx() != defaultIconSize || b.Dy() != defaultIconSize {
opt.ReaderImg = imaging.Resize(opt.ReaderImg, defaultIconSize, defaultIconSize, imaging.Lanczos)
}

img := image.NewNRGBA64(image.Rect(0, 0, defaultImageWidth, defaultImageHeight))

dc := gg.NewContextForImage(img)
Expand All @@ -268,25 +284,7 @@ func (r *Reader) Render(cards [3]Card, Q, A string) (image.Image, error) {
dc.SetFontFace(ff)
dc.SetColor(color.White)

// aW := defaultImageWidth * 2 / 3
// // aH := defaultImageHeight
// startH := 200
// var card Card
// for idx := 0; idx < 3; idx++ {
// card = cards[idx]
// pic := card.Pic
// if card.Position == PositionReversed {
// pic = imaging.Rotate(pic, 180, color.NRGBA{0, 0, 0, 0})
// }
// center := aW * (idx + 1) / 4
// dc.DrawImageAnchored(pic, center, startH, 0.5, 0)

// s := card.ZhString()
// dc.DrawStringAnchored(s, float64(center), float64(startH-20), 0.5, 0.5)
// }

aW := defaultImageWidth * 2 / 3
// aH := defaultImageHeight
startH := 100
var card Card
for idx := 0; idx < 1; idx++ {
Expand Down Expand Up @@ -319,8 +317,17 @@ func (r *Reader) Render(cards [3]Card, Q, A string) (image.Image, error) {
Size: 18,
})
dc.SetFontFace(ff)
DrawStringWrapped(dc, ff, Q+"\n\n"+A,
float64(defaultImageWidth*5/6)-75, float64(defaultImageHeight/2), 0.5, 0.5, float64(defaultImageWidth/3), 1, gg.AlignLeft)

dc.DrawImageAnchored(opt.AskerImg, aW-75, 20, 0, 0)
dc.DrawStringAnchored(opt.Asker+":", float64(aW-75+30+5), float64(20+15), 0, 0.5)
yAsker := DrawStringWrapped(dc, ff, Q,
float64(aW-75), float64(20+30+10), 0, 0, float64(defaultImageWidth/3), 1, gg.AlignLeft)

dc.DrawImageAnchored(opt.ReaderImg, aW-75, int(yAsker)+20, 0, 0)
dc.DrawStringAnchored(opt.Reader+":", float64(aW-75+30+5), float64(yAsker+20+15), 0, 0.5)

DrawStringWrapped(dc, ff, A,
float64(aW-75), float64(yAsker+20+30+10), 0, 0, float64(defaultImageWidth/3), 1, gg.AlignLeft)

return dc.Image(), nil
}

0 comments on commit 63cab9e

Please sign in to comment.