Repository navigation
Useful constants, render(), ring(), squorus(), label(), thread(), knurl() #133
Description
Activity
This is great! I'd like to incorporate a couple of these things into
solid.utils. Doable?
I'm, uh, blissfully ignorant about the fine points of licensing, so please excuse a couple questions.If I were to put, say,
label()intosolid.utils, is there particular attribution you'd want outside the docstring? The rest of SolidPython is all LGPL 2.1. When people make contributions, I usually put a name and link in the docstring and call it good, but you've already got all your metadata set up-- what do you think is the best way to combine those two?Totally doable - that's why I posted it here (I'm not really into the whole git hacking thing).
Just cut-and-paste whatever you like - I don't mind and hereby give permission.
Make changes if you like, too. Yesterday I made a few minor improvements (mostly in label). It's frustrating that we don't know exactly what all the text() parameters really do except by trial-and-error.
Latest version (today) is below:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Nerdfever.com personal Python library for SolidPython. """ __author__ = 'NerdFever.com' __copyright__ = 'Copyright 2018-2019 NerdFever.com' __version__ = '' __email__ = 'dave@nerdfever.com' __status__ = 'Development' __license__ = """Copyright 2018-2019 NerdFever.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ ''' Modifiers: .set_modifier("*") # disable aka disable() .set_modifier("!") # show only aka root() .set_modifier("#") # highlight aka debug() .set_modifier("%") # transparent aka background()) ''' import os, math import numpy as np from solid import * from solid.utils import * from solid import screw_thread #from nerdlib import * # controls number of segments in rendered circles (==$fn in OpenSCAD) SEGMENTS = 128 ''' CONSTANTS ''' ZortraxM200BuildVolume = [200, 200, 180] # x, y, z, max # these work on Z-ABS at layer size 0.14 to 0.19 mm (at least - haven't tested enough otherwise) # all are per side, use double for diameters # NOTE - These work at 0.19 mm/layer, normal quality. # At 0.09mm/layer + high quality, they are all a little loose. TightFit = 0.1875 # considerable resistance - stays put SnugFit = 0.25 # virtually zero contact LooseFit = 0.50 VeryLooseFit = 0.75 # Major (outer) diameters - ref https://en.wikipedia.org/wiki/Unified_Thread_Standard Screw_0_80_Major_Diameter = 1.5240 Screw_1_64_Major_Diameter = 1.8542 Screw_2_56_Major_Diameter = 2.1844 Screw_4_40_Major_Diameter = 2.8448 Screw_6_32_Major_Diameter = 3.5052 Screw_10_24_Major_Diameter = 4.8260 Screw_10_24_Drill = 3.7973 # #25 drill Screw_M3_Major_Diameter = 2.93 Screw_M3_Head_Diameter = 5.25 # Lens mounts (FFD = flange focal distance) Pentax_Q_FFD = 9.2 Leica_M_FFD = 27.8 B4_1_2inch_FFD = 35.74 # 1/2" standard B4_1_2inch_Sony_FFD = 38.0 # 1/2" Sony B4_2_3inch_FFD = 48.0 # 2/3" C_Mount_FFD = 17.526 # 0.69" C_Mount_Diameter = 25.4 # 1.0" C_Mount_Threads_Per_MM = 32/25.4 # 32.0 TPI CS_Mount_FFD = C_Mount_FFD - 5.0 CS_Mount_Diameter = C_Mount_Diameter CS_Mount_Threads_Per_MM = C_Mount_Threads_Per_MM def render(object, baseFilename=None, prefix="", suffix=""): """ Renders object, outputs to base filename with optional prefixes and suffixes added""" if baseFilename == None: import __main__ as main baseFilename = os.path.splitext(main.__file__)[0] scad_render_to_file(object, prefix+baseFilename+suffix + ".scad", file_header='$fn = %s;' % SEGMENTS, include_orig_code=True) def ring(od, id, h, center=False): """ Returns a ring (cylinder with a hole in it). od = outer diameter id = inner diameter h = height """ if center==True: return cylinder(d=od, h=h, center=True) - cylinder(d=id, h=h+1, center=True)#.set_modifier("#") else: return cylinder(d=od, h=h) - down(1)(cylinder(d=id, h=h+2))#.set_modifier("#") def squorus(wall, outer, center=False): """ Creates a "squorus" (square torus; a cube with a square hole thru it). Argument names are optional if given in the order shown here. The hole is centered about the z axis. When center==True, it is also centered vertically along the z axis. (similar to "cylinder") As with "cube" if you give a single scalar as "outer", it will make all sides that same length. wall = wall thickness outer = tuple or list (x,y,z) of outer dimensions (or scalar) (same as "cube") center == False (default), squorus is in the 1st (positive) octant, one corner at (0,0,0) (same as "cube") center == True, squorus is centered at (0,0,0) (same as "cube") """ if type(outer) not in (tuple, list): outer = ( outer, outer, outer ) inner = (outer[0] - wall*2, outer[1] - wall*2, outer[2]+1) # +1 ensures hole extends past wall of outer cube (avoid z-fighting in OpenSCAD preview; https://github.com/openscad/openscad/issues/1793) if center: thing = cube(outer, center) - cube(inner, center) else: thing = cube(outer, center) - translate([wall, wall,-0.5])(cube(inner, center)) return thing def label(string, width=15, halign="left", valign="baseline", size=10, depth=0.5, lineSpacing=1.15, font="MgOpen Modata:style=Bold", segments=40, spacing=1): """Renders a multi-line string into a single 3D object.""" lines = string.splitlines() texts = [] for idx, l in enumerate(lines): t = text(text=l, halign=halign, valign=valign, font=font, spacing=spacing).add_param('$fn', segments) t = linear_extrude(height=1)(t) t = translate([0, -size * idx * lineSpacing, 0])(t) texts.append(t) result = union()(texts) result = resize([width, 0, depth])(result) result = translate([0, (len(lines)-1)*size / 2, 0])(result) return result def thread(diameter, length, lead, tooth=None, toothBite=None, toothBottom=None, toothTop=None, external=True, starts=1, neckInDegrees=0, neckOutDegrees=0, segments=64, ): ''' Makes an internal or external screw thread. diameter - of the base cylinder to which threads are referenced length - Z axis length of the thread (goes bottom to top) lead - Z distance the thread moves per turn (if starts==1, this is the pitch) tooth - the tooth profile (see below). if None, the default profile is used. toothBite - the extended radius of the tooth (from the base cylinder) [only if using default tooth profile] toothBottom - Z size of base of tooth [only if using default tooth profile] toothTop - Z size of end of tooth [only if using default tooth profile] external - if True makes an external thread (external to base cyldiner). Otherwise, an internal thread. starts - number of thread starts neckInDegrees - degrees used to feather bottom toothBite neckOutDegrees - degrees used to feather top toothBite segments - number of circle segments ($fs in OpenSCAD). More is smoother, but takes longer to render. The default thread profile will produce either a isosceles triangle profile (if tooothTop == 0), or an isosceles trapezoid profile (if toothTop > 0); this is a triangle with a truncated (flat) top. Re pitch and lead: pitch (Z distance between threads) == lead / starts, and, lead (Z distance per turn) == pitch * starts ''' if tooth == None: # if toothTop==0, this gives a triangular thread. If toothTop>0, gives a thread with a flat (truncated) top. tooth = [[0, -toothBottom / 2], [toothBite, -toothTop / 2], [toothBite, +toothTop / 2], [0, +toothBottom / 2]] thread = [] for twist in np.linspace(0,360,num=starts,endpoint=False).tolist(): start = screw_thread.thread(outline_pts=tooth, inner_rad=diameter/2, external=external, pitch=lead, length=length, segments_per_rot=segments, neck_in_degrees=neckInDegrees, neck_out_degrees=neckOutDegrees) start = rotate([0,0,twist])(start) thread += start return thread def knurl(d, h, r=1, numKnurls=360/15, center=False): """ Makes simple knurls that wrap around a cylinder in z. (Useful for control knobs or thumbscrews.) It places knurls on the outside of a cylinder of diameter d and height h. Each knurl is a 90 degree corner. Knurls extend to an additional radius of r (resulting outside diameter is therefore d+2*r). d is the diamter of the base cylinder (not including knurls) h is the height r is the additional radius of each knurl (in addition the cylinder radius) numKnurls is the number of knurls around the cylinder (rounded up to the next multiple of 4). center is the same as for "cylinder" OpenSCAD primitive """ knurlSide = math.sqrt((d+2*r)**2/2) knurl = cube([knurlSide, knurlSide, h])#.set_modifier("#") knurl = translate([-knurlSide/2, -knurlSide/2, 0])(knurl) knurls = [] degreeStep = int(90/math.ceil(numKnurls/4)) for deg in range(0, 90, degreeStep): knurls += rotate([0,0,deg])(knurl) knurls -= down(1)(cylinder(d=d, h=h+2)) if center: knurls = down(h/2)(knurls) return knurls if __name__ == '__main__': def main(): offset = 0 increment = 20 thing = [] def demo(d): nonlocal offset, thing thing += right(offset)(d) offset += increment demo( ring(10, 5, 2, center=False) ) demo( ring(10, 5, 2, center=True) ) demo( squorus(3, 10, center=False) ) demo( squorus(3, [10, 20, 5], center=False) ) demo( squorus(3, 10, center=True) ) demo( squorus(3, [10, 20, 5], center=True) ) demo( label("""Hello\nworld!""") ) demo( thread(diameter=5, length=10, lead=3, toothBite=2, toothBottom=1, toothTop=1, starts=2, neckInDegrees=180, neckOutDegrees=60, external=True) ) demo( knurl(d=5, h=3) ) demo( knurl(d=5, h=3, center=True) ) render(thing) main()Right on. Free use is best use! I guess I'll leave this issue open for the time being to increase visibility. I'm sure there's a better way to make extra code available to people, but I'm not sure what it would be just yet.
Maybe useful to somebody. Just run it and look at the resulting .scad to see what it can do.
Redistribution is welcome (free).