From f0960f93379de573461c091037f71e0b7b68c689 Mon Sep 17 00:00:00 2001 From: Elmo Date: Mon, 23 Aug 2010 20:51:25 +0800 Subject: [PATCH 1/3] Corrections spotted by tests --- gears.scad | 40 +++++++++++++++++----------------- involute_gears.scad | 52 ++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/gears.scad b/gears.scad index 9818431..9deacb1 100644 --- a/gears.scad +++ b/gears.scad @@ -1,6 +1,6 @@ //test_involute_curve(); -test_gears(); -demo_3d_gears(); +//test_gears(); +//demo_3d_gears(); // Geometry Sources: // http://www.cartertools.com/involute.html @@ -15,14 +15,14 @@ module gear(number_of_teeth, pressure_angle=20, clearance = 0) { if (circular_pitch==false && diametral_pitch==false) echo("MCAD ERROR: gear module needs either a diametral_pitch or circular_pitch"); - + //Convert diametrial pitch to our native circular pitch circular_pitch = (circular_pitch!=false?circular_pitch:180/diametral_pitch); - + // Pitch diameter: Diameter of pitch circle. pitch_diameter = number_of_teeth * circular_pitch / 180; pitch_radius = pitch_diameter/2; - + // Base Circle base_diameter = pitch_diameter*cos(pressure_angle); base_radius = base_diameter/2; @@ -32,24 +32,24 @@ module gear(number_of_teeth, // Addendum: Radial distance from pitch circle to outside circle. addendum = 1/pitch_diametrial; - + //Outer Circle outer_radius = pitch_radius+addendum; outer_diameter = outer_radius*2; - + // Dedendum: Radial distance from pitch circle to root diameter dedendum = addendum + clearance; // Root diameter: Diameter of bottom of tooth spaces. root_radius = pitch_radius-dedendum; root_diameter = root_radius * 2; - + half_thick_angle = 360 / (4 * number_of_teeth); - + union() { rotate(half_thick_angle) circle($fn=number_of_teeth*2, r=root_radius*1.001); - + for (i= [1:number_of_teeth]) //for (i = [0]) { @@ -76,32 +76,32 @@ module involute_gear_tooth( ) { pitch_to_base_angle = involute_intersect_angle( base_radius, pitch_radius ); - + outer_to_base_angle = involute_intersect_angle( base_radius, outer_radius ); - + base1 = 0 - pitch_to_base_angle - half_thick_angle; pitch1 = 0 - half_thick_angle; outer1 = outer_to_base_angle - pitch_to_base_angle - half_thick_angle; - + b1 = polar_to_cartesian([ base1, base_radius ]); p1 = polar_to_cartesian([ pitch1, pitch_radius ]); o1 = polar_to_cartesian([ outer1, outer_radius ]); - + b2 = polar_to_cartesian([ -base1, base_radius ]); p2 = polar_to_cartesian([ -pitch1, pitch_radius ]); o2 = polar_to_cartesian([ -outer1, outer_radius ]); - + // ( root_radius > base_radius variables ) pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius ); root1 = pitch1 - pitch_to_root_angle; root2 = -pitch1 + pitch_to_root_angle; r1_t = polar_to_cartesian([ root1, root_radius ]); r2_t = polar_to_cartesian([ -root1, root_radius ]); - + // ( else ) r1_f = polar_to_cartesian([ base1, root_radius ]); r2_f = polar_to_cartesian([ -base1, root_radius ]); - + if (root_radius > base_radius) { //echo("true"); @@ -115,7 +115,7 @@ module involute_gear_tooth( r1_f, b1,p1,o1,o2,p2,b2,r2_f ], convexity = 3); } - + } // Mathematical Functions @@ -157,7 +157,7 @@ module demo_3d_gears() translate([0,0,10]) linear_extrude(height = 10, center = true, convexity = 10, twist = 45) gear(number_of_teeth=17,diametral_pitch=1); } - + //spur gear translate([0,-50]) linear_extrude(height = 10, center = true, convexity = 10, twist = 0) gear(number_of_teeth=17,diametral_pitch=1); @@ -170,4 +170,4 @@ module test_involute_curve() { translate(polar_to_cartesian([involute_intersect_angle( 0.1,i) , i ])) circle($fn=15, r=0.5); } -} \ No newline at end of file +} diff --git a/involute_gears.scad b/involute_gears.scad index c6e6bdc..8780ae1 100644 --- a/involute_gears.scad +++ b/involute_gears.scad @@ -32,13 +32,13 @@ module test_bevel_gear_pair(){ module test_bevel_gear(){bevel_gear();} -bevel_gear(); +//bevel_gear(); pi=3.1415926535897932384626433832795; //================================================== // Bevel Gears: -// Two gears with the same cone distance, circular pitch (measured at the cone distance) +// Two gears with the same cone distance, circular pitch (measured at the cone distance) // and pressure angle will mesh. module bevel_gear_pair ( @@ -49,7 +49,7 @@ module bevel_gear_pair ( { outside_pitch_radius1 = gear1_teeth * outside_circular_pitch / 360; outside_pitch_radius2 = gear2_teeth * outside_circular_pitch / 360; - pitch_apex1=outside_pitch_radius2 * sin (axis_angle) + + pitch_apex1=outside_pitch_radius2 * sin (axis_angle) + (outside_pitch_radius2 * cos (axis_angle) + outside_pitch_radius1) / tan (axis_angle); cone_distance = sqrt (pow (pitch_apex1, 2) + pow (outside_pitch_radius1, 2)); pitch_apex2 = sqrt (pow (cone_distance, 2) - pow (outside_pitch_radius2, 2)); @@ -68,7 +68,7 @@ module bevel_gear_pair ( cone_distance=cone_distance, pressure_angle=30, outside_circular_pitch=outside_circular_pitch); - + rotate([0,-(pitch_angle1+pitch_angle2),0]) translate([0,0,-pitch_apex2]) bevel_gear ( @@ -125,7 +125,7 @@ module bevel_gear ( // Calculate and display the pitch angle. This is needed to determine the angle to mount two meshing cone gears. // Base Circle for forming the involute teeth shape. - base_radius = back_cone_radius * cos (pressure_angle); + base_radius = back_cone_radius * cos (pressure_angle); // Diametrial pitch: Number of teeth per unit length. pitch_diametrial = number_of_teeth / outside_pitch_diameter; @@ -143,9 +143,9 @@ module bevel_gear ( root_cone_full_radius = tan (root_angle)*apex_to_apex; back_cone_full_radius=apex_to_apex / tan (pitch_angle); - back_cone_end_radius = - outside_pitch_radius - - dedendum * cos (pitch_angle) - + back_cone_end_radius = + outside_pitch_radius - + dedendum * cos (pitch_angle) - gear_thickness / tan (pitch_angle); back_cone_descent = dedendum * sin (pitch_angle) + gear_thickness; @@ -158,9 +158,9 @@ module bevel_gear ( face_cone_height = apex_to_apex-face_width / cos (pitch_angle); face_cone_full_radius = face_cone_height / tan (pitch_angle); face_cone_descent = dedendum * sin (pitch_angle); - face_cone_end_radius = + face_cone_end_radius = outside_pitch_radius - - face_width / sin (pitch_angle) - + face_width / sin (pitch_angle) - face_cone_descent / tan (pitch_angle); // For the bevel_gear_flat finish option, calculate the height of a cube to select the portion of the gear that includes the full pitch face. @@ -198,7 +198,7 @@ module bevel_gear ( { translate ([0,0,-back_cone_descent]) cylinder ( - $fn=number_of_teeth*2, + $fn=number_of_teeth*2, r1=back_cone_end_radius, r2=back_cone_full_radius*2, h=apex_to_apex + back_cone_descent); @@ -211,7 +211,7 @@ module bevel_gear ( bevel_gear_flat_height]); } } - + if (finish == bevel_gear_back_cone) { translate ([0,0,-face_cone_descent]) @@ -223,7 +223,7 @@ module bevel_gear ( translate ([0,0,pitch_apex - apex_to_apex]) cylinder (r=bore_diameter/2,h=apex_to_apex); - } + } } module involute_bevel_gear_tooth ( @@ -247,9 +247,9 @@ module involute_bevel_gear_tooth ( min_radius = max (base_radius*2,root_radius*2); - pitch_point = + pitch_point = involute ( - base_radius*2, + base_radius*2, involute_intersect_angle (base_radius*2, back_cone_radius*2)); pitch_angle = atan2 (pitch_point[1], pitch_point[0]); centre_angle = pitch_angle + half_thick_angle; @@ -310,7 +310,7 @@ module gear ( involute_facets=0, flat=false) { - if (circular_pitch==false && diametral_pitch==false) + if (circular_pitch==false && diametral_pitch==false) echo("MCAD ERROR: gear module needs either a diametral_pitch or circular_pitch"); //Convert diametrial pitch to our native circular pitch @@ -387,7 +387,7 @@ module gear ( circle (r=bore_diameter/2); if (circles>0) { - for(i=[0:circles-1]) + for(i=[0:circles-1]) rotate([0,0,i*360/circles]) translate([circle_orbit_diameter/2,0,-1]) linear_exturde_flat_option(flat =flat, height=max(gear_thickness,rim_thickness)+3) @@ -488,15 +488,15 @@ function involute_intersect_angle (base_radius, radius) = sqrt (pow (radius/base // Calculate the involute position for a given base radius and involute angle. -function rotated_involute (rotate, base_radius, involute_angle) = +function rotated_involute (rotate, base_radius, involute_angle) = [ cos (rotate) * involute (base_radius, involute_angle)[0] + sin (rotate) * involute (base_radius, involute_angle)[1], cos (rotate) * involute (base_radius, involute_angle)[1] - sin (rotate) * involute (base_radius, involute_angle)[0] ]; -function mirror_point (coord) = +function mirror_point (coord) = [ - coord[0], + coord[0], -coord[1] ]; @@ -506,7 +506,7 @@ function rotate_point (rotate, coord) = cos (rotate) * coord[1] - sin (rotate) * coord[0] ]; -function involute (base_radius, involute_angle) = +function involute (base_radius, involute_angle) = [ base_radius*(cos (involute_angle) + involute_angle*pi/180*sin (involute_angle)), base_radius*(sin (involute_angle) - involute_angle*pi/180*cos (involute_angle)), @@ -523,7 +523,7 @@ module test_gears() gear (number_of_teeth=17, circular_pitch=500, circles=8); - + rotate ([0,0,360*4/17]) translate ([39.088888,0,0]) { @@ -587,7 +587,7 @@ module test_gears() circles=0); } } - + rotate ([0,0,360*-5/17]) translate ([44.444444444,0,0]) gear (number_of_teeth=15, @@ -598,7 +598,7 @@ module test_gears() gear_thickness=4, hub_thickness=6, circles=9); - + rotate ([0,0,360*-1/17]) translate ([30.5555555,0,-1]) gear (number_of_teeth=5, @@ -612,7 +612,7 @@ module test_gears() module meshing_double_helix () { test_double_helix_gear (); - + mirror ([0,1,0]) translate ([58.33333333,0,0]) test_double_helix_gear (teeth=13,circles=6); @@ -676,7 +676,7 @@ module test_backlash () bore_diameter=5, backlash = 2, circles=8); - + rotate ([0,0,360/teeth/4]) gear ( number_of_teeth = teeth, From 30607913a3f51f9aec9a2c882d536513fa384209 Mon Sep 17 00:00:00 2001 From: Elmo Date: Thu, 26 Aug 2010 16:05:04 +0800 Subject: [PATCH 2/3] Small improvements to the testing infra. --- openscad_utils.py | 30 +++++++++++++++++------------- test_compile.py | 28 +++++++++++++++------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/openscad_utils.py b/openscad_utils.py index 51f383a..b7a48d5 100644 --- a/openscad_utils.py +++ b/openscad_utils.py @@ -4,20 +4,21 @@ from subprocess import Popen, PIPE mod_re = (r"\bmodule\s+(", r")\s*\(\s*") func_re = (r"\bfunction\s+(", r")\s*\(") -def extract_mod_names(fpath, name_re=r"\w+"): - regex = name_re.join(mod_re) +def extract_definitions(fpath, name_re=r"\w+", def_re=""): + regex = name_re.join(def_re) matcher = re.compile(regex) return (m.group(1) for m in matcher.finditer(fpath.read())) +def extract_mod_names(fpath, name_re=r"\w+"): + return extract_definitions(fpath, name_re=name_re, def_re=mod_re) + def extract_func_names(fpath, name_re=r"\w+"): - regex = name_re.join(func_re) - matcher = re.compile(regex) - return (m.group(1) for m in matcher.finditer(fpath.read())) + return extract_definitions(fpath, name_re=name_re, def_re=func_re) -def collect_test_modules(): - dirpath = py.path.local("./") +def collect_test_modules(dirpath=None): + dirpath = dirpath or py.path.local("./") print "Collecting openscad test module names" - + test_files = {} for fpath in dirpath.visit('*.scad'): #print fpath @@ -26,21 +27,24 @@ def collect_test_modules(): test_files[fpath] = modules return test_files -collect_test_modules() +class Timeout(Exception): pass -def call_openscad(path, stlpath, timeout=20): +def call_openscad(path, stlpath, timeout=1): try: - proc = Popen(['openscad', '-s', str(stlpath), str(path)], + command = ['openscad', '-s', str(stlpath), str(path)] + print command + proc = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True) calltime = time.time() + time.sleep(0.01) #print calltime while True: if proc.poll() is not None: break - time.sleep(0.1) + time.sleep(0.5) #print time.time() if time.time() > calltime + timeout: - raise Exception("Timeout") + raise Timeout() finally: try: proc.terminate() diff --git a/test_compile.py b/test_compile.py index 2208186..a50e582 100644 --- a/test_compile.py +++ b/test_compile.py @@ -2,21 +2,22 @@ import py from openscad_utils import * -def pytest_generate_tests(metafunc): - if "modpath" in metafunc.funcargnames: - if "modname" in metafunc.funcargnames: - for fpath, modnames in collect_test_modules().items(): - for modname in modnames: - metafunc.addcall(funcargs=dict(modname=modname, modpath=fpath)) - else: - dirpath = py.path.local("./") - for fpath in dirpath.visit('*.scad'): - metafunc.addcall(funcargs=dict(modpath=fpath)) temppath = py.test.ensuretemp('MCAD') +def pytest_generate_tests(metafunc): + if "modpath" in metafunc.funcargnames: + for fpath, modnames in collect_test_modules().items(): + os.system("cp %s %s/" % (fpath, temppath)) + if "modname" in metafunc.funcargnames: + for modname in modnames: + metafunc.addcall(funcargs=dict(modname=modname, modpath=fpath)) + else: + metafunc.addcall(funcargs=dict(modpath=fpath)) + + def test_compile(modname, modpath): - tempname = "test_" + modpath.basename + modname + tempname = "test_" + modpath.basename + modname + '.scad' fpath = temppath.join(tempname) stlpath = temppath.join(tempname + ".stl") f = fpath.open('w') @@ -24,10 +25,10 @@ def test_compile(modname, modpath): //generated testfile include <%s> -%s() +%s(); """ % (modpath, modname)) f.flush - output = call_openscad(path=fpath, stlpath=stlpath) + output = call_openscad(path=fpath, stlpath=stlpath, timeout=5) print output assert output[0] is 0 assert "warning" or "error" not in output[2].strip().lowercase() @@ -40,5 +41,6 @@ def test_compile_default(modpath): print output assert output[0] is 0 assert "warning" or "error" not in output[2].strip().lowercase() + assert len(stlpath.readlines()) == 2 From d07f846e30f077fdb24dcc074ff5e9de32a5ad34 Mon Sep 17 00:00:00 2001 From: Elmom Date: Thu, 26 Aug 2010 17:04:17 +0800 Subject: [PATCH 3/3] Added code from the openscad svn repo for easy use and improvement. --- boxes.scad | 43 ++++++++++++++++ shapes.scad | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ triangles.scad | 4 +- 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 boxes.scad create mode 100644 shapes.scad diff --git a/boxes.scad b/boxes.scad new file mode 100644 index 0000000..d9cb1ee --- /dev/null +++ b/boxes.scad @@ -0,0 +1,43 @@ +// Library: boxes.scad +// Version: 1.0 +// Author: Marius Kintel +// Copyright: 2010 +// License: BSD + +// roundedBox([width, height, depth], float radius, bool sidesonly); + +// EXAMPLE USAGE: +// roundedBox([20, 30, 40], 5, true); + +// size is a vector [w, h, d] +module roundedBox(size, radius, sidesonly) +{ + rot = [ [0,0,0], [90,0,90], [90,90,0] ]; + if (sidesonly) { + cube(size - [2*radius,0,0], true); + cube(size - [0,2*radius,0], true); + for (x = [radius-size[0]/2, -radius+size[0]/2], + y = [radius-size[1]/2, -radius+size[1]/2]) { + translate([x,y,0]) cylinder(r=radius, h=size[2], center=true); + } + } + else { + cube([size[0], size[1]-radius*2, size[2]-radius*2], center=true); + cube([size[0]-radius*2, size[1], size[2]-radius*2], center=true); + cube([size[0]-radius*2, size[1]-radius*2, size[2]], center=true); + + for (axis = [0:2]) { + for (x = [radius-size[axis]/2, -radius+size[axis]/2], + y = [radius-size[(axis+1)%3]/2, -radius+size[(axis+1)%3]/2]) { + rotate(rot[axis]) + translate([x,y,0]) + cylinder(h=size[(axis+2)%3]-2*radius, r=radius, center=true); + } + } + for (x = [radius-size[0]/2, -radius+size[0]/2], + y = [radius-size[1]/2, -radius+size[1]/2], + z = [radius-size[2]/2, -radius+size[2]/2]) { + translate([x,y,z]) sphere(radius); + } + } +} diff --git a/shapes.scad b/shapes.scad new file mode 100644 index 0000000..17c9349 --- /dev/null +++ b/shapes.scad @@ -0,0 +1,135 @@ +/* + * OpenSCAD Shapes Library (www.openscad.org) + * Copyright (C) 2009 Catarina Mota + * + * License: LGPL 2.1 or later +*/ + + +//box(width, height, depth); +//roundedBox(width, height, depth, factor); +//cone(height, radius); +//oval(width, height, depth); +//tube(height, radius, wall); +//ovalTube(width, height, depth, wall); +//hexagon(height, depth); +//octagon(height, depth); +//dodecagon(height, depth); +//hexagram(height, depth); +//rightTriangle(adjacent, opposite, depth); +//equiTriangle(side, depth); +//12ptStar(height, depth); + +//---------------------- + +// size is a vector [w, h, d] +module box(size) { + cube(size, true); +} + +// size is a vector [w, h, d] +module roundedBox(size, radius) { + cube(size - [2*radius,0,0], true); + cube(size - [0,2*radius,0], true); + for (x = [radius-size[0]/2, -radius+size[0]/2], + y = [radius-size[1]/2, -radius+size[1]/2]) { + translate([x,y,0]) cylinder(r=radius, h=size[2], center=true); + } +} + +module cone(height, radius, center = false) { + cylinder(height, radius, 0, center); +} + +module oval(w,h, height, center = false) { + scale([1, h/w, 1]) cylinder(h=height, r=w, center=center); +} + +// wall is wall thickness +module tube(height, radius, wall, center = false) { + difference() { + cylinder(h=height, r=radius, center=center); + cylinder(h=height, r=radius-wall, center=center); + } +} + +// wall is wall thickness +module ovalTube(height, rx, ry, wall, center = false) { + difference() { + scale([1, ry/rx, 1]) cylinder(h=height, r=rx, center=center); + scale([(rx-wall)/rx, (ry-wall)/rx, 1]) cylinder(h=height, r=rx, center=center); + } +} + +// size is the XY plane size, height in Z +module hexagon(size, height) { + boxWidth = size/1.75; + for (r = [-60, 0, 60]) rotate([0,0,r]) cube([boxWidth, size, height], true); +} + +// size is the XY plane size, height in Z +module octagon(size, height) { + intersection() { + cube([size, size, height], true); + rotate([0,0,45]) cube([size, size, height], true); + } +} + +// size is the XY plane size, height in Z +module dodecagon(size, height) { + intersection() { + hexagon(size, height); + rotate([0,0,90]) hexagon(size, height); + } +} + +// size is the XY plane size, height in Z +module hexagram(size, height) { + boxWidth=size/1.75; + for (v = [[0,1],[0,-1],[1,-1]]) { + intersection() { + rotate([0,0,60*v[0]]) cube([size, boxWidth, height], true); + rotate([0,0,60*v[1]]) cube([size, boxWidth, height], true); + } + } +} + +module rightTriangle(adjacent, opposite, height) { + difference() { + translate([-adjacent/2,opposite/2,0]) cube([adjacent, opposite, height], true); + translate([-adjacent,0,0]) { + rotate([0,0,atan(opposite/adjacent)]) dislocateBox(adjacent*2, opposite, height); + } + } +} + +module equiTriangle(side, height) { + difference() { + translate([-side/2,side/2,0]) cube([side, side, height], true); + rotate([0,0,30]) dislocateBox(side*2, side, height); + translate([-side,0,0]) { + rotate([0,0,60]) dislocateBox(side*2, side, height); + } + } +} + +module 12ptStar(size, height) { + starNum = 3; + starAngle = 360/starNum; + for (s = [1:starNum]) { + rotate([0, 0, s*starAngle]) cube([size, size, height], true); + } +} + +//----------------------- +//MOVES THE ROTATION AXIS OF A BOX FROM ITS CENTER TO THE BOTTOM LEFT CORNER +//FIXME: Why are the dimensions changed? +// why not just translate([0,0,-d/2]) cube([w,h,d]); +module dislocateBox(w,h,d) { + translate([w/2,h,0]) { + difference() { + cube([w, h*2, d+1]); + translate([-w,0,0]) cube([w, h*2, d+1]); + } + } +} diff --git a/triangles.scad b/triangles.scad index 76a4822..72ff86f 100644 --- a/triangles.scad +++ b/triangles.scad @@ -16,7 +16,7 @@ * @param number depth How wide/deep the triangle is in the 3rd dimension */ /* -module triangle(o_len, a_len, depth) +module triangle_old(o_len, a_len, depth) { difference() { @@ -51,5 +51,5 @@ module triangle(o_len, a_len, depth) } } -triangle(5,10,7); +//triangle(5,10,7);