灰姑娘(GO版)

故事背景(简化版)

  • 辛德瑞拉有个继母和2个姐姐,每天被她们欺负
  • 有一天,城里举行一场舞会。参加舞会需要礼服和鞋子。姐姐们都有,而辛德瑞拉没有。
  • 仙女用魔法给辛德瑞拉变出了礼服和鞋子(水晶鞋),并告诉辛德瑞拉魔法会在午夜12点后失效
  • 辛德瑞拉在城里见到王子
  • 午夜12点,辛德瑞拉不得已要马上离开,在仓皇间留下了一只水晶鞋。
  • 王子拿着水晶鞋寻找辛德瑞拉
  • 只有辛德瑞拉才能穿上水晶鞋,姐姐们穿上都掉下来
  • 灰姑娘和王子在一起,幸福地生活

登场人物设定

go语言不像其它语言存在class。使用构造体来定义登场人物

package main

import "fmt"

// Sex -- 性别
type Sex int

//
const (
	_ Sex = iota
	Woman
	Man
)

// Actor -- 登场角色
type Actor struct {
	Name  string
	Age   int
	Sex   Sex
	Cos   Costume
	Shoes *Shoes
}

// NewActor -- 创建角色
func NewActor(n string, age int, s Sex) *Actor {
	return &Actor{
		Name: n,
		Age:  age,
		Sex:  s,
	}
}

// Say -- 角色-说
func (a *Actor) Say(s string) {
	fmt.Printf("%v: %v\n", a.Name, s)
}

// SetCostume -- 角色-穿服装
func (a *Actor) SetCostume(c Costume) {
	a.Cos = c
}

// SetShoes -- 角色-穿鞋
func (a *Actor) SetShoes(s *Shoes) {
	a.Shoes = s
}

Actor构造体使用NewActor来创建角色实例。
使用NewActor这个函数就可以创建辛德瑞拉,王子,继母等角色了

常量中生成连续整数

const (
	_ Sex = iota // _ Sex = 0
	Woman // Woman Sex = 1
	Man // Man Sex = 2
)

使用iota可以生成连续整数,iota只能在const里使用

服装构造体定义

参加舞会需要礼服。男的燕尾服,女的连衣裙

package main

// Costume -- 服装接口
type Costume interface {
	Wear(a *Actor) bool
}

// Dress -- 连衣裙
type Dress struct {
	Owner *Actor
}

// NewDress -- 生成连衣裙
func NewDress(a *Actor) Costume {
	return &Dress{
		Owner: a,
	}
}

// Wear -- 只有女性且所有者才能穿上
func (d *Dress) Wear(a *Actor) bool {
	return a == d.Owner && a.Sex == Woman
}

// Tailcoat -- 燕尾服
type Tailcoat struct {
	Owner *Actor
}

// NewTailcoat -- 生成燕尾服
func NewTailcoat(a *Actor) Costume {
	return &Tailcoat{
		Owner: a,
	}
}

// Wear -- 只有男性且所有者才能穿上
func (t *Tailcoat) Wear(a *Actor) bool {
	return a == t.Owner && a.Sex == Man
}

定义了连衣裙燕尾服构造体了,还实现了Costume服装接口。这样不管是连衣裙还是燕尾服,角色是否匹配,就可以通过Costume服装接口Wear方法来判断了

鞋子构造体定义

故事里需要水晶鞋。

package main

// Shoes -- 鞋子
type Shoes struct {
	Owner *Actor
}

// NewShoes -- 生成鞋子
func NewShoes(a *Actor) *Shoes {
	return &Shoes{
		Owner: a,
	}
}

// Wear -- 鞋子只能持有者能穿
func (s *Shoes) Wear(a *Actor) bool {
	return a == s.Owner
}

这里并没有实现Costume服装接口Wear方法

和继母,两个姐姐一起生活的辛德瑞拉

辛德瑞拉有个继母和2个姐姐,每天被她们欺负

	setMother := NewActor("继母", 52, Woman)
	sisterA := NewActor("姐姐A", 23, Woman)
	sisterB := NewActor("姐姐B", 20, Woman)
	cinderella := NewActor("辛德瑞拉", 18, Woman)

	setMother.Say("今天欺负一下辛德瑞拉~~~")
	sisterA.Say("今天欺负一下辛德瑞拉~~~")
	sisterB.Say("今天欺负一下辛德瑞拉~~~")
	cinderella.Say("orz...")

运行结果

继母: 今天欺负一下辛德瑞拉~~~
姐姐A: 今天欺负一下辛德瑞拉~~~
姐姐B: 今天欺负一下辛德瑞拉~~~
辛德瑞拉: orz...

城里举行舞会

舞会定义

package main

import (
	"fmt"
	"sync"
)

// Ball -- 舞会
type Ball struct {
	m          sync.Mutex
	Entries    []*Actor
	Clock      int //时间
	FinishedAt int
}

// NewBall ...
func NewBall(startedAt, finishedAt int) *Ball {
	return &Ball{
		Clock:      startedAt,
		FinishedAt: finishedAt,
	}
}

// Start ...
func (b *Ball) Start() {
	fmt.Printf("舞会开始")
}

// Dancing ...
func (b *Ball) Dancing() {
	b.m.Lock()
	defer b.m.Unlock()
	fmt.Printf("现在%d点\n", b.Clock)
	for _, a := range b.Entries {
		fmt.Printf("%v 在跳舞\n", a.Name)
	}
	b.Clock++
}

// Finish ...
func (b *Ball) Finish() {
	fmt.Println("舞会结束")
}

// IsFinish ...
func (b *Ball) IsFinish() bool {
	return b.Clock >= b.FinishedAt
}

// Entry ...
func (b *Ball) Entry(a *Actor) {
	if a.Cos != nil {
		b.Entries = append(b.Entries, a)
		fmt.Printf("%v 参加了舞会", a.Name)
	} else {
		fmt.Println("没有服装不能参加舞会")
		fmt.Printf("%v 不能参加舞会", a.Name)
	}
}

// Exit ...
func (b *Ball) Exit(a *Actor) {
	b.m.Lock()
	defer b.m.Unlock()
	var entries []*Actor
	for _, e := range b.Entries {
		if e != a {
			entries = append(entries, e)
		}
	}

	b.Entries = entries
	fmt.Printf("%v 离开舞会回家了\n", a.Name)
}

参加舞会必须有服装

	if a.Cos != nil {
		b.Entries = append(b.Entries, a)
		fmt.Printf("%v 参加了舞会", a.Name)
	} else {
		fmt.Println("没有服装不能参加舞会")
		fmt.Printf("%v 不能参加舞会", a.Name)
	}

通过Actor构造体的Costume接口来检查能否参加

可能发生重入

type Ball struct {
	m          sync.Mutex
	...
}

// 省略
	b.m.Lock()
	defer b.m.Unlock()

ExitDancing同时执行的时候,可能会使程序崩溃。
使用sync.Mutex来上锁防止
可以使用-race参数来确认go run -race ./

举行舞会

ball := NewBall(19, 27)

参加舞会前的准备

继母家的服装屋

package main

// DressRoom ...
type DressRoom struct {
	Dresses []*Dress
}

// NewDressRoom ...
func NewDressRoom() *DressRoom {
	return &DressRoom{}
}

// Store -- 保管连衣裙
func (d *DressRoom) Store(actors ...*Actor) {
	for _, a := range actors {
		cos := NewDress(a)
		if dress, ok := cos.(*Dress); ok {
			d.Dresses = append(d.Dresses, dress)
		}
	}
}

// GetDress -- 获取连衣裙
func (d *DressRoom) GetDress(a *Actor) {
	for _, dress := range d.Dresses {
		if dress.Wear(a) {
			a.SetCostume(dress)
		}
	}
}

可变个数参数

Store(actors ...*Actor) 

...3个点支持不限定个数参数

类型声明

Actor构造体里Cos Costume匹配到实体类型为Dress,保存到服装屋

		if dress, ok := cos.(*Dress); ok {
			d.Dresses = append(d.Dresses, dress)
		}

继母和姐姐们的准备

	//舞会服装准备
	dressRoom := NewDressRoom()
	dressRoom.Store(setMother, sisterA, sisterB)

去参加舞会

继母和姐姐们去参加舞会,而辛德瑞拉没有礼服不能参加

	//继母有连衣裙
	dressRoom.GetDress(setMother)

	//继母参加舞会
	ball.Entry(setMother)

	//姐姐A,B有连衣裙,参加舞会
	dressRoom.GetDress(sisterA)
	ball.Entry(sisterA)
	dressRoom.GetDress(sisterB)
	ball.Entry(sisterB)

	//辛德瑞拉没有连衣裙

	dressRoom.GetDress(cinderella)
	ball.Entry(cinderella)

运行结果

继母 参加了舞会
姐姐A 参加了舞会
姐姐B 参加了舞会
没有服装不能参加舞会
辛德瑞拉 不能参加舞会

魔法变出了礼服和水晶鞋

魔法生成道具

package main

import "fmt"

// Magic ...
type Magic struct {
	Target *Actor
	Broken chan int
}

// NewMagic ...
func NewMagic(a *Actor) *Magic {
	return &Magic{
		Target: a,
		Broken: make(chan int, 1),
	}
}

// GenerateDress ...
func (m *Magic) GenerateDress() Costume {
	fmt.Printf("%v 获得了魔法道具连衣裙\n", m.Target.Name)
	return NewDress(m.Target)
}

// GenerateGlassShoes ...
func (m *Magic) GenerateGlassShoes() *Shoes {
	fmt.Printf("%v 获得了魔法道具水晶鞋\n", m.Target.Name)
	return NewShoes(m.Target)
}

辛德瑞拉穿上了魔法道具

	magic := NewMagic(cinderella)
	cinderella.SetCostume(magic.GenerateDress())
	cinderella.SetShoes(magic.GenerateGlassShoes())

魔法会在0点失效,警告辛德瑞拉需要回家了

func (m *Magic) Limit(limit chan int) {
	<-limit //从limit接收值
	fmt.Println("快到晚上0点了")
	fmt.Println("魔法要被解除了")
	m.Broken <- 1 //往m.Broken发送值
}

辛德瑞拉去参加舞会

	limit := make(chan int, 1)
	go magic.Limit(limit)

	ball.Entry(cinderella)

运行结果

辛德瑞拉 获得了魔法道具连衣裙
辛德瑞拉 获得了魔法道具水晶鞋
辛德瑞拉 参加了舞会

舞会上一见钟情的王子

王子身穿燕尾服登场

	prince := NewActor("王子", 18, Man)
	tailcoat := NewTailcoat(prince)
	prince.SetCostume(tailcoat)
	ball.Entry(prince)

运行结果

王子 参加了舞会

舞会进行中

大家都在跳舞,而辛德瑞拉需要在24点回家

	finished := make(chan int, 1)
	go func() {
		for !ball.IsFinished() {
			<-time.After(1 * time.Second)
			ball.Dancing()

			if ball.Clock == 24 {
				limit <- 1
			}
		}
		ball.Finish()
		finished <- 1
	}()

使用time.After的管道每1秒接收信号,执行Dancing方法
当24点时,需要给limit管道发送信号,警告辛德瑞拉魔法要失效,需要回家
finished管道用来接收舞会结束信号

魔法失效,辛德瑞拉遗失了水晶鞋

	<-magic.Broken

	ball.Exit(cinderella)
	falledShoes := cinderella.Shoes
	cinderella.Shoes = nil

王子捡到了水晶鞋

	foundShoes := falledShoes

	//舞会结束
	<-finished

运行结果

舞会开始
现在19点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在20点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在21点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在22点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在23点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
快到晚上0点了
魔法要被解除了
辛德瑞拉 离开舞会回家了
现在24点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
现在25点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
现在26点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
舞会结束

王子拿鞋寻找辛德瑞拉

从舞会参加者里寻找

	fmt.Println("王子开始寻找辛德瑞拉")
	
	for _, a := range ball.Entries {
		if a.Sex == Woman {
			if foundShoes.Wear(a) {
				fmt.Printf("找到了,是%v 的水晶鞋\n", a.Name)
			} else {
				fmt.Printf("%v: 不是%v 的水晶鞋\n", prince.Name, a.Name)
			}
		}
	}

从参加舞会的女性中寻找, 辛德瑞拉中途离开,不在名单ball.Entries

只有辛德瑞拉能穿起鞋子不掉下来

	if foundShoes.Wear(cinderella) {
		fmt.Printf("找到了,是%v 的水晶鞋\n", cinderella.Name)
	}

运行结果

王子开始寻找辛德瑞拉
王子: 不是继母 的水晶鞋
王子: 不是姐姐A 的水晶鞋
王子: 不是姐姐B 的水晶鞋
找到了,是辛德瑞拉 的水晶鞋

结局

灰姑娘和王子在一起,幸福地生活

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
首先,需要解析三角形文件,获取每个三角形的顶点坐标和法向量信息。然后,对于每个三角形,需要判断它是否可见。 判断三角形是否可见的方法有多种,这里介绍一种基于相机位置和三角形法向量的方法。假设相机位置是 $(x_c,y_c,z_c)$,三角形的三个顶点分别是 $(x_1,y_1,z_1)$、$(x_2,y_2,z_2)$ 和 $(x_3,y_3,z_3)$,三角形法向量为 $(n_x,n_y,n_z)$。 首先,计算相机位置到三角形的距离 $d$,即: $$ d = \frac{(x_1 - x_c)n_x + (y_1 - y_c)n_y + (z_1 - z_c)n_z}{\sqrt{n_x^2 + n_y^2 + n_z^2}} $$ 如果 $d < 0$,则三角形背向相机,不可见;如果 $d \geq 0$,则三角形朝向相机,可能可见。 接下来,需要判断相机是否在三角形所在的平面内。如果在平面内,那么需要判断相机在三角形的哪一侧。根据三角形法向量的定义,三角形所在平面的方程为: $$ n_x(x - x_1) + n_y(y - y_1) + n_z(z - z_1) = 0 $$ 将相机位置代入该方程,得到: $$ n_x(x_c - x_1) + n_y(y_c - y_1) + n_z(z_c - z_1) = 0 $$ 如果上式成立,则相机在三角形平面内;如果不成立,则相机不在平面内。 如果相机在平面内,那么需要计算相机到三角形各边的距离,并判断相机是否在三角形内部。具体方法是,将三角形拆分成三个三角形,分别由相机位置和三角形的两个顶点构成。对于每个拆分出来的三角形,计算相机到三角形的距离 $d_i$,如果 $d_i < 0$,则相机在三角形的外部;如果 $d_i \geq 0$,则相机在三角形的内部。 综合上述方法,可以编写一个函数来判断三角形是否可见,如下所示: ```python import numpy as np def is_visible(camera_pos, triangle): # 计算三角形法向量 v1 = triangle[1] - triangle[0] v2 = triangle[2] - triangle[0] normal = np.cross(v1, v2) normal /= np.linalg.norm(normal) # 计算相机到三角形的距离 d = np.dot(triangle[0] - camera_pos, normal) if d < 0: # 三角形背向相机,不可见 return False # 判断相机是否在三角形平面内 on_plane = np.isclose(np.dot(camera_pos - triangle[0], normal), 0) if not on_plane: # 相机不在平面内 return False # 判断相机是否在三角形内部 for i in range(3): j = (i + 1) % 3 v1 = triangle[j] - triangle[i] v2 = camera_pos - triangle[i] normal = np.cross(v1, v2) normal /= np.linalg.norm(normal) d = np.dot(v2, normal) if d < 0: # 相机在三角形外部 return False return True ``` 最后,遍历所有三角形,将可见的三角形加入集合即可: ```python def visible_triangles(camera_pos, triangles): visible = set() for triangle in triangles: if is_visible(camera_pos, triangle): visible.add(triangle) return visible ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值