From 74f9270779f89e46b83521b38a0a7e82d9f03b96 Mon Sep 17 00:00:00 2001 From: Alex Nichol Date: Sun, 27 Aug 2023 16:18:22 -0700 Subject: [PATCH] cherry, straw, fix internals --- .../usable/milkshake_ringholder/cherry.go | 75 +++++++++++++++++++ examples/usable/milkshake_ringholder/cream.go | 47 ++++++++++-- examples/usable/milkshake_ringholder/cup.go | 2 +- examples/usable/milkshake_ringholder/main.go | 8 +- examples/usable/milkshake_ringholder/straw.go | 69 +++++++++++++++++ 5 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 examples/usable/milkshake_ringholder/cherry.go create mode 100644 examples/usable/milkshake_ringholder/straw.go diff --git a/examples/usable/milkshake_ringholder/cherry.go b/examples/usable/milkshake_ringholder/cherry.go new file mode 100644 index 0000000..1f3aa2d --- /dev/null +++ b/examples/usable/milkshake_ringholder/cherry.go @@ -0,0 +1,75 @@ +package main + +import ( + "github.com/unixpickle/model3d/model2d" + "github.com/unixpickle/model3d/model3d" + "github.com/unixpickle/model3d/render3d" + "github.com/unixpickle/model3d/toolbox3d" +) + +const ( + CherryRadius = 0.25 + CherryStemLength = 0.15 + CherryStemRadius = 0.05 +) + +func CherrySolid() (model3d.Solid, toolbox3d.CoordColorFunc) { + curve := model2d.SmoothBezier( + model2d.XY(0, -CherryRadius), + model2d.XY(-CherryRadius*0.7, -CherryRadius), + model2d.XY(-CherryRadius, -CherryRadius*0.3), + model2d.XY(-CherryRadius, 0), + // Point 1 + model2d.XY(-CherryRadius*0.9, CherryRadius*0.5), + model2d.XY(-CherryRadius*0.6, CherryRadius*0.7), + // Point 2 + model2d.XY(-CherryRadius*0.4, CherryRadius*0.9), + model2d.XY(-CherryRadius*0.1, CherryRadius*0.8), + // Point 3 + model2d.XY(-CherryRadius*0.1, CherryRadius*0.7), + model2d.XY(0, CherryRadius*0.7), + ) + profile := model2d.CurveMesh(curve, 100) + profile.AddMesh(profile.MapCoords(model2d.XY(-1, 1).Mul)) + solid2d := model2d.NewColliderSolid(model2d.MeshToCollider(profile)) + center := CreamHeight + CupHeight + solid := model3d.CheckedFuncSolid( + model3d.XYZ(-CherryRadius, -CherryRadius, center-CherryRadius), + model3d.XYZ(CherryRadius, CherryRadius, center+CherryRadius+0.2), + func(c model3d.Coord3D) bool { + x := c.XY().Norm() + y := c.Z - center + return solid2d.Contains(model2d.XY(x, y)) + }, + ) + + stemCurve := model2d.BezierCurve{ + model2d.XY(0.0, center+CherryRadius-0.03), + model2d.XY(0.0, center+CherryRadius+CherryStemLength/2), + model2d.XY(CherryStemLength*0.4, center+CherryRadius+CherryStemLength), + } + var segments []model3d.Segment + eps := 0.01 + for t := 0.0; t < 1.0-eps; t += eps { + p1 := stemCurve.Eval(t) + p2 := stemCurve.Eval(t + eps) + segments = append( + segments, + model3d.NewSegment( + model3d.YZ(p1.X, p1.Y), + model3d.YZ(p2.X, p2.Y), + ), + ) + } + stem := toolbox3d.LineJoin(CherryStemRadius, segments...) + + colorFn := func(c model3d.Coord3D) render3d.Color { + if stem.Contains(c) { + return render3d.NewColorRGB(0.2, 0.0, 0.0) + } else { + return render3d.NewColorRGB(1.0, 0.0, 0.0) + } + } + + return model3d.JoinedSolid{solid, stem}, colorFn +} diff --git a/examples/usable/milkshake_ringholder/cream.go b/examples/usable/milkshake_ringholder/cream.go index cca483c..4edc04b 100644 --- a/examples/usable/milkshake_ringholder/cream.go +++ b/examples/usable/milkshake_ringholder/cream.go @@ -10,14 +10,38 @@ import ( ) const ( - CreamRadius = 0.15 - CreamHeight = 0.8 - CreamZOffset = 0.05 + CreamRadius = 0.15 + CreamHeight = 0.8 + CreamZOffset = 0.05 + CreamRadiusOffset = 0.05 ) func CreamSolid() (model3d.Solid, toolbox3d.CoordColorFunc) { mesh := CreamMesh() - return model3d.NewColliderSolid(model3d.MeshToCollider(mesh)), + collider := model3d.MeshToCollider(mesh) + direction := model3d.NewCoord3DRandUnit() + solid := model3d.CheckedFuncSolid( + collider.Min(), + collider.Max(), + func(c model3d.Coord3D) bool { + var numInterior, numExterior int + ray := model3d.Ray{Origin: c, Direction: direction} + collider.RayCollisions(&ray, func(rc model3d.RayCollision) { + if rc.Normal.Dot(direction) < 0 { + numExterior++ + } else { + numInterior++ + } + }) + return numInterior > numExterior + }, + ) + interior := &model3d.Cone{ + Tip: model3d.Z(solid.Max().Z), + Base: model3d.Z(CupHeight - CreamZOffset), + Radius: CupTopRadius - CreamRadius, + } + return model3d.JoinedSolid{solid, interior}, toolbox3d.ConstantCoordColorFunc(render3d.NewColor(1.0)) } @@ -52,14 +76,25 @@ func CreamMesh() *model3d.Mesh { rotation := c.Z / length * 25 x1, x2 = x1.Scale(math.Cos(rotation)).Add(x2.Scale(math.Sin(rotation))), x1.Scale(-math.Sin(rotation)).Add(x2.Scale(math.Cos(rotation))) - return centerCoord.Add(x1.Scale(c.Y)).Add(x2.Scale(c.X)) + + // Taper off at the end + t := c.Z / length + taper := 1.0 + if t > 0.85 { + taper = (1.0 - t) / (1 - 0.85) + } + + return centerCoord.Add(x1.Scale(c.Y * taper)).Add(x2.Scale(c.X * taper)) }) } func creamCurve(t float64) model3d.Coord3D { theta := t * 30 // We want the start to naturally "tuck into" the spiral. - radius := (CupTopRadius - CreamRadius) * (1 - math.Abs(t*1.1-0.1)) + radius := math.Min( + CupTopRadius-CreamRadius-CreamRadiusOffset, + (CupTopRadius-CreamRadius)*(1-math.Abs(t*1.1-0.1)), + ) return model3d.XYZ( math.Cos(theta)*radius, math.Sin(theta)*radius, diff --git a/examples/usable/milkshake_ringholder/cup.go b/examples/usable/milkshake_ringholder/cup.go index 204f762..051535a 100644 --- a/examples/usable/milkshake_ringholder/cup.go +++ b/examples/usable/milkshake_ringholder/cup.go @@ -18,7 +18,7 @@ const ( CupHeight = 3 CupRimHeight = 0.4 CupRimThickness = 0.1 - CupContentsDepth = 0.1 + CupContentsDepth = 0.05 ) func CupSolid() (model3d.Solid, toolbox3d.CoordColorFunc) { diff --git a/examples/usable/milkshake_ringholder/main.go b/examples/usable/milkshake_ringholder/main.go index a7dd8de..46a44a8 100644 --- a/examples/usable/milkshake_ringholder/main.go +++ b/examples/usable/milkshake_ringholder/main.go @@ -9,14 +9,20 @@ import ( func main() { cup, cupColor := CupSolid() cream, creamColor := CreamSolid() - joined := model3d.JoinedSolid{cup, cream} + straw, strawColor := StrawSolid() + cherry, cherryColor := CherrySolid() + joined := model3d.JoinedSolid{cup, cream, straw, cherry} mesh, interior := model3d.MarchingCubesInterior(joined, 0.02, 8) + mesh = model3d.MeshToHierarchy(mesh)[0].Mesh colorFunc := toolbox3d.JoinedSolidCoordColorFunc( interior, cup, cupColor, cream, creamColor, + straw, strawColor, + cherry, cherryColor, ) render3d.SaveRandomGrid("rendering.png", mesh, 3, 3, 300, colorFunc.RenderColor) + mesh.SaveMaterialOBJ("milkshake.zip", colorFunc.TriangleColor) } diff --git a/examples/usable/milkshake_ringholder/straw.go b/examples/usable/milkshake_ringholder/straw.go new file mode 100644 index 0000000..c34693d --- /dev/null +++ b/examples/usable/milkshake_ringholder/straw.go @@ -0,0 +1,69 @@ +package main + +import ( + "math" + + "github.com/unixpickle/model3d/model3d" + "github.com/unixpickle/model3d/render3d" + "github.com/unixpickle/model3d/toolbox3d" +) + +const ( + StrawHeight = 1.5 + StrawRadius = 0.1 + StrawInset = 0.02 + StrawTwistRate = 5.0 +) + +func StrawSolid() (model3d.Solid, toolbox3d.CoordColorFunc) { + s1, c1 := SingleStrawSolid() + xf1 := model3d.JoinedTransform{ + model3d.Rotation(model3d.Y(1), 0.5), + &model3d.Translate{Offset: model3d.XZ(-1.1, 0.1)}, + } + s2 := model3d.TransformSolid(xf1, s1) + c2 := c1.Transform(xf1) + xf2 := model3d.JoinedTransform{ + model3d.Rotation(model3d.XY(-0.2, 1).Normalize(), -0.2), + &model3d.Translate{Offset: model3d.XZ(0.3, 0.0)}, + } + s3 := model3d.TransformSolid(xf2, s1) + c3 := c1.Transform(xf2) + return model3d.JoinedSolid{s2, s3}, toolbox3d.JoinedSolidCoordColorFunc(nil, s2, c2, s3, c3) +} + +func SingleStrawSolid() (model3d.Solid, toolbox3d.CoordColorFunc) { + outerSolid := &model3d.Cylinder{ + P1: model3d.Z(CupHeight * 0.9), + P2: model3d.Z(CupHeight + StrawHeight), + Radius: StrawRadius, + } + solid := &model3d.SubtractedSolid{ + Positive: outerSolid, + Negative: &model3d.Cylinder{ + P1: model3d.Z(CupHeight + StrawHeight - 0.05), + P2: model3d.Z(CupHeight + StrawHeight), + Radius: StrawRadius - StrawInset, + }, + } + colorFn := func(c model3d.Coord3D) render3d.Color { + if c.XY().Norm() <= StrawRadius-StrawInset { + return render3d.NewColor(0.8) + } + theta := math.Atan2(c.X, c.Y) + twist := c.Z * StrawTwistRate + theta += twist + for theta < 0 { + theta += math.Pi * 2 + } + for theta > math.Pi*2 { + theta -= math.Pi * 2 + } + if theta < math.Pi/4 || (theta > math.Pi && theta < math.Pi+math.Pi/4) { + return render3d.NewColorRGB(1.0, 0.0, 0.0) + } else { + return render3d.NewColor(0.8) + } + } + return solid, colorFn +}