Skip to content

Instantly share code, notes, and snippets.

@joe-warren
Last active January 22, 2025 02:37
Show Gist options
  • Save joe-warren/abf41560c187fb39cb424e0e75ca6b3e to your computer and use it in GitHub Desktop.
Save joe-warren/abf41560c187fb39cb424e0e75ca6b3e to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
import qualified Waterfall
import Linear
import Control.Lens ((^.), (.~))
import Data.Function ((&))
import Waterfall.SVG (parsePath)
import Control.Monad (sequence)
import Data.Either (fromRight)
-- This code makes heavy use of a module called "Waterfall.SVG"
-- I plan on including this into the opencascade-hs monorepo, however need to clean it up first
-- Until I've done so, running this will be moderately difficult
-- print two of these, one of them mirrored, and then glue them back to back
-- to get a more symetrical ornament
blobfishLogo :: Waterfall.Solid
blobfishLogo =
let fromPaths h = mconcat . fmap (Waterfall.prism h . Waterfall.fromPath) . mconcat . fromRight (error "bad path") . traverse parsePath
outerPath =
["m 17.218114,49.338036 c 0.238335,-2.798139 2.269297,-6.369214 4.978501,-8.673645 1.433798,-1.219579 4.763468,-2.594095 8.19962,-2.821158 3.436153,-0.227063 7.371077,1.883077 10.109838,4.794609 2.738762,2.911532 2.795879,4.294033 3.732882,7.56037 0.836009,0.368529 1.730346,0.402379 2.25933,2.205782 0.650047,2.216127 -2.025726,5.306277 -3.222797,5.201234 -1.176752,1.056308 -2.833491,2.474228 -5.661439,2.512677 -2.827948,0.03845 -4.229676,-1.582493 -5.845724,-1.71041 -1.616048,-0.127917 -2.423022,0.67068 -3.731985,0.617363 -1.308963,-0.05332 -2.637131,-0.976095 -3.955464,-0.990455 -1.318333,-0.01436 -2.336644,1.150172 -4.390725,0.569669 -1.553869,-0.439139 -2.256624,-2.925159 -2.256624,-2.925159 0,0 -0.657619,0.101546 -1.423828,-0.514475 -0.766209,-0.616021 -2.075721,-2.074519 -1.663024,-3.794424 0.412697,-1.719906 2.871439,-2.031978 2.871439,-2.031978 z"
]
leftFin =
["m 18.801102,53.551571 c -0.397811,0.337705 -0.802384,0.349209 -1.358124,1.160482 0,0 -2.552174,-0.838306 -1.861502,-3.073586 0.418007,-1.352832 2.929731,-1.395312 2.929731,-1.395312 z"
]
center =
["m 18.359296,51.957311 c -0.206229,-3.295991 1.175757,-7.04399 3.427658,-9.459569 2.117155,-2.271039 5.506634,-3.43887 8.609281,-3.555272 4.423474,-0.165956 8.521494,2.629913 10.689677,6.641573 1.27432,2.357798 1.827715,5.039056 1.509554,7.700235 -0.327363,2.738152 -0.857437,4.858515 -5.894706,5.655794 -1.736388,0.274829 -3.308943,-1.863149 -5.059072,-2.029352 -1.140629,-0.108321 -1.998628,0.615125 -3.143669,0.57452 -1.284158,-0.04554 -2.480811,-0.779762 -3.76561,-0.800381 -1.215739,-0.01951 -2.679106,1.208554 -3.779192,0.690661 -1.811653,-0.852881 -2.468878,-3.419746 -2.593921,-5.418209 z"
]
rightFin =
[ "m 38.466855,51.815976 c 0.921724,1.158085 1.675101,2.188029 4.348946,4.679775 2.027978,-1.170854 2.710204,-2.784309 2.243101,-4.006993 -0.705822,-1.84755 -4.352036,-2.293646 -5.326149,-1.98199 -0.598556,0.1915 -1.265898,1.309208 -1.265898,1.309208 z"
]
face =
[ "M 25.831835 45.230447 C 25.65013 45.525794 25.46349 45.848641 25.2975 46.194212 A 2.4694452 2.4743323 0 0 0 23.861412 45.732225 A 2.4694452 2.4743323 0 0 0 21.392312 48.206492 A 2.4694452 2.4743323 0 0 0 23.861412 50.68076 A 2.4694452 2.4743323 0 0 0 25.332124 50.192418 C 25.485682 50.429518 25.672837 50.663377 25.898497 50.890566 C 26.255767 51.250258 26.657594 51.50185 27.077753 51.66933 C 23.160288 52.372011 20.590294 54.713072 20.590294 54.713072 L 21.149433 55.215884 C 21.149433 55.215884 24.567513 53.01464 28.828034 52.706476 C 33.130367 52.39529 38.887885 56.140892 38.887885 56.140892 L 39.3199 55.668569 C 39.3199 55.668569 34.803871 52.276407 30.519921 51.594399 C 30.647584 51.546694 30.768935 51.496048 30.882689 51.443504 C 31.298022 51.251661 31.608792 51.063051 31.808731 50.794965 L 31.207217 50.345897 C 31.168867 50.397317 30.918511 50.601771 30.568496 50.763442 C 30.218484 50.925114 29.763132 51.071912 29.275552 51.146365 C 28.300393 51.295273 27.225101 51.162164 26.429731 50.3614 C 25.600992 49.527043 25.428264 48.72994 25.529527 47.925889 C 25.630792 47.121841 26.045039 46.314835 26.470555 45.623188 L 25.831835 45.230447 z"
, "m 36.864544,48.244328 c 0,1.402253 -1.283265,2.796067 -2.571088,2.796067 -1.287825,0 -2.679753,-1.301276 -2.679753,-2.703529 10e-7,-1.402253 1.418988,-2.796325 2.706812,-2.796325 1.287824,0 2.544029,1.301534 2.544029,2.703787 z"
]
glints =
[ "m 24.997777,46.847293 c -0.245583,0.602332 -0.293329,1.433777 -0.230685,2.044382 -0.291116,-0.750768 -0.894501,-1.934243 -1.557937,-2.314524 0.854491,-0.27642 1.327725,-0.07877 1.788622,0.270142 z"
, "m 35.565173,46.671185 c 0.377929,0.433036 0.543038,1.039082 0.405482,1.334483 -0.202047,0.433897 -0.7173,0.669147 -1.083671,0.62109 -0.291116,-0.750768 -0.773852,-1.66702 -1.437288,-2.047301 0.684146,-0.587035 1.73537,-0.343803 2.115477,0.09173 z"
]
eyes =
[ "M 23.913089 44.861477 C 22.100326 44.861477 20.660574 46.271363 20.660574 48.157917 C 20.660574 50.044471 22.087924 51.375809 23.900686 51.375808 C 24.796831 51.375808 25.56042 51.050673 26.103653 50.493691 C 25.516457 49.924667 25.157457 49.118013 25.157457 48.171353 C 25.157457 47.217696 25.525267 46.385835 26.12329 45.796304 C 25.582316 45.211377 24.816566 44.861478 23.913089 44.861477 z"
, "m 37.628795,48.290466 a 3.2822986,3.4159122 0 0 1 -3.282298,3.415912 3.2822986,3.4159122 0 0 1 -3.282299,-3.415912 3.2822986,3.4159122 0 0 1 3.282299,-3.415912 3.2822986,3.4159122 0 0 1 3.282298,3.415912 z"
]
cutMainHole = (`Waterfall.difference` Waterfall.translate (unit _z) (fromPaths 3 (leftFin <> center <> rightFin)))
cutGlints = (`Waterfall.difference` Waterfall.translate (unit _z ^* 2) (fromPaths 3 glints))
in fromPaths 3 outerPath
& cutMainHole
& (<> fromPaths 1.5 center)
& (<> fromPaths 2 rightFin)
& (<> fromPaths 3 face)
& (<> fromPaths 2 eyes)
& cutGlints
& Waterfall.mirror (unit _x)
circle :: Waterfall.Path2D
circle = Waterfall.pathFrom (unit _x)
[ Waterfall.arcViaTo (unit _y) (negate $ unit _x)
, Waterfall.arcViaTo (negate $ unit _y) (unit _x)
]
ornament :: Waterfall.Solid
ornament =
let
com = Waterfall.centerOfMass blobfishLogo
Just (lo, _hi) = Waterfall.axisAlignedBoundingBox blobfishLogo
rawHoop = Waterfall.sweep (Waterfall.fromPath2D . Waterfall.uScale2D 3 $ circle) (Waterfall.fromPath circle)
hoopClipped = rawHoop `Waterfall.intersection` (Waterfall.centeredCube & Waterfall.translate (unit _z ^* 0.5) & Waterfall.uScale 10)
hoopPositioned = hoopClipped
& Waterfall.translate (unit _x ^* (com ^. _x))
& Waterfall.translate (unit _y ^* (lo ^. _y))
in hoopPositioned <> blobfishLogo
main :: IO ()
main = do
Waterfall.writeSTL 0.1 "blobfish-ornament.stl" ornament
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment