{ "cells": [ { "cell_type": "markdown", "source": [ "Weaving a transformable curved surface from catenoid to helicoid.\n", "You can buy the kit [via my Booth site](https://hyrodium.booth.pm/items/5046306)!" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Load packages" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using Luxor\n", "using IntervalSets\n", "using BasicBSpline\n", "using BasicBSplineFitting\n", "using StaticArrays\n", "using ElasticSurfaceEmbedding\n", "using LinearAlgebra" ], "metadata": {}, "execution_count": 1 }, { "cell_type": "markdown", "source": [ "## Define the shape of the surface" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "f2 (generic function with 1 method)" }, "metadata": {}, "execution_count": 2 } ], "cell_type": "code", "source": [ "const N = 8\n", "const J = 1\n", "f0(s) = max(-abs(s+1/2N-1)-(1/2N-1), 0)\n", "f1(s) = -1/2+f0(mod(s-J/N, 2))\n", "f2(s) = 1/2-f0(mod(s-1-J/N, 2))" ], "metadata": {}, "execution_count": 2 }, { "cell_type": "markdown", "source": [ "0≤u≤2π, -π/2≤v≤π/2\n", "0≤s≤2, 0≤t≤1" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "u(s,t) = π*s\n", "v(s,t) = π*(f1(s)*(1-t) + t*f2(s))\n", "catenoid(u,v) = SVector(cos(u)*cosh(v),sin(u)*cosh(v),v)\n", "ElasticSurfaceEmbedding.𝒑₍₀₎(s,t) = catenoid(u(s,t), v(s,t))" ], "metadata": {}, "execution_count": 3 }, { "cell_type": "markdown", "source": [ "## Compute the shape of the embeddings" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "splitat = [-1/N, -1/2N, 0, 1/2N, 1/N, 1, 1+1/2N, 1+1/N]\n", "steptree = StepTree()\n", "for shift in [0, -1/N, -2/N, -3/N]\n", " initial_state!(steptree, (0+shift..2+shift, 0..1), splitat)\n", " newton_onestep!(steptree, fixingmethod=:fix5points)\n", " newton_onestep!(steptree, fixingmethod=:fix3points)\n", " newton_onestep!(steptree)\n", " refinement!(steptree, p₊=(0,1), k₊=ElasticSurfaceEmbedding.suggest_knotvector(steptree))\n", " for _ in 1:5 newton_onestep!(steptree) end\n", " pin!(steptree)\n", "end" ], "metadata": {}, "execution_count": 4 }, { "cell_type": "markdown", "source": [ "## Helper functions to export svg images" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "svector2point (generic function with 1 method)" }, "metadata": {}, "execution_count": 5 } ], "cell_type": "code", "source": [ "function create_bezierpath(C::BSplineManifold{1,(3,),Point})\n", " P = bsplinespaces(C)[1]\n", " k = knotvector(P)\n", " k′ = 3*unique(k) + k[[1,end]]\n", " P′ = BSplineSpace{3}(k′)\n", " C′ = refinement(C,P′)\n", " a′ = controlpoints(C′)\n", " n′ = dim(P′)\n", " m = (n′-1) ÷ 3\n", " bezierpath = BezierPath([BezierPathSegment(a′[3i-2], a′[3i-1], a′[3i], a′[3i+1]) for i in 1:m])\n", " return bezierpath\n", "end\n", "function svector2point(M::BSplineManifold)\n", " P = bsplinespaces(M)\n", " a = controlpoints(M)\n", " a′ = [Point(p[1], -p[2])*100/π for p in a]\n", " M′ = BSplineManifold(a′, P)\n", " return M′\n", "end" ], "metadata": {}, "execution_count": 5 }, { "cell_type": "markdown", "source": [ "## Settings for export" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "400" }, "metadata": {}, "execution_count": 6 } ], "cell_type": "code", "source": [ "xlims=(-2,2)\n", "ylims=(-2,2)\n", "unitlength = (100, \"mm\")\n", "\n", "width = (xlims[2] - xlims[1]) * unitlength[1]\n", "height = (ylims[2] - ylims[1]) * unitlength[1]" ], "metadata": {}, "execution_count": 6 }, { "cell_type": "markdown", "source": [ "## Export embeddings" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "mkpath(\"helicatenoid2\")\n", "for i in 1:(N+1)÷2\n", " filepath = joinpath(\"helicatenoid2\", \"embedding-$(i).svg\")\n", " M = svector2point(steptree.steps[10i].manifold)\n", " D¹ = domain(bsplinespaces(M)[1])\n", " D² = domain(bsplinespaces(M)[2])\n", " u²₋ = minimum(D²)\n", " u²₊ = maximum(D²)\n", "\n", " Drawing(width, height, filepath)\n", " origin()\n", " background(\"white\")\n", " sethue(\"red\")\n", "\n", " C = M(:,u²₋)\n", " path = create_bezierpath(C)\n", " drawbezierpath(path, :stroke)\n", " C = M(:,u²₊)\n", " path = create_bezierpath(C)\n", " drawbezierpath(path, :stroke)\n", "\n", " p1 = controlpoints(M)[begin,begin]\n", " p2 = controlpoints(M)[begin,end]\n", " p3 = controlpoints(M)[end,begin]\n", " p4 = controlpoints(M)[end,end]\n", "\n", " v12 = p1-p2\n", " q1 = p1 - Point(v12[2],-v12[1])/norm(v12) * 6\n", " q2 = p2 - Point(v12[2],-v12[1])/norm(v12) * 6\n", " line(p1,q1)\n", " line(q2)\n", " line(p2)\n", " strokepath()\n", "\n", " v34 = p3-p4\n", " q3 = p3 + Point(v34[2],-v34[1])/norm(v34) * 6\n", " q4 = p4 + Point(v34[2],-v34[1])/norm(v34) * 6\n", " line(p3,q3)\n", " line(q4)\n", " line(p4)\n", " strokepath()\n", "\n", " finish()\n", " preview()\n", "\n", " script = read(filepath, String)\n", " lines = split(script, \"\\n\")\n", " lines[2] = replace(lines[2],\"pt\\\"\"=>\"mm\\\"\")\n", " write(filepath, join(lines,\"\\n\"))\n", "end" ], "metadata": {}, "execution_count": 7 }, { "cell_type": "markdown", "source": [ "The output files will be saved as `embedding-$(i).svg`." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "\n", "\n", "\n", "" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "# References\n", "- [懸垂面螺旋面製作キット](https://hackmd.io/@hyrodium/Hy4D5r633)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "---\n", "\n", "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" ], "metadata": {} } ], "nbformat_minor": 3, "metadata": { "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.11.4" }, "kernelspec": { "name": "julia-1.11", "display_name": "Julia 1.11.4", "language": "julia" } }, "nbformat": 4 }