nurb
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesenurb
nurb
A part is a Python function and its keyword defaults are its parameters. A project is any directory with a folder; there is no init step. You are the CAD operator: the user describes the part and you do everything else, including creating the project and modelling.
parts/Start in the background and hand the user its URL before anything else. It prints one, and the viewer it serves is where the user watches the part take shape: every save rebuilds and repaints without moving their camera. When your shell runs on the user's own machine, puts the viewer on their screen without the URL hop. So work in saves, not in silence. already emits a working part; put that blocky draft on screen in the first minute and refine it while they watch. Caption it when you hand the URL over: a user who opens the viewer to an unannounced block cannot tell the placeholder from the design, and one sentence saying it is the starting shape you are now replacing turns the same block into progress. Ten correct but invisible minutes of modelling read as a hang. And repeat the URL at the end of every reply, the handoff included: chat scrolls the link away, and a part the user was never pointed at is a part they cannot judge. The URL deep-links: opens on that part and loads one of its card variants, so link the exact thing you changed rather than making the user find it in the list. And it is one server and one tab for the whole session: the process outlives your turns and every save already reaches the open viewer, so never restart to show new work, and is for the first start only, never per reply, because every extra open stacks another identical tab on the user's screen. A second start for a project already being served refuses and prints the running URL; reuse it.
nurb dev--opennurb new?part=<name>&variant=<name>nurb dev--openWhen says something is out of date, fix it before modelling. Its startup lines carry the version check: a line means the package is old, and a line means this very file is older than the installed nurb. Run the named command and tell the user, and after a sync re-read the skill file, because the stale copy is the one you are working from.
nurb devnurb updatenurb skill --syncOffer the permission allowlist before the prompts start. A session is dozens of invocations and part-file saves, and a harness that asks about each one turns twenty unattended minutes into twenty check-ins the user has to keep coming back for. If your harness keeps a per-project allowlist (in Claude Code it is ), offer at the start of the first session in a project to add what the loop needs, merging with anything already there, and move on without it if the user declines:
nurb.claude/settings.local.jsonjson
{
"permissions": {
"allow": [
"Bash(nurb:*)",
"Edit(parts/**)",
"Edit(system.py)",
"Edit(measurements.toml)",
"Edit(printer.toml)"
]
}
}Other harnesses take the same grants, the prefix and edits under plus , in their own file: Codex a in , Gemini in , Cursor's CLI a entry in , OpenCode a block in . Amp never prompts, so there is nothing to offer.
nurbparts/system.pyprefix_rule.codex/rules/tools.allowed.gemini/settings.jsonShell(nurb).cursor/cli.jsonpermissionopencode.jsonRun before you design. It prints the doctrine: printability, load paths, aesthetics, the polish pass, the kernel traps, card discipline, and what to verify. This file stays thin on purpose so there is one copy of that, in the package, which cannot drift.
nurb rulesAsk the tool before you read its source. prints the vocabulary a part file gets, with signatures, so finding out what returns is one command rather than a trip into site-packages. measures a built one: face areas, normals, which faces sit on the bed, every concave edge, and each finding resolved to the face it fired on, in the units the rules report. Between them they answer the questions that otherwise become a throwaway probe script apiece, which is the most expensive habit in this loop. When a finding fires and the fix is not obvious from its message, run and look before you edit: one PNG per finding, camera standing at the face it fired on with that face painted. A wall message that names its own thickness needs no photograph, but any edit that starts with guessing which face is guilty costs a rebuild cycle the look would have saved.
nurb apiconcave_edgesnurb inspect <part>nurb inspect --renderWhat you saw is a hypothesis, and the thing that settles it is a number. A look, yours at a render or the user's at the viewer, is how a fault gets noticed and never how it gets confirmed: a shelf that reads as floating can be seated and lit badly, two holes that look off centre can be the perspective, and the wall that looks too thin is sometimes the one dimension the user actually measured. So turn every visual read into something that answers on its own before you act on it or repeat it back: for anything a rule covers, for a face area or a normal or which faces sit on the bed, for whether an edit moved what you meant and nothing else. Then say the number rather than the impression, because "the tab is 1.6mm and your printer lays 1.0" ends the conversation that "you're right, it does look thin" continues forever. This matters most when the eyes are the user's: "that looks weak" is a symptom, the part they are pointing at may be fine while a different one is not, and thickening whatever they named is how a design accumulates material nobody needed.
nurb checknurb inspectnurb diffAsk before you model, and research before you ask. When the part mates with a manufactured product or a published standard (a VESA mount, a gridfinity bin, a camera thread), the numbers live in a spec: ask for the product name or a link instead of six caliper readings, look the spec up, and record it in with naming the source. What nobody published (an opening, a bracket, the object it holds) is a question for the user: ask for those measurements in one batch, up front, using your harness's question tool if it has one, and record the answers the way the doctrine describes. Anything that is taste rather than fit becomes a parameter instead of a question, because the viewer's sliders are how the user answers those. A guessed proportion costs one slider drag; a guessed clearance prints the wrong part.
measurements.tomlhowName parameters in the user's words, because the names are the sliders. Every keyword default shows up in the viewer labelled exactly as you spelled it, and a user staring at or can only learn what it moves by dragging it and guessing. Prefer plain words over trade vocabulary (, not ; , not ), say what the dimension is of, and keep a term of art only when the user said it first. Two or three words is still short; a name that needs the user to know machining jargon is a label in the wrong language. And a name is still only a label: give each parameter a one-line description in the function's docstring (), because the viewer shows that line when the user hovers the slider, and it is how a control explains itself without you in the room.
pilot_diatip_bossscrew_hole_widthpilot_diabar_thicknessbarname: what it movesshelf_depth: how far the shelf sticks out from the wallA photo names the shape, a scan measures it, a coupon proves it. When the part has to fit something in the physical world, a photo is for recognition: it tells you the siding is dutch lap or the rail is 2020 extrusion, and the published profile carries the numbers from there. It is never for dimensions, because pixels carry no millimetres. When no spec exists and calipers are not going to happen, ask for a ten-second phone scan instead: Scaniverse is free and exports STL, OBJ or GLB meshes, and reads one in mm, states the units it used, and slices a cross-section into a polyline short enough to sketch against. Record what the scan gave in with naming the file and the slice, and mark every scan-derived fit , because a phone scan is reference geometry rather than metrology. Then, before printing the full part, print a fit coupon: a thin strip carrying only the mating profile, minutes of filament, and the user holding it against the real thing answers in one photo what no scan can promise. When the coupon fits, the provisional flag comes off.
nurb scan <file>--section zmeasurements.tomlhowprovisionalOffer the scan at the moment shape is the problem, and script it for someone who has never scanned. A number is still cheaper than a scan: "how wide is the opening" costs the user a tape measure and you one question, so ask for dimensions when a dimension is what is missing, and offer a scan when the missing thing is a shape a few numbers cannot carry (a contoured surface, an irregular object, the wall the part has to hug). Nobody knows their phone can do this, so the offer teaches it in one breath, in their words: "this would fit better from a quick 3D scan: get Scaniverse (free), start a new scan in Mesh mode, slowly sweep your phone over the siding for about ten seconds, export as GLB, and drop the file here." Name the mode, because the app decides splat or mesh when the scan starts, splat is the default, and a splat scan can only export point clouds, points with no surfaces, which refuses saying so. So when a point-cloud PLY lands anyway, the fix is a rescan in Mesh mode (or reprocessing the scan as a mesh if the app offers it), never a different export of the same splat; say it in their terms, ten more seconds, not a failure. When the file lands, read it back in one sentence ("300 by 250mm, and I can see the 5mm step between courses"), because that sentence is what tells a first-time scanner it worked, and then model from it, coupon before part, as above.
nurb scanA downloaded model is a measurement, not a starting point, and say so before you touch it. When a file arrives from MakerWorld, Printables or Thingiverse, the user is usually asking you to change it, and the honest answer is that there is nothing in the file to change: a mesh is triangles, so its wall thickness is a distance between two sheets rather than a number, and no edit you make to it produces a parameter. reads it in one command and its last line says whether a solid can come out at all. A 3MF is the half of those downloads nurb does not read, and the fix is one menu item rather than a dead end: ask the user to open it in their slicer and export the plate as STL. Tell the user plainly what you are about to do, in their words: "I can't edit that file directly, but I can measure it and rebuild it as a part you can actually adjust, which takes a couple of minutes." Then rebuild it, and never mark a download or ask for a fit coupon: those exist because a phone scan is rough, and a downloaded file is exact to the micron. A scan and a download read identically here, so the difference is something the user told you, not something the mesh can say. Where a download is genuinely useful unchanged is as the thing your part has to clear: measure it, model the pocket or the bracket around it, and leave the original alone.
nurb scan <file>provisionalWhile rebuilding a mesh, declare it the part's target and let the deviation drive the loop. Copy the file into the project and put in the card's settings block (a table form takes when the file needs it). The viewer then draws the mesh as an amber ghost over the part, the target button hides it, and the corner carries two numbers refreshed on every save: how far the part's surface sits off the original, and how much of the original's surface the part does not reproduce yet. prints the same two directions with worst, typical and 95th-percentile distances, and asks a one-off without touching the card. Read the directions separately and never average them: the mesh is the ground truth, and material the original never had is wrong even while the overall numbers look close. The comparison centers the target on the part and rotates nothing, so model in the mesh's own orientation, and large symmetric numbers usually mean a frame mismatch rather than a bad shape.
target = "scans/original.stl"unitsnurb compare--against <file>import_stlWhen parts have to work together, assemble them before printing either. A part that mounts to another, or moves against one, gets an : a function in that returns placed solids, with building the siblings, declaring how one moves, and standing in for the machine or wall they mount into. then sweeps each joint through its declared range and reports the angle where it jams and where the contact is, which is the one failure no per-part check can see: a door that builds clean, checks clean, prints beautifully and will not open. The joint angle is a keyword default, so the viewer's slider swings the assembly live for the user. Model obstacles from the user's measurements, and say on the assembly's card how rough they are.
@assemblyparts/use()hinge()obstacle()nurb checkA family of parts keeps its shared design in one file. When siblings are meant to match, the shared wall, chamfer, corner radius or label geometry lives in at the project root, imported by every part; saving it rebuilds the whole project, so one edit moves the family together. But a system is extracted, never scaffolded: build the first two or three parts plainly, then run , which reports the constructions the files already say twice. Lift what is genuinely shared and leave the rest, because two parts saying the same thing is not yet a system; two parts that would both have to change is. The viewer runs the same scan and quietly folds a "shared with siblings" note under the parameters when three or more parts repeat a construction.
system.pynurb extractFinish with the polish pass. Chamfered edges are what make a print feel designed rather than extruded, so the doctrine's last step (, 1mm on exposed edges) stays in the part through every edit; the template emits already ends with it. When the user asks for a rounded rim on a closed wall, that is , the bead that survives a roofline rising and falling; hand-rolling it instead is how issue #55 spent 689 lines. Never round a rim unprompted: chamfer stays the default on every edge, rims included. Say so when you hand the part over, because the user can only ask for sharp edges if they know the chamfers are there on purpose. The viewer builds polished by default, and its polish button flips to the faster draft build.
polishnurb newcrown(wall)Say what the print costs before the user commits to it. drives the slicer they already have and comes back with the two numbers nothing upstream of it knows: how long the plate takes and what it weighs in grams, which is the unit a spool is sold in. Both are design feedback while the design can still move, because a wall going from 2 to 3mm is a shrug in the viewer and forty minutes on the bed, and nobody wants to learn that from a progress bar. Run it once the shape has settled and put the answer in the handoff ("about 50 minutes, 23g of filament"), and run it again when a change is the kind that costs time: infill volume, height, anything that adds a support. It needs the machine named once, which is the same line in the rules already use, and it never touches the printer or the network. If no slicer is installed it says so and still writes the 3MF. The viewer has the same answer on a button: print time is the toolbar's second row, it slices whatever the sliders are holding, and the duration replaces the button in place. If the machine has never been named it asks which printer this is for right there instead of sending the user to a file, and moving a slider puts the row back to its button, because that number was about the old shape. So tell the user the row exists once the first estimate is worth having, and expect them to have their own number in front of them.
nurb sliceprofileprinter.tomlnurb exportWhen the part is structural, put a number on "will it hold". presses a weight onto the part and says where the stress concentrates, how far it sags, and the weight it breaks at, quoted against layer adhesion because that is where an FDM print actually parts. It guesses the load on the highest big upward face and the hold on the largest downward one, says so, and plus aim it properly; repeats, one per mounting point, because a shelf on four hooks is held four times and fixing one big back face instead hides the stress at the joints, which is where these parts actually fail. Run it when a part carries a real load (a shelf, a hook, a bracket) and put the answer in the handoff ("holds about 30 kg; the weak point is the gusset root"). The viewer has the same answer on the stress button, and you aim it for the user before they ever press it: write a table in the card's settings block (, , ) naming where the weight realistically sits and every spot that carries the part, and the button opens already answered, with the picks placed and movable. You know where the part hangs because you built it; the user should not have to learn that vocabulary to get the heat map. Without the block the button guesses and shows its guess as movable markers. The block also takes when the user has said what they print in (PLA, PETG, ABS, ASA, Nylon, PC; TPU is refused because a stretchy plastic breaks none of these ways), and the answer says whether the part fails in the plastic or by splitting at the layer seams, computed from the print orientation, so a part that fails at the seams is a part worth reorienting or thickening where the seams carry tension. It is a voxel estimate, honest to about ±30%: trust where the hot spot is, treat the margin as a bearing rather than a promise, and when it comes back thin, thicken what the hot spot names rather than everything.
nurb stress--kg--at x,y,z--hold x,y,z[stress]kgload = [x, y, z]hold = [[x, y, z], ...]material = "PETG"The user does not know what they can ask for, so teach the viewer one affordance at a time. Most users have never driven CAD through chat: they do not realize "make it chunkier" or "it sags when I load it" is a complete instruction, so end every handoff with one sentence of what they can do next: drag the sliders, hit 3mf to print at the current values, or say what feels wrong in plain words. Beyond that standing close, at most one tip per reply, and only at the event that makes it relevant: the first taste parameter is the moment for "that is a slider, and when you find a setting you like, the write button makes it the part's new default"; a part that grows an interior is the moment for the section button; the end of a session is the moment for viewer.command, the double-click that reopens all of this without you. A tip at its moment reads as guidance; the same tip a reply earlier reads as noise, and every feature at once is a manual nobody asked for.
Write the card before you hand the part over, even for a single part. A part gets printed, used, and then revisited weeks later to adjust something, and by then the session that built it is gone. is the only record of what was tried and rejected, so without it the next agent helpfully re-adds the lead-in chamfer that was retired on purpose. There is no one-off: there is the first session and the ones after it, and the card is what you are leaving for them.
## Don'tnurb rules the doctrine, read this before designing
nurb api the vocabulary a part file gets, with signatures
nurb new <name> create parts/<name>.py and its card
nurb build [part] build once, report size and timing
nurb check [part] the printability rules; on an assembly, the motion sweep. --strict for CI
nurb inspect [part] faces, normals, concave edges, each finding on its face; --render pictures each finding
nurb scan <file> measure a mesh in mm, a phone scan or a downloaded model; --section z slices a profile polyline
nurb compare [part] deviation from the card's target mesh, both directions; --against <file> for a one-off
nurb card [part] regenerate a card's AUTO block
nurb diff [part] what moved since the card was written: size, volume, faces, verdict
nurb slice [part] print time and filament, from the slicer already installed
nurb stress [part] where a load stresses the part: peak MPa, sag, margin to breaking; --kg, --at/--hold x,y,z
nurb verify [part] the doctrine's verification list: solids, flex, checks, card. --report bundles verdict and renders into build/renders/
nurb render [part] PNG into build/renders/, so you can look at what you made; --section z:4mm cuts it open
nurb export [part] 3MF into build/, --formats for STL, STEP or GLB
nurb extract find duplication across parts
nurb dev watch, rebuild, serve the viewer on :7373 or the next free port
nurb launcher rewrite viewer.command, the double-clickable `nurb dev` a project is born withA standing preference is a file, not a flag you have to remember, and a printer is a fact about the workshop, not the project. When the user tells you what machine they own, record it once in () so no project ever asks again; check that file before asking, because they may have answered in an earlier project. The same file takes an table ( for a user who always wants STEP alongside), and every bare honors it. at the project root takes the same schema and wins where they disagree, which is what makes it the right place for the exception: the one project aimed at a different machine.
~/.config/nurb/config.tomlprofile = "bambu_a1_mini"[export]formats = ["3mf", "step"]nurb exportprinter.tomlRead before editing . Its section is what was tried and rejected, and it is the only place that records it.
parts/<name>.mdparts/<name>.py## Don'tIf is not on PATH: or .
nurbuv tool install nurbpip install nurb一个零件对应一个Python函数,其关键字参数的默认值即为零件的参数。项目指任意带有文件夹的目录;无需初始化步骤。你作为CAD操作员:用户描述零件需求,你负责完成其余所有工作,包括创建项目和建模。
parts/首先在后台启动,并将其URL交给用户。 它会输出一个URL,其提供的查看器是用户观察零件成型过程的地方:每次保存都会重新构建并渲染模型,且不会改变用户的视角。当你的Shell在用户本地机器运行时,参数可直接在用户屏幕打开查看器,无需跳转URL。因此要边保存边工作,不要静默操作。会生成一个可运行的零件;在第一分钟内就把这个块状草稿展示在屏幕上,然后在用户观看时逐步优化。交付URL时附上说明:如果用户打开查看器看到一个未说明的方块,无法区分这是占位符还是设计稿,而一句“这是我即将替换的初始模型”就能让同一个方块变成进度的体现。十分钟正确但不可见的建模会让用户以为程序卡住了。并且在每次回复末尾重复URL,包括交接时:聊天记录会滚动让链接消失,用户没看到的零件就无法评估。URL支持深度链接:会直接打开对应零件,会加载其卡片变体,因此要链接到你修改的具体内容,而非让用户在列表中查找。整个会话只需要一个服务器和一个标签页:进程会持续到会话结束,每次保存都会自动同步到已打开的查看器,因此永远不要重启来展示新内容,且仅在首次启动时使用,不要每次回复都用,因为多次打开会在用户屏幕堆叠相同的标签页。如果项目已在服务中,再次启动会拒绝并输出运行中的URL;直接复用该URL即可。
nurb dev--opennurb new?part=<name>&variant=<name>nurb dev--open当提示内容过时,先修复再建模。 启动信息包含版本检查:出现意味着包版本过旧,出现意味着当前文件比已安装的nurb版本旧。运行指定命令并告知用户,同步后重新读取技能文件,因为你之前使用的是过时副本。
nurb devnurb updatenurb skill --sync在开始操作前提供权限允许列表。 一次会话需要调用数十次并保存零件文件,如果每次操作都需要用户确认,二十分钟的无人值守工作会变成二十次用户必须参与的确认环节。如果你的工具支持按项目设置允许列表(在Claude Code中是),在项目的首次会话开始时,主动提议添加循环所需的权限,与已有的权限合并;如果用户拒绝则跳过:
nurb.claude/settings.local.jsonjson
{
"permissions": {
"allow": [
"Bash(nurb:*)",
"Edit(parts/**)",
"Edit(system.py)",
"Edit(measurements.toml)",
"Edit(printer.toml)"
]
}
}其他工具也需要相同权限,前缀和下的编辑权限以及,只是配置文件不同:Codex在中设置,Gemini在中设置,Cursor的CLI在中添加条目,OpenCode在中添加块。Amp无需提示,因此无需提供。
nurbparts/system.py.codex/rules/prefix_rule.gemini/settings.jsontools.allowed.cursor/cli.jsonShell(nurb)opencode.jsonpermission设计前先运行。 它会打印设计准则:可打印性、载荷路径、美学、抛光步骤、核心陷阱、卡片规范以及验证内容。本文件特意保持简洁,因此准则只有一份存于包内,不会出现版本差异。
nurb rules先调用工具再查看其源代码。 会打印零件文件可用的词汇及签名,因此要了解的返回值只需运行一个命令,无需进入site-packages目录查看。会测量已构建的零件:面面积、法线、哪些面位于打印床上、每个凹边,以及每个检测结果对应的面,单位与准则一致。这两个工具可以回答原本需要编写临时探测脚本才能解决的问题,而编写临时脚本是这个工作流中效率最低的习惯。当检测结果的修复方案不明显时,运行并查看结果:每个检测结果对应一张PNG,相机对准检测到的面并高亮该面。如果提示信息已经说明壁厚问题则无需图片,但任何基于猜测的编辑都会浪费一次重建周期,而查看图片可以避免这种情况。
nurb apiconcave_edgesnurb inspect <part>nurb inspect --render你的视觉判断只是假设,数据才能定论。 无论是你查看渲染图还是用户查看查看器,都只能发现问题,无法确认问题:看起来悬空的搁板可能只是放置和光照问题,看起来偏心的两个孔可能是视角问题,看起来太薄的壁可能恰好是用户实际测量的尺寸。因此在采取行动或反馈前,将所有视觉判断转化为可验证的数据:用检查准则覆盖的内容,用测量面面积、法线或哪些面位于打印床上,用确认编辑是否只修改了目标内容。然后告知用户数据而非主观印象,因为“凸台厚度为1.6mm,而你的打印机层厚为1.0mm”可以直接结束对话,而“你说得对,它看起来确实太薄”会引发无休止的讨论。这在用户提出视觉判断时尤为重要:“看起来不够牢固”只是症状,用户指向的零件可能没问题,而另一个零件可能存在问题,盲目加厚用户提到的零件会导致设计增加不必要的材料。
nurb checknurb inspectnurb diff建模前先询问,询问前先调研。 当零件需要与量产产品或公开标准配合时(如VESA支架、Gridfinity收纳盒、相机螺纹),参数存在于规格文档中:询问产品名称或链接,而非六个卡尺读数,查找规格文档,并将其记录在中,用字段注明来源。对于未公开的内容(如开口、支架、零件要固定的对象),则询问用户:一次性收集所有测量数据,如果工具支持则使用其提问功能,并按照准则记录答案。任何涉及偏好而非适配的内容应设为参数,而非提问,因为查看器的滑块是用户回答这类问题的方式。猜测比例只需要拖动一次滑块;猜测间隙则会打印出错误的零件。
measurements.tomlhow用用户的语言命名参数,因为参数名就是滑块标签。 每个关键字参数的默认值都会在查看器中显示为你拼写的名称,用户看到或时,只能通过拖动滑块猜测其作用。优先使用通俗语言而非行业术语(如而非;而非),说明该尺寸对应的对象,只有当用户先使用行业术语时才保留。两到三个单词的名称依然简洁;需要用户了解机械术语的名称属于错误的语言。名称只是标签:在函数的文档字符串中为每个参数添加一行的描述(如),因为当用户悬停在滑块上时,查看器会显示该行描述,这是控件无需你在场就能自我解释的方式。
pilot_diatip_bossscrew_hole_widthpilot_diabar_thicknessbarname: 作用shelf_depth: 搁板从墙面伸出的距离照片用于识别形状,扫描用于测量尺寸,试片用于验证适配。 当零件需要适配现实世界中的物体时,照片用于识别:它能告诉你壁板是荷兰式搭接还是2020型材,公开的型材参数包含具体尺寸。照片永远不能用于测量,因为像素没有毫米单位。当没有规格文档且无法使用卡尺时,请求用户进行十秒钟的手机扫描:Scaniverse是免费应用,可导出STL、OBJ或GLB网格,会读取扫描文件并以毫米为单位测量,说明使用的单位,可将横截面切片为足够短的多段线用于草图绘制。将扫描得到的数据记录在中,用字段注明文件名和切片信息,并标记所有基于扫描的适配为(临时),因为手机扫描属于参考几何而非计量数据。然后在打印完整零件前,打印适配试片:仅包含配合轮廓的薄条,只需几分钟耗材,用户将其与实物比对,一张照片就能验证扫描无法保证的适配性。当试片适配后,移除临时标记。
nurb scan <file>--section zmeasurements.tomlhowprovisional在形状成为问题时提议扫描,并为从未扫描过的用户编写指导。 获取数据比扫描更简单:“开口宽度是多少”只需要用户用卷尺测量并回答一个问题,因此当缺少尺寸时询问尺寸;当缺少的是无法用几个数字描述的形状时(如曲面、不规则物体、零件要贴合的墙面),提议扫描。没人知道手机可以做这件事,因此提议时要用用户能理解的语言一步到位:“快速3D扫描能让适配更精准:下载Scaniverse(免费),在Mesh模式下开始新扫描,缓慢用手机扫描壁板约十秒钟,导出为GLB格式,然后将文件上传到这里。” 说明模式,因为应用在扫描开始时会决定是splat模式还是mesh模式,splat是默认模式,而splat扫描只能导出点云(无表面的点),会拒绝处理并提示。因此如果收到点云格式的PLY文件,解决方案是重新用Mesh模式扫描(或如果应用支持,将扫描重新处理为mesh),不要导出同一个splat扫描的其他格式;用用户能理解的语言说明:“再扫描十秒钟即可”,而非告知失败。当文件上传后,用一句话反馈读取结果(“尺寸为300×250mm,我能看到板材间5mm的台阶”),这能让首次扫描的用户确认操作成功,然后按照上述步骤建模,先打印试片再打印完整零件。
nurb scan下载的模型是测量数据,而非起点,且在处理前告知用户。 当用户从MakerWorld、Printables或Thingiverse下载文件时,通常是要求你修改它,但实际情况是无法直接修改这类文件:网格由三角形组成,其壁厚是两个面之间的距离而非数值,任何编辑都无法生成可调整的参数。可读取文件,最后一行会说明是否能生成实体。3MF是nurb无法读取的格式之一,解决方案很简单:让用户在切片器中打开文件并将平台导出为STL。用用户能理解的语言明确告知你要做的事:“我无法直接编辑该文件,但我可以测量它并重新构建为可调整的零件,这需要几分钟时间。” 然后重新构建,不要标记下载文件为临时或要求打印试片:这些措施是因为手机扫描不够精准,而下载的文件精度可达微米级。扫描文件和下载文件在这里的读取方式相同,区别在于用户告知的信息,而非网格本身。下载文件真正有用的场景是作为零件需要避开的对象:测量它,围绕它建模口袋或支架,保留原文件不变。
nurb scan <file>重建网格时,将其设为零件的目标,让偏差驱动工作流。 将文件复制到项目中,并在卡片的设置块中添加(表格形式可在文件需要时添加字段)。查看器会将网格显示为琥珀色虚影叠加在零件上,target按钮可隐藏它,角落会显示两个每次保存都会刷新的数值:零件表面与原模型的偏差距离,以及原模型表面未被零件重现的比例。会打印相同的两个方向的偏差,包含最差、典型和95百分位距离,可进行一次性比对而无需修改卡片。分别读取两个方向的偏差,不要取平均值:网格是基准,原模型没有的材料即使整体偏差看起来很小也是错误的。比对会将目标与零件居中对齐,不进行旋转,因此要在网格自身的方向建模,较大的对称数值通常意味着坐标系不匹配而非形状错误。
target = "scans/original.stl"unitsnurb compare--against <file>import_stl当零件需要配合工作时,先组装再打印。 需要安装到其他零件或与其他零件相对运动的零件需要添加:中的一个函数,返回放置好的实体,用构建关联零件,声明运动方式,代表零件要安装的机器或墙面。会扫描每个关节的运动范围,报告卡住的角度和接触位置,这是单个零件检查无法发现的故障:如门的模型构建干净、检查通过、打印完美,但无法打开。关节角度是关键字参数的默认值,因此查看器的滑块可让用户实时摆动装配体。根据用户的测量数据建模障碍物,并在装配体卡片上说明其精度。
@assemblyparts/use()hinge()obstacle()nurb check零件族将共享设计存于一个文件中。 当多个零件需要匹配时,共享的壁、倒角、圆角半径或标签几何存于项目根目录的中,被所有零件导入;保存该文件会重建整个项目,因此一次编辑即可修改所有零件。但系统是提取出来的,而非预先搭建:先简单构建前两三个零件,然后运行,它会报告文件中重复的结构。提取真正共享的内容,保留其余部分,因为两个零件有相同内容还不是系统;只有两个零件都需要修改的内容才是系统。当三个或更多零件重复某个结构时,查看器会进行相同的扫描,并在参数下悄悄添加“与其他零件共享”的说明。
system.pynurb extract最后进行抛光步骤。 倒角边缘能让打印件看起来是设计出来的而非挤出成型的,因此准则的最后一步(,暴露边缘倒角1mm)会保留在零件的每次编辑中;生成的模板已经包含该步骤。当用户要求封闭壁的边缘圆角时,使用,这是能适应屋顶线起伏的圆角;手动构建会像issue #55那样花费689行代码。不要未经提示就给边缘圆角:所有边缘默认使用倒角,包括边缘。交付零件时告知用户这一点,因为只有用户知道倒角是特意添加的,才会要求保留尖锐边缘。查看器默认构建抛光后的模型,其polish按钮可切换为更快的草稿构建模式。
polishnurb newcrown(wall)在用户决定打印前告知打印成本。 调用用户已安装的切片器,返回两个上游工具无法获取的数值:打印时间和耗材重量(克,这是线轴的销售单位)。这两个数值是设计过程中的反馈,因为壁从2mm变为3mm在查看器中变化不大,但会增加四十分钟打印时间,没人想从进度条中得知这一点。当形状确定后运行一次,并在交付时告知结果(“约50分钟,23g耗材”),当修改会影响时间时再次运行:如填充率、高度、任何需要添加支撑的修改。只需指定一次机器,即中准则已使用的字段,它不会连接打印机或网络。如果未安装切片器,它会提示,仍会导出3MF文件。查看器的按钮也能显示该信息:print time在工具栏第二行,它会对滑块当前设置的模型进行切片,时长会替换按钮显示。如果从未指定机器,它会直接询问用户使用的打印机,而非让用户去修改文件,拖动滑块会让该行恢复为按钮,因为数值对应的是旧模型。因此当首次估算有价值时,告知用户该行的存在,用户可能已经有自己的估算。
nurb sliceprinter.tomlprofilenurb export当零件是结构件时,给出“能否承重”的数值。 会对零件施加重量,报告应力集中位置、下垂量以及断裂载荷,并与层间附着力对比,因为FDM打印件通常在层间断裂。它会猜测最大向上面的载荷和最大向下面的支撑点,并说明,加可精准定位;可重复设置,每个安装点一次,因为四个挂钩的搁板有四个支撑点,而固定一个大背面会隐藏关节处的应力,这是这类零件实际断裂的位置。当零件需要承载实际载荷时(如搁板、挂钩、支架)运行该命令,并在交付时告知结果(“可承重约30kg;薄弱点在角撑根部”)。查看器的stress按钮也能显示该信息,你要在用户点击前为其设置好:在卡片的设置块中添加表格(、、),指定重量的实际位置和每个支撑点,按钮打开时会直接显示结果,且选择点可移动。你知道零件的悬挂位置,因为是你构建的;用户无需学习相关术语就能查看热图。如果没有该块,按钮会进行猜测并显示可移动的标记。当用户说明打印材料时,该块还可添加(支持PLA、PETG、ABS、ASA、Nylon、PC;TPU会被拒绝,因为弹性塑料不会以这种方式断裂),结果会说明零件是塑料断裂还是层间开裂,这是根据打印方向计算的,因此层间开裂的零件需要重新定向或加厚层间受拉的位置。这是体素估算,精度约为±30%:信任热点位置,将余量视为参考而非承诺,当余量不足时,加厚热点位置而非整个零件。
nurb stress--kg--at x,y,z--hold x,y,z[stress]kgload = [x, y, z]hold = [[x, y, z], ...]material = "PETG"用户不知道可以请求什么,因此每次教一个查看器功能。 大多数用户从未通过聊天使用CAD:他们不知道“让它更厚实”或“加载时会下垂”是完整的指令,因此每次交付时都要添加一句用户下一步可以做什么:拖动滑块、点击3mf按钮按当前值打印,或用通俗语言说明哪里不满意。除此之外,最多每次回复一个提示,且只在相关场景下提供:第一个偏好参数出现时,提示“这是一个滑块,当你找到喜欢的设置时,点击write按钮可将其设为零件的新默认值”;当零件添加内部结构时,提示section按钮;会话结束时,提示viewer.command,双击可重新打开所有内容而无需你参与。在合适的时机提供提示是指导;提前提供提示是干扰,一次性展示所有功能则是没人要的手册。
交付零件前先编写卡片,即使是单个零件。 零件会被打印、使用,几周后可能需要重新调整,而当时的会话记录已经消失。部分记录了尝试过并被拒绝的内容,没有它,下一个代理可能会重新添加特意取消的导入倒角。没有一次性的项目:只有第一次会话和后续会话,卡片是你留给后续会话的记录。
## Don'tnurb rules 设计准则,设计前阅读
nurb api 零件文件可用的词汇及签名
nurb new <name> 创建parts/<name>.py及其卡片
nurb build [part] 构建一次,报告尺寸和耗时
nurb check [part] 可打印性检查;针对装配体则检查运动范围。--strict用于CI
nurb inspect [part] 面、法线、凹边,每个检测结果对应其面;--render为每个检测结果生成图片
nurb scan <file> 测量网格(毫米单位),支持手机扫描或下载的模型;--section z生成轮廓多段线切片
nurb compare [part] 与卡片目标网格的偏差,两个方向;--against <file>用于一次性比对
nurb card [part] 重新生成卡片的AUTO块
nurb diff [part] 自卡片编写以来的变化:尺寸、体积、面、验证结果
nurb slice [part] 打印时间和耗材重量,调用已安装的切片器
nurb stress [part] 载荷下零件的应力分布:峰值MPa、下垂量、断裂余量;--kg, --at/--hold x,y,z
nurb verify [part] 准则的验证列表:实体、柔性、检查、卡片。--report将验证结果和渲染图打包到build/renders/
nurb render [part] 生成PNG到build/renders/,用于查看模型;--section z:4mm生成横截面
nurb export [part] 导出3MF到build/,--formats支持STL、STEP或GLB
nurb extract 查找零件间的重复结构
nurb dev 监听文件变化、重建模型、在:7373或下一个可用端口提供查看器服务
nurb launcher 重写viewer.command,双击即可启动`nurb dev`的项目启动器固定偏好存于文件中,而非需要记住的标志;打印机是车间的配置,而非项目的配置。当用户告知你他们拥有的机器时,在中记录一次(),这样所有项目都无需再次询问;询问前先检查该文件,因为用户可能在之前的项目中已经回答过。该文件还可添加表格(如适用于总是需要STEP文件的用户),所有不带参数的都会遵循该设置。项目根目录的使用相同的 schema,当与全局配置冲突时优先使用项目配置,这适用于例外情况:针对不同机器的项目。
~/.config/nurb/config.tomlprofile = "bambu_a1_mini"[export]formats = ["3mf", "step"]nurb exportprinter.toml编辑前先阅读。其部分记录了尝试过并被拒绝的内容,这是唯一的记录。
parts/<name>.pyparts/<name>.md## Don't如果不在PATH中:运行或。",
nurbuv tool install nurbpip install nurb