-
Notifications
You must be signed in to change notification settings - Fork 12
/
profiles.go
94 lines (84 loc) · 2.56 KB
/
profiles.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package main
import (
"math"
"github.com/unixpickle/model3d/model2d"
)
// DrivenProfile creates a 2D Solid implementing the
// driven disk in a Geneva drive.
func DrivenProfile(s *Spec) model2d.Solid {
var negative model2d.JoinedSolid
// Cut out the circles corresponding to the driver.
for i := 0; i < 4; i++ {
theta := math.Pi * 2 * float64(i) / 4
center := model2d.Coord{X: math.Cos(theta), Y: math.Sin(theta)}
center = center.Scale(s.CenterDistance - s.Slack)
negative = append(negative, &model2d.Circle{
Center: center,
Radius: s.DriveRadius() - s.PinRadius,
})
}
// Cut out lines where the pin will enter the driven disk.
for i := 0; i < 4; i++ {
theta := math.Pi*2*float64(i)/4 + math.Pi/4
direction := model2d.Coord{X: math.Cos(theta), Y: math.Sin(theta)}
innerPoint := direction.Scale(s.CenterDistance - s.DriveRadius() - s.Slack)
outerPoint := direction.Scale(s.DrivenRadius)
mesh := model2d.NewMeshSegments([]*model2d.Segment{{innerPoint, outerPoint}})
collider := model2d.MeshToCollider(mesh)
negative = append(negative, model2d.NewColliderSolidHollow(collider, s.PinRadius))
}
baseProfile := &model2d.SubtractedSolid{
Positive: &model2d.Circle{Radius: s.DrivenRadius},
Negative: negative,
}
// Cut off the sharp edges (if there are any).
return model2d.IntersectedSolid{
baseProfile,
&model2d.Circle{Radius: MaximumRadius(baseProfile) - s.SharpEdgeCutoff},
}
}
// DriveProfile creates a 2D Solid implementing the drive
// disk in a Geneva drive.
//
// Requires the driven profile to cut it out properly.
func DriveProfile(s *Spec, driven model2d.Solid) model2d.Solid {
return model2d.JoinedSolid{
&model2d.Circle{
Center: model2d.Coord{X: s.DriveRadius() + s.Slack},
Radius: s.PinRadius - s.Slack,
},
&model2d.SubtractedSolid{
Positive: &model2d.Circle{
Radius: s.DriveRadius() - s.PinRadius - s.Slack,
},
Negative: &model2d.Circle{
Center: model2d.Coord{X: s.CenterDistance},
Radius: MaximumRadius(driven) + s.Slack,
},
},
}
}
// MaximumRadius finds the smallest radius of a circle
// centered at the origin that doesn't intersect s.
func MaximumRadius(s model2d.Solid) float64 {
max := s.Max().Dist(s.Min())
min := 0.0
for i := 0; i < 32; i++ {
r := (max + min) / 2
if collidesRadius(s, r) {
min = r
} else {
max = r
}
}
return max
}
func collidesRadius(s model2d.Solid, radius float64) bool {
for theta := 0.0; theta < math.Pi*2; theta += 0.01 {
d := model2d.Coord{X: math.Cos(theta), Y: math.Sin(theta)}
if s.Contains(d.Scale(radius)) {
return true
}
}
return false
}