1
0
mirror of https://github.com/nophead/NopSCADlib.git synced 2025-09-04 12:45:30 +02:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Chris Palmer
7c2df8d36d The pose module can now specify the camera distance supressing viewall and
autocentre.
2021-02-20 19:28:52 +00:00
Chris Palmer
8474718884 Added printed pocket_handle(). 2021-02-17 13:20:39 +00:00
Chris Palmer
fe0f32ddc5 Merge branch 'martinbudden-polyholes' 2021-02-11 09:41:27 +00:00
Chris Palmer
f191b9b0f4 Updated readme 2021-02-11 09:40:18 +00:00
Chris Palmer
2b3908b6fd Merge branch 'polyholes' of https://github.com/martinbudden/NopSCADlib into martinbudden-polyholes 2021-02-11 09:11:42 +00:00
Chris Palmer
0a84bf0927 plateup.py now saves the used files to speed up processing when a part hasn't changed.
Added times to plateup.py.
2021-02-11 09:10:15 +00:00
Chris Palmer
da825b17ab Added colorama.init() to plateup.py to handle new coloured changed messages. 2021-02-10 10:22:38 +00:00
Chris Palmer
ca153c971d Fixed platters and panels not working in the GUI, a regression.
set_config() now puts $cwd in target.scad.
use_stl() and use_dxf() included again instead of used.
2021-02-10 10:17:03 +00:00
Martin Budden
0199587907 Allow poly_shapes to collapse to non-poly form. 2021-02-10 08:29:15 +00:00
Chris Palmer
60350eb228 Updated gallery.py for new page break format. 2021-02-09 23:44:03 +00:00
Chris Palmer
4f9729cf86 Now shows what changed to trigger an openscad invocation in cyan. 2021-02-09 23:32:53 +00:00
Chris Palmer
26f1338ca2 Fixed removal of old deps 2021-02-09 14:34:27 +00:00
Chris Palmer
fc44b43638 Temporary files used during make_all and tests now in tmp dir. 2021-02-09 09:52:26 +00:00
Chris Palmer
182f39876a Moved deps directories to separate stl deps from views deps. 2021-02-09 09:18:30 +00:00
Chris Palmer
055e90cbb3 /stls/ and /dxfs/ excluded from deps to prevent circular dependencies. 2021-02-08 22:44:21 +00:00
Chris Palmer
832380f893 Fixed set_config always writing to target.scad. 2021-02-08 20:31:10 +00:00
Chris Palmer
929d082b25 openscad.py now quits if there are errors or warnings in the log.
This is because the exit status is not always set correctly.
2021-02-08 16:03:59 +00:00
Chris Palmer
57212b5701 set_config.py now defines $target. 2021-02-08 15:00:44 +00:00
Chris Palmer
1c3f136657 Fixed $cwd and $target not defined during silent run. 2021-02-08 15:00:01 +00:00
Chris Palmer
cfd2fd32a1 Now checks openscad.echo for warnings when used instead of openscad.log. 2021-02-08 14:58:50 +00:00
23 changed files with 500 additions and 121 deletions

View File

@@ -1,40 +1,30 @@
# A gallery of projects made with NopSCADlib
<a name="TOP"></a>
## ArduinoThermostat
Arduino thermostat to control a beer fridge to use it as an environmental chamber.
![](ArduinoThermostat.png)
<a name="TOP"></a>
## EnviroPlus
Environmental monitor using Enviro+ sensor board and a Raspberry Pi Zero.
![](EnviroPlus.png)
<a name="TOP"></a>
## FilamentDryBox
A small fan oven with a spool holder to keep the filament warm and dry.
![](FilamentDryBox.png)
<a name="TOP"></a>
## HydraBot
Current state of HydraRaptor after being modified for laser engraving.
![](HydraBot.png)
<a name="TOP"></a>
## IOT 50V PSU
WiFi controllable PSU
![](IOT_50V_PSU.png)
<a name="TOP"></a>
## Lab ATX PSU
Bench power supply built around an ATX PSU.
@@ -47,15 +37,11 @@ Bench power supply built around an ATX PSU.
<a name="TOP"></a>
## Laser Load
15kV dummy load for testing CO2 laser PSUs
![](Laser_load.png)
<a name="TOP"></a>
## MainsBreakOutBox
13A socket break out box with 4mm jacks to measure voltage and / or load current and earth leakage current.
@@ -72,8 +58,6 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
![](MainsBreakOutBox.png)
<a name="TOP"></a>
## Mains Box
Mains isolated and variable supply with metering.
@@ -81,15 +65,11 @@ Mains isolated and variable supply with metering.
<a name="TOP"></a>
## SunBot
A solar tracker to keep a solar panel pointing at the sun.
![](SunBot.png)
<a name="TOP"></a>
## Turntable
WiFi enabled remote control turntable for photography
@@ -97,8 +77,6 @@ WiFi enabled remote control turntable for photography
Was actually made from DiBond but shown made with carbon fibre here.
<a name="TOP"></a>
## Variac
Motorised variac with WiFi control, see [hydraraptor.blogspot.com/2018/04/esp8266-spi-spy](https://hydraraptor.blogspot.com/2018/04/esp8266-spi-spy.html)
@@ -106,4 +84,3 @@ Motorised variac with WiFi control, see [hydraraptor.blogspot.com/2018/04/esp826

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 KiB

After

Width:  |  Height:  |  Size: 886 KiB

View File

@@ -117,6 +117,7 @@ use <tests/flat_hinge.scad>
use <tests/foot.scad>
use <tests/handle.scad>
use <tests/PCB_mount.scad>
use <tests/pocket_handle.scad>
use <tests/printed_box.scad>
use <tests/printed_pulleys.scad>
use <tests/ribbon_clamp.scad>
@@ -180,7 +181,10 @@ translate([x5, cable_grommets_y + 250])
translate([950, 600])
box_test();
translate([890, 750])
translate([830, 770])
pocket_handles();
translate([950, 750])
printed_boxes();
translate([850, 1330])

154
printed/pocket_handle.scad Normal file
View File

@@ -0,0 +1,154 @@
//
// NopSCADlib Copyright Chris Palmer 2021
// nop.head@gmail.com
// hydraraptor.blogspot.com
//
// This file is part of NopSCADlib.
//
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with NopSCADlib.
// If not, see <https://www.gnu.org/licenses/>.
//
//
//! Customisable pocket handle
//
include <../core.scad>
function pocket_handle(hand_size = [90, 40, 40], slant = 35, screw = M3_cs_cap_screw, panel_t = 3, wall = 4, rad = 4) = //! Construct a pocket_handle property list
[hand_size, slant, screw, panel_t, wall, rad];
function pocket_handle_hand_size(type) = type[0]; //! Size of the hole for the fingers
function pocket_handle_slant(type) = type[1]; //! Upward slant of the hand hole
function pocket_handle_screw(type) = type[2]; //! Screw type, can be countersunk or not
function pocket_handle_panel_t(type) = type[3]; //! Thickness of the panel it is mounted in
function pocket_handle_wall(type) = type[4]; //! Wall thickness
function pocket_handle_rad(type) = type[5]; //! Min corner rad
function pocket_handle_flange(type) = //! Size of the flange
let(w = pocket_handle_wall(type),
f = washer_diameter(screw_washer(pocket_handle_screw(type))) + 2 + w,
s = pocket_handle_hand_size(type))
[s.x + 2 * f, s.y + 2 * f, w];
module pocket_handle_hole_positions(type) { //! Place children at screw hole positions
f = pocket_handle_flange(type);
h = pocket_handle_hand_size(type);
x_pitch = (f.x + h.x) / 4;
y_pitch = (f.y + h.y) / 4;
for(x = [-1, 1], y = [-1, 1])
translate([x * x_pitch, y * y_pitch])
children();
}
module pocket_handle_holes(type, h = 0) { //! Panel cutout and screw holes
hand = pocket_handle_hand_size(type);
w = pocket_handle_wall(type);
slot = [hand.x + 2 * w, hand.y + 2 * w];
t = pocket_handle_panel_t(type);
clearance = norm([slot.y, t]) - slot.y + 0.2; // has to be enough clearance for the diagonal to swing it in
extrude_if(h) {
pocket_handle_hole_positions(type)
drill(screw_clearance_radius(pocket_handle_screw(type)), 0);
rounded_square([slot.x + clearance, slot.y + clearance], pocket_handle_rad(type) + w + clearance / 2);
}
}
module pocket_handle(type) { //! Generate STL for pocket_handle
f = pocket_handle_flange(type);
r = pocket_handle_rad(type);
s = pocket_handle_slant(type);
o = f.z * tan(s);
h = pocket_handle_hand_size(type);
t = pocket_handle_panel_t(type);
w = pocket_handle_wall(type);
screw = pocket_handle_screw(type) ;
stl("pocket_handle")
union() {
difference() {
hull() {
rounded_rectangle(f, r);
translate_z(f.z - eps)
rounded_rectangle([f.x + 2 * o, f.y + 2 * o, eps], r + o);
}
hull() {
rounded_rectangle([h.x, h.y, f.z + eps], r);
translate_z(-eps)
rounded_rectangle([h.x + 2 * o, h.y + 2 * o, eps], r + o);
}
pocket_handle_hole_positions(type) {
if(screw_head_height(screw))
translate_z(-eps)
poly_cylinder(r = screw_clearance_radius(screw), h = f.z + 2 * eps, center = false);
else
screw_polysink(screw, h = 2 * f.z + eps, alt = true);
}
}
translate_z(f.z)
linear_extrude(t)
difference() {
rounded_square([h.x + 2 * w, h.y + 2 * w], r + w);
rounded_square([h.x, h.y], r);
}
translate_z(f.z + t)
difference() {
height = h.z - f.z - t;
hull() {
rounded_rectangle([h.x + 2 * w, h.y + 2 * w, eps], r + w);
translate((height + w) * [0, sin(s), cos(s)])
rounded_rectangle([h.x + 2 * w, h.y + 2 * w, eps], r + w);
}
hull() {
translate_z(-eps)
rounded_rectangle([h.x, h.y, eps], r);
translate(height * [0, sin(s), cos(s)])
rounded_rectangle([h.x, h.y, eps], r);
}
}
}
}
module pocket_handle_assembly(type) { //! Assembly with fasteners in place
f = pocket_handle_flange(type);
screw = pocket_handle_screw(type);
nut = screw_nut(screw);
t = pocket_handle_panel_t(type);
washers = screw_head_height(screw) ? 2 : 1;
screw_length = screw_length(screw, f.z + t, washers, nyloc = true);
translate_z(f.z + t / 2) hflip() {
stl_colour(pp1_colour)
pocket_handle(type);
pocket_handle_hole_positions(type) {
translate_z(f.z + t)
explode(15, true)
nut_and_washer(nut, true);
vflip()
if(washers == 2)
screw_and_washer(screw, screw_length);
else
screw(screw, screw_length);
}
}
}

View File

@@ -35,15 +35,15 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
<tr><td> <a href = "#Displays">Displays</a> </td><td> <a href = "#Nuts">Nuts</a> </td><td> <a href = "#Spools">Spools</a> </td><td> <a href = "#Handle">Handle</a> </td><td> <a href = "#Rounded_cylinder">Rounded_cylinder</a> </td><td></td></tr>
<tr><td> <a href = "#Extrusion_brackets">Extrusion_brackets</a> </td><td> <a href = "#O_ring">O_ring</a> </td><td> <a href = "#Springs">Springs</a> </td><td> <a href = "#PCB_mount">PCB_mount</a> </td><td> <a href = "#Rounded_polygon">Rounded_polygon</a> </td><td></td></tr>
<tr><td> <a href = "#Extrusions">Extrusions</a> </td><td> <a href = "#Opengrab">Opengrab</a> </td><td> <a href = "#Stepper_motors">Stepper_motors</a> </td><td> <a href = "#PSU_shroud">PSU_shroud</a> </td><td> <a href = "#Rounded_right_triangle">Rounded_right_triangle</a> </td><td></td></tr>
<tr><td> <a href = "#Fans">Fans</a> </td><td> <a href = "#PCB">PCB</a> </td><td> <a href = "#Swiss_clips">Swiss_clips</a> </td><td> <a href = "#Press_fit">Press_fit</a> </td><td> <a href = "#Sector">Sector</a> </td><td></td></tr>
<tr><td> <a href = "#Fuseholder">Fuseholder</a> </td><td> <a href = "#PCBs">PCBs</a> </td><td> <a href = "#Toggles">Toggles</a> </td><td> <a href = "#Printed_box">Printed_box</a> </td><td> <a href = "#Sweep">Sweep</a> </td><td></td></tr>
<tr><td> <a href = "#Geared_steppers">Geared_steppers</a> </td><td> <a href = "#PSUs">PSUs</a> </td><td> <a href = "#Transformers">Transformers</a> </td><td> <a href = "#Printed_pulleys">Printed_pulleys</a> </td><td> <a href = "#Thread">Thread</a> </td><td></td></tr>
<tr><td> <a href = "#Green_terminals">Green_terminals</a> </td><td> <a href = "#Panel_meters">Panel_meters</a> </td><td> <a href = "#Tubings">Tubings</a> </td><td> <a href = "#Ribbon_clamp">Ribbon_clamp</a> </td><td> <a href = "#Tube">Tube</a> </td><td></td></tr>
<tr><td> <a href = "#Hot_ends">Hot_ends</a> </td><td> <a href = "#Pillars">Pillars</a> </td><td> <a href = "#Variacs">Variacs</a> </td><td> <a href = "#SSR_shroud">SSR_shroud</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Hygrometer">Hygrometer</a> </td><td> <a href = "#Pin_headers">Pin_headers</a> </td><td> <a href = "#Veroboard">Veroboard</a> </td><td> <a href = "#Screw_knob">Screw_knob</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#IECs">IECs</a> </td><td> <a href = "#Pulleys">Pulleys</a> </td><td> <a href = "#Washers">Washers</a> </td><td> <a href = "#Socket_box">Socket_box</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Inserts">Inserts</a> </td><td></td><td> <a href = "#Wire">Wire</a> </td><td> <a href = "#Strap_handle">Strap_handle</a> </td><td></td><td></td></tr>
<tr><td></td><td></td><td> <a href = "#Zipties">Zipties</a> </td><td></td><td></td><td></td></tr>
<tr><td> <a href = "#Fans">Fans</a> </td><td> <a href = "#PCB">PCB</a> </td><td> <a href = "#Swiss_clips">Swiss_clips</a> </td><td> <a href = "#Pocket_handle">Pocket_handle</a> </td><td> <a href = "#Sector">Sector</a> </td><td></td></tr>
<tr><td> <a href = "#Fuseholder">Fuseholder</a> </td><td> <a href = "#PCBs">PCBs</a> </td><td> <a href = "#Toggles">Toggles</a> </td><td> <a href = "#Press_fit">Press_fit</a> </td><td> <a href = "#Sweep">Sweep</a> </td><td></td></tr>
<tr><td> <a href = "#Geared_steppers">Geared_steppers</a> </td><td> <a href = "#PSUs">PSUs</a> </td><td> <a href = "#Transformers">Transformers</a> </td><td> <a href = "#Printed_box">Printed_box</a> </td><td> <a href = "#Thread">Thread</a> </td><td></td></tr>
<tr><td> <a href = "#Green_terminals">Green_terminals</a> </td><td> <a href = "#Panel_meters">Panel_meters</a> </td><td> <a href = "#Tubings">Tubings</a> </td><td> <a href = "#Printed_pulleys">Printed_pulleys</a> </td><td> <a href = "#Tube">Tube</a> </td><td></td></tr>
<tr><td> <a href = "#Hot_ends">Hot_ends</a> </td><td> <a href = "#Pillars">Pillars</a> </td><td> <a href = "#Variacs">Variacs</a> </td><td> <a href = "#Ribbon_clamp">Ribbon_clamp</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Hygrometer">Hygrometer</a> </td><td> <a href = "#Pin_headers">Pin_headers</a> </td><td> <a href = "#Veroboard">Veroboard</a> </td><td> <a href = "#SSR_shroud">SSR_shroud</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#IECs">IECs</a> </td><td> <a href = "#Pulleys">Pulleys</a> </td><td> <a href = "#Washers">Washers</a> </td><td> <a href = "#Screw_knob">Screw_knob</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Inserts">Inserts</a> </td><td></td><td> <a href = "#Wire">Wire</a> </td><td> <a href = "#Socket_box">Socket_box</a> </td><td></td><td></td></tr>
<tr><td></td><td></td><td> <a href = "#Zipties">Zipties</a> </td><td> <a href = "#Strap_handle">Strap_handle</a> </td><td></td><td></td></tr>
</table>
---
@@ -4956,6 +4956,56 @@ The stl must be given a parameterless wrapper in the project that uses it.
| 1 | pcb_mount_PI_IO_5.stl |
<a href="#top">Top</a>
---
<a name="Pocket_handle"></a>
## Pocket_handle
Customisable pocket handle
[printed/pocket_handle.scad](printed/pocket_handle.scad) Implementation.
[tests/pocket_handle.scad](tests/pocket_handle.scad) Code for this example.
### Properties
| Function | Description |
|:--- |:--- |
| `pocket_handle_hand_size(type)` | Size of the hole for the fingers |
| `pocket_handle_panel_t(type)` | Thickness of the panel it is mounted in |
| `pocket_handle_rad(type)` | Min corner rad |
| `pocket_handle_screw(type)` | Screw type, can be countersunk or not |
| `pocket_handle_slant(type)` | Upward slant of the hand hole |
| `pocket_handle_wall(type)` | Wall thickness |
### Functions
| Function | Description |
|:--- |:--- |
| `pocket_handle(hand_size = [90, 40, 40], slant = 35, screw = M3_cs_cap_screw, panel_t = 3, wall = 4, rad = 4)` | Construct a pocket_handle property list |
| `pocket_handle_flange(type)` | Size of the flange |
### Modules
| Module | Description |
|:--- |:--- |
| `pocket_handle(type)` | Generate STL for pocket_handle |
| `pocket_handle_assembly(type)` | Assembly with fasteners in place |
| `pocket_handle_hole_positions(type)` | Place children at screw hole positions |
| `pocket_handle_holes(type, h = 0)` | Panel cutout and screw holes |
![pocket_handle](tests/png/pocket_handle.png)
### Vitamins
| Qty | Module call | BOM entry |
| ---:|:--- |:---|
| 4 | `nut(M3_nut, nyloc = true)` | Nut M3 x 2.4mm nyloc |
| 4 | `screw(M3_cs_cap_screw, 12)` | Screw M3 cs cap x 12mm |
| 4 | `washer(M3_washer)` | Washer M3 x 7mm x 0.5mm |
### Printed
| Qty | Filename |
| ---:|:--- |
| 1 | pocket_handle.stl |
<a href="#top">Top</a>
---
@@ -6016,6 +6066,13 @@ If the code to make an STL or DXF is made a child of the `stl()` or `dxf()` modu
it with code.
This can speed up the generation of the build instructions greatly but isn't compatible with STLs that include support structures.
The `pose()` module allows assembly views in the readme to be posed differently to the default view in the GUI:
* Setting the `exploded` parameter to `true` allows just the exploded version to be posed and setting to `false` allows just the assembled view to be posed, the default is both.
* If the `d` parameter is set to specify the camera distance then the normal `viewall` and `autocenter` options are supressed allowing a small section to be zoomed in to fill the view.
* To get the parameter values make the GUI window square, pose the view with the mouse and then copy the viewport parameters from the Edit menu and paste them into the pose invocation.
* Two `pose()` modules can be chained to allow different poses for exploded and assembled views.
[utils/core/bom.scad](utils/core/bom.scad) Implementation.
[tests/BOM.scad](tests/BOM.scad) Code for this example.
@@ -6039,7 +6096,7 @@ This can speed up the generation of the build instructions greatly but isn't com
| `no_explode()` | Prevent children being exploded |
| `no_pose()` | Force children not to be posed even if parent is |
| `not_on_bom(on = false)` | Specify the following child parts are not on the BOM, for example when they are on a PCB that comes assembled |
| `pose(a = [55, 0, 25], t = [0, 0, 0], exploded = undef)` | Pose an STL or assembly for rendering to png by specifying rotation `a` and translation `t`, `exploded = true for` just the exploded view or `false` for unexploded only. |
| `pose(a = [55, 0, 25], t = [0, 0, 0], exploded = undef, d = undef)` | Pose an STL or assembly for rendering to png by specifying rotation `a`, translation `t` and optionally `d`, `exploded = true for` just the exploded view or `false` for unexploded only. |
| `pose_hflip(exploded = undef)` | Pose an STL or assembly for rendering to png by flipping around the Y axis, `exploded = true for` just the exploded view or `false` for unexploded only. |
| `pose_vflip(exploded = undef)` | Pose an STL or assembly for rendering to png by flipping around the X axis, `exploded = true for` just the exploded view or `false` for unexploded only. |
| `stl(name)` | Name an stl that will appear on the BOM, there needs to a module named `<name>_stl` to make it |
@@ -6173,18 +6230,18 @@ leaving a scar on either surface.
### Functions
| Function | Description |
|:--- |:--- |
| `corrected_diameter(d, n = 0)` | Adjusted diameter to make flats lie on the circle |
| `corrected_radius(r, n = 0)` | Adjusted radius to make flats lie on the circle |
| `sides(r)` | Optimium number of sides for specified radius |
| `corrected_diameter(d, n = undef)` | Adjusted diameter to make flats lie on the circle |
| `corrected_radius(r, n = undef)` | Adjusted radius to make flats lie on the circle |
| `sides(r, n = undef)` | Optimium number of sides for specified radius |
### Modules
| Module | Description |
|:--- |:--- |
| `drill(r, h = 100, center = true)` | Make a cylinder for drilling holes suitable for CNC routing, set h = 0 for circle |
| `poly_circle(r, sides = 0)` | Make a circle adjusted to print the correct size |
| `poly_cylinder(r, h, center = false, sides = 0, chamfer = false, twist = 0)` | Make a cylinder adjusted to print the correct size |
| `poly_circle(r, sides = undef)` | Make a circle adjusted to print the correct size |
| `poly_cylinder(r, h, center = false, sides = undef, chamfer = false, twist = 0)` | Make a cylinder adjusted to print the correct size |
| `poly_drill(r, h = 100, center = true)` | Make a cylinder for drilling holes suitable for CNC routing if cnc_bit_r is non zero, otherwise a poly_cylinder. |
| `poly_ring(or, ir, sides = 0)` | Make a 2D ring adjusted to have the correct internal radius |
| `poly_ring(or, ir, sides = undef)` | Make a 2D ring adjusted to have the correct internal radius |
| `poly_tube(or, ir, h, center = false)` | Make a tube adjusted to have the correct internal radius |
| `slot(r, l, h = 100)` | Make a horizontal slot suitable for CNC routing, set h = 0 for 2D version |

View File

@@ -61,6 +61,7 @@ class BOM:
self.name = name
self.big = None
self.ngb = False
self.zoomed = 0
self.count = 1
self.vitamins = {}
self.printed = {}
@@ -75,6 +76,7 @@ class BOM:
"name" : self.name,
"big" : self.big,
"ngb" : self.ngb,
"zoomed" : self.zoomed,
"count" : self.count,
"assemblies" : assemblies,
"vitamins" : {v : self.vitamins[v].data() for v in self.vitamins},

View File

@@ -18,6 +18,7 @@
#
import os
from set_config import source_dir
from colorama import Fore
def mtime(file):
if os.path.isfile(file):
@@ -41,13 +42,13 @@ def read_deps(dname):
def check_deps(target, dname):
target_mtime = mtime(target)
if not target_mtime:
return target + " missing"
return Fore.CYAN + target + " missing" + Fore.WHITE
if not os.path.isfile(dname):
return "no deps"
return Fore.CYAN + "no deps" + Fore.WHITE
deps = read_deps(dname)
for dep in deps:
if mtime(dep) > target_mtime:
return dep + ' changed'
return Fore.CYAN + dep + ' changed' + Fore.WHITE
return None
def source_dirs(bom_dir):

View File

@@ -28,7 +28,10 @@ from set_config import *
import time
import times
from deps import *
from tmpdir import *
import json
import shutil
from colorama import Fore, init
def bom_to_parts(bom_dir, part_type, assembly = None):
#
@@ -43,7 +46,7 @@ def bom_to_parts(bom_dir, part_type, assembly = None):
if words:
last_word = words[-1]
if last_word.endswith(suffix):
part_files.append(last_word[:-4] + '.' + part_type)
part_files.append(last_word[:-4] + '.' + part_type)
return part_files
def usage(t):
@@ -62,12 +65,20 @@ def make_parts(target, part_type, parts = None):
#
top_dir = set_config(target, lambda: usage(part_type))
target_dir = top_dir + part_type + 's'
deps_dir = top_dir + "deps"
deps_dir = target_dir + "/deps"
bom_dir = top_dir + "bom"
tmp_dir = mktmpdir(top_dir)
if not os.path.isdir(target_dir):
os.makedirs(target_dir)
if not os.path.isdir(deps_dir):
os.makedirs(deps_dir)
old_deps = top_dir + 'deps' #old location
if os.path.isdir(old_deps):
shutil.rmtree(old_deps)
times.read_times(target_dir)
#
# Decide which files to make
@@ -120,15 +131,15 @@ def make_parts(target, part_type, parts = None):
changed = check_deps(part_file, dname)
changed = times.check_have_time(changed, part)
if part_type == 'stl' and not changed and not part in bounds_map:
changed = "No bounds"
changed = Fore.CYAN + "No bounds" + Fore.WHITE
if changed:
print(changed)
#
# make a file to use the module
#
part_maker_name = part_type + ".scad"
part_maker_name = tmp_dir + '/' + part_type + ".scad"
with open(part_maker_name, "w") as f:
f.write("use <%s/%s>\n" % (dir, filename))
f.write("use <%s/%s>\n" % (reltmp(dir, target), filename))
f.write("%s();\n" % module);
t = time.time()
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name)
@@ -145,6 +156,10 @@ def make_parts(target, part_type, parts = None):
with open(bounds_fname, 'w') as outfile:
json.dump(bounds_map, outfile, indent = 4)
#
# Remove tmp dir
#
rmtmpdir(tmp_dir)
#
# List the ones we didn't find
#
if targets:

View File

@@ -68,9 +68,10 @@ def gallery(force):
match = re.match(r"^(#+).*$", line)
if match:
line = '#' + line
if line == '---\n':
break;
print(line[:-1], file = output_file)
if line == '---\n' or line == '<span></span>\n':
break
if line != '<a name="TOP"></a>\n':
print(line[:-1], file = output_file)
else:
print(Fore.MAGENTA + "Can't find", document, Fore.WHITE);
with open(target_dir + "/readme.html", "wt") as html_file:

View File

@@ -32,12 +32,18 @@ def run_list(args, silent = False, verbose = False):
print()
with open("openscad.log", "w") as log:
rc = subprocess.call(cmd, stdout = log, stderr = log)
for line in open("openscad.log", "rt"):
log_file = "openscad.echo" if "openscad.echo" in cmd else "openscad.log"
bad = False
for line in open(log_file, "rt"):
if verbose or 'ERROR:' in line or 'WARNING:' in line:
bad = True
print(line[:-1])
if rc:
sys.exit(rc)
if bad:
sys.exit(1)
def run(*args):
run_list(list(args), False)

View File

@@ -20,6 +20,7 @@
# Set command line options from enviroment variables and check if they have changed
import json, os, deps
from colorama import Fore, init
def check_options(dir = '.'):
global options, options_mtime
@@ -37,7 +38,7 @@ def check_options(dir = '.'):
def have_changed(changed, target):
if not changed and deps.mtime(target) < options_mtime:
return "command line options changed"
return Fore.CYAN + "command line options changed" + Fore.WHITE
return changed
def list():

View File

@@ -26,8 +26,10 @@ import sys
import c14n_stl
from set_config import *
from deps import *
from shutil import copyfile
import shutil
import re
import time
import times
source_dirs = { "stl" : "platters", "dxf" : "panels" }
target_dirs = { "stl" : "printed", "dxf" : "routed" }
@@ -41,11 +43,14 @@ def plateup(target, part_type, usage = None):
target_dir = parts_dir + '/' + target_dirs[part_type]
source_dir1 = source_dirs[part_type]
source_dir2 = top_dir + source_dirs[part_type]
times.read_times(target_dir)
#
# Loop through source directories
#
used = []
all_used = []
all_sources = []
all_parts = []
for dir in [source_dir1, source_dir2]:
if not os.path.isdir(dir):
continue
@@ -54,9 +59,12 @@ def plateup(target, part_type, usage = None):
#
# Make the deps dir
#
deps_dir = dir + "/deps"
deps_dir = parts_dir + "/deps"
if not os.path.isdir(deps_dir):
os.makedirs(deps_dir)
if os.path.isdir(dir + '/deps'): #old deps
shutil.rmtree(dir + '/deps')
#
# Decide which files to make
#
@@ -65,42 +73,55 @@ def plateup(target, part_type, usage = None):
#
# Run OpenSCAD on the source files to make the targets
#
target_def = ['-D$target="%s"' % target] if target else []
cwd_def = ['-D$cwd="%s"' % os.getcwd().replace('\\', '/')]
for src in sources:
src_file = dir + '/' + src
part_file = target_dir + '/' + src[:-4] + part_type
part = src[:-4] + part_type
all_parts.append(part)
part_file = target_dir + '/' + part
uses_file = deps_dir + '/' + src[:-4] + 'txt'
dname = deps_name(deps_dir, src)
changed = check_deps(part_file, dname)
oldest = part_file if mtime(part_file) < mtime(uses_file) else uses_file
changed = check_deps(oldest, dname)
used = []
if changed:
print(changed)
target_def = ['-D$target="%s"' % target] if target else []
cwd_def = ['-D$cwd="%s"' % os.getcwd().replace('\\', '/')]
t = time.time()
openscad.run_list(["-D$bom=1"] + target_def + cwd_def + ["-d", dname, "-o", part_file, src_file])
if part_type == 'stl':
c14n_stl.canonicalise(part_file)
times.add_time(part, t)
log_name = 'openscad.log'
#
# Add the files on the BOM to the used list
#
with open(log_name) as file:
for line in file.readlines():
match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line)
if match:
used.append(match.group(1))
with open(uses_file, "wt") as file:
for part in used:
print(part, file = file)
else:
log_name = 'openscad.echo'
openscad.run_silent("-D$bom=1", "-o", log_name, src_file)
#
# Add the files on the BOM to the used list
#
with open(log_name) as file:
for line in file.readlines():
match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line)
if match:
used.append(match.group(1))
with open(uses_file, "rt") as file:
for line in file:
used.append(line[:-1])
all_used += used
copied = []
if all_sources:
#
# Copy files that are not included
#
for file in os.listdir(parts_dir):
if file.endswith('.' + part_type) and not file in used:
if file.endswith('.' + part_type) and not file in all_used:
src = parts_dir + '/' + file
dst = target_dir + '/' + file
if mtime(src) > mtime(dst):
print("Copying %s to %s" % (src, dst))
copyfile(src, dst)
shutil.copyfile(src, dst)
copied.append(file)
#
# Remove any cruft
@@ -111,3 +132,12 @@ def plateup(target, part_type, usage = None):
if not file in targets and not file in copied:
print("Removing %s" % file)
os.remove(target_dir + '/' + file)
targets = [file[:-4] + 'txt' for file in all_sources]
for file in os.listdir(deps_dir):
if file.endswith('.' + 'txt'):
if not file in targets:
print("Removing %s" % file)
os.remove(deps_dir + '/' + file)
times.print_times(all_parts)

View File

@@ -30,6 +30,7 @@ from tests import do_cmd, update_image, colour_scheme, background
from deps import mtime
from colorama import init
import json
from tmpdir import *
def usage():
print("\nusage:\n\trender [target_config] - Render images of the stl and dxf files.");
@@ -40,6 +41,7 @@ def render(target, type):
# Make the target directory
#
top_dir = set_config(target, usage)
tmp_dir = mktmpdir(top_dir)
target_dir = top_dir + type + 's'
bom_dir = top_dir + 'bom'
if not os.path.isdir(target_dir):
@@ -80,7 +82,7 @@ def render(target, type):
# make a file to import the stl
#
if mtime(part_file) > mtime(png_name):
png_maker_name = "png.scad"
png_maker_name = tmp_dir + "/png.scad"
pp1 = [0, 146/255, 0]
colour = pp1
if part in colours:
@@ -88,15 +90,19 @@ def render(target, type):
if not '[' in colour:
colour = '"' + colour + '"'
with open(png_maker_name, "w") as f:
f.write('color(%s) import("%s");\n' % (colour, part_file))
f.write('color(%s) import("%s");\n' % (colour, reltmp(part_file, target)))
cam = "--camera=0,0,0,70,0,315,500" if type == 'stl' else "--camera=0,0,0,0,0,0,500"
render = "--preview" if type == 'stl' or colour != pp1 else "--render"
tmp_name = 'tmp.png'
tmp_name = tmp_dir + '/' + part[:-4] + '.png'
openscad.run(colour_scheme, "--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall", "-o", tmp_name, png_maker_name);
do_cmd(("magick "+ tmp_name + " -trim -resize 280x280 -background %s -gravity Center -extent 280x280 -bordercolor %s -border 10 %s"
% (background, background, tmp_name)).split())
update_image(tmp_name, png_name)
os.remove(png_maker_name)
#
# Remove tmp dir
#
rmtmpdir(tmp_dir)
if __name__ == '__main__':
init()

View File

@@ -69,17 +69,22 @@ def set_config(target, usage = None):
sys.exit(1)
fname = source_dir + "/target.scad"
text = "include <config_%s.scad>\n" % target;
line = ""
text = ['include <config_%s.scad>\n' % target,
'$target = "%s";\n' % target,
'$cwd="%s";\n' % os.getcwd().replace('\\', '/')
]
lines = [""]
try:
with open(fname,"rt") as f:
line = f.read()
lines = f.readlines()
except:
pass
if line != text:
if lines != text:
with open(fname,"wt") as f:
f. write(text);
for t in text:
f. write(t);
return target + "/"
def usage():

View File

@@ -34,6 +34,7 @@ import shutil
from deps import *
from blurb import *
from colorama import Fore
from tmpdir import *
w = 4096
h = w
@@ -94,6 +95,7 @@ def usage():
def tests(tests):
scad_dir = "tests"
tmp_dir = mktmpdir(scad_dir + '/')
deps_dir = scad_dir + "/deps"
png_dir = scad_dir + "/png"
bom_dir = scad_dir + "/bom"
@@ -234,7 +236,7 @@ def tests(tests):
if changed:
print(changed)
t = time.time()
tmp_name = 'tmp.png'
tmp_name = tmp_dir + '/tmp.png'
openscad.run_list(options.list() + ["-D$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name]);
times.add_time(scad_name, t)
do_cmd(["magick", tmp_name, "-trim", "-resize", "1000x600", "-bordercolor", background, "-border", "10", tmp_name])
@@ -303,6 +305,11 @@ def tests(tests):
with open(doc_base_name + ".html", "wt") as html_file:
do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file)
times.print_times()
#
# Remove tmp dir
#
rmtmpdir(tmp_dir)
do_cmd(('codespell -L od ' + doc_name).split())
if __name__ == '__main__':

View File

@@ -44,7 +44,7 @@ def got_time(name):
def check_have_time(changed, name):
if not changed and not got_time(name):
changed = "no previous time"
changed = Fore.CYAN + "no previous time" + Fore.WHITE
return changed
def add_time(name, start):

40
scripts/tmpdir.py Normal file
View File

@@ -0,0 +1,40 @@
#
# NopSCADlib Copyright Chris Palmer 2021
# nop.head@gmail.com
# hydraraptor.blogspot.com
#
# This file is part of NopSCADlib.
#
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with NopSCADlib.
# If not, see <https://www.gnu.org/licenses/>.
#
"""
Make a directory for tmp files.
"""
import os
import time
def mktmpdir(top_dir):
tmp_dir = top_dir + 'tmp'
if not os.path.isdir(tmp_dir):
os.makedirs(tmp_dir)
else:
for file in os.listdir(tmp_dir):
os.remove(tmp_dir + '/' + file)
return tmp_dir
def reltmp(dir, target):
return dir if os.path.isabs(dir) else '../../' + dir if target else '../' + dir
def rmtmpdir(tmp_dir):
os.rmdir(tmp_dir)
while os.path.isdir(tmp_dir):
time.sleep(0.1)

View File

@@ -38,6 +38,7 @@ import shutil
import re
import copy
from colorama import Fore
from tmpdir import *
def is_assembly(s):
return s[-9:] == '_assembly' or s[-11:] == '_assemblies'
@@ -129,8 +130,9 @@ def views(target, do_assemblies = None):
# Make the target directory
#
top_dir = set_config(target, usage)
tmp_dir = mktmpdir(top_dir)
target_dir = top_dir + 'assemblies'
deps_dir = top_dir + "deps"
deps_dir = target_dir + "/deps"
bom_dir = top_dir + "bom"
if not os.path.isdir(target_dir):
os.makedirs(target_dir)
@@ -183,6 +185,7 @@ def views(target, do_assemblies = None):
#
for ass in flat_bom:
if ass["name"] == real_name:
zoomed = ass['zoomed']
if not "blurb" in ass:
ass["blurb"] = blurb.scrape_module_blurb(lines[:line_no])
break
@@ -204,20 +207,21 @@ def views(target, do_assemblies = None):
changed = check_deps(png_name, dname)
changed = times.check_have_time(changed, png_name)
changed = options.have_changed(changed, png_name)
tmp_name = 'tmp.png'
tmp_name = tmp_dir + '/' + real_name + '.png'
if changed:
print(changed)
#
# make a file to use the module
#
png_maker_name = 'png.scad'
png_maker_name = tmp_dir + '/png.scad'
with open(png_maker_name, "w") as f:
f.write("use <%s/%s>\n" % (dir, filename))
f.write("use <%s/%s>\n" % (reltmp(dir, target), filename))
f.write("%s();\n" % module);
t = time.time()
target_def = ['-D$target="%s"' % target] if target else []
cwd_def = ['-D$cwd="%s"' % os.getcwd().replace('\\', '/')]
openscad.run_list(options.list() + target_def + cwd_def + ["-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name]);
view_def = ['--viewall', '--autocenter'] if not (zoomed & (1 << explode)) else ['--camera=0,0,0,55,0,25,140']
openscad.run_list(options.list() + target_def + cwd_def + view_def +["-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "-d", dname, "-o", tmp_name, png_maker_name]);
times.add_time(png_name, t)
do_cmd(["magick", tmp_name, "-trim", "-resize", "1004x1004", "-bordercolor", background, "-border", "10", tmp_name])
update_image(tmp_name, png_name)
@@ -439,6 +443,10 @@ def views(target, do_assemblies = None):
dst.write(line)
i += 1
#
# Remove tmp dir
#
rmtmpdir(tmp_dir)
#
# Spell check
#
do_cmd(('codespell -L od ' + top_dir + 'readme.md').split())

BIN
tests/png/pocket_handle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

40
tests/pocket_handle.scad Normal file
View File

@@ -0,0 +1,40 @@
//
// NopSCADlib Copyright Chris Palmer 2021
// nop.head@gmail.com
// hydraraptor.blogspot.com
//
// This file is part of NopSCADlib.
//
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with NopSCADlib.
// If not, see <https://www.gnu.org/licenses/>.
//
include <../core.scad>
include <../vitamins/sheets.scad>
use <../printed/pocket_handle.scad>
show_holes = false;
handle = pocket_handle();
module pocket_handles() {
if($preview) {
pocket_handle_assembly(handle);
if(show_holes)
#pocket_handle_holes(handle);
}
else
pocket_handle(handle);
}
pocket_handles();

View File

@@ -34,14 +34,19 @@
//! If the code to make an STL or DXF is made a child of the `stl()` or `dxf()` module then the STL or DXF will be used in the assembly views generated by `views.py` instead of generating
//! it with code.
//! This can speed up the generation of the build instructions greatly but isn't compatible with STLs that include support structures.
//!
//! The `pose()` module allows assembly views in the readme to be posed differently to the default view in the GUI:
//!
//! * Setting the `exploded` parameter to `true` allows just the exploded version to be posed and setting to `false` allows just the assembled view to be posed, the default is both.
//! * If the `d` parameter is set to specify the camera distance then the normal `viewall` and `autocenter` options are supressed allowing a small section to be zoomed in to fill the view.
//! * To get the parameter values make the GUI window square, pose the view with the mouse and then copy the viewport parameters from the Edit menu and paste them into the pose invocation.
//! * Two `pose()` modules can be chained to allow different poses for exploded and assembled views.
//
function bom_mode(n = 1) = $_bom >= n && (is_undef($on_bom) || $on_bom); //! Current BOM mode, 0 = none, 1 = printed and routed parts and assemblies, 2 includes vitamins as well
function exploded() = is_undef($exploded_parent) ? $exploded : 0; //! Returns the value of `$exploded` if it is defined, else `0`
function show_supports() = !$preview || exploded(); //! True if printed support material should be shown
module no_explode() let($exploded_parent = true) children(); //! Prevent children being exploded
module no_pose() let($posed = true) children(); //! Force children not to be posed even if parent is
module explode(d, explode_children = false, offset = [0,0,0]) { //! Explode children by specified Z distance or vector `d`, option to explode grand children
v = is_list(d) ? d : [0, 0, d];
o = is_list(offset) ? offset : [0, 0, offset];
@@ -62,17 +67,29 @@ module explode(d, explode_children = false, offset = [0,0,0]) { //! Explode
children();
}
module pose(a = [55, 0, 25], t = [0, 0, 0], exploded = undef) //! Pose an STL or assembly for rendering to png by specifying rotation `a` and translation `t`, `exploded = true for` just the exploded view or `false` for unexploded only.
if(is_undef($pose) || !is_undef($posed) || (!is_undef(exploded) && exploded != !!exploded()))
children();
else
let($posed = true) // only pose the top level
rotate([55, 0, 25])
rotate([-a.x, 0, 0])
rotate([0, -a.y, 0])
rotate([0, 0, -a.z])
translate(-t)
children();
module no_pose() let($posed = true, $zoomed = undef) children(); //! Force children not to be posed even if parent is
module pose(a = [55, 0, 25], t = [0, 0, 0], exploded = undef, d = undef) //! Pose an STL or assembly for rendering to png by specifying rotation `a`, translation `t` and optionally `d`, `exploded = true for` just the exploded view or `false` for unexploded only.
let($zoomed = is_undef(d)
? is_undef($zoomed)
? undef
: $zoomed
: is_undef(exploded)
? 3
: exploded
? 2
: 1)
if(is_undef($pose) || !is_undef($posed) || (!is_undef(exploded) && exploded != !!exploded()))
children();
else
let($posed = true) // only pose the top level
rotate([55, 0, 25])
translate_z(is_undef(d) ? 0 : 140 - d)
rotate([-a.x, 0, 0])
rotate([0, -a.y, 0])
rotate([0, 0, -a.z])
translate(-t)
children();
module pose_hflip(exploded = undef) //! Pose an STL or assembly for rendering to png by flipping around the Y axis, `exploded = true for` just the exploded view or `false` for unexploded only.
if(is_undef($pose) || !is_undef($posed) || (!is_undef(exploded) && exploded != !!exploded()))
@@ -93,7 +110,9 @@ module pose_vflip(exploded = undef) //! Pose an STL or assembly for render
module assembly(name, big = undef, ngb = false) { //! Name an assembly that will appear on the BOM, there needs to a module named `<name>_assembly` to make it. `big` can force big or small assembly diagrams.
if(bom_mode()) {
args = is_undef(big) && !ngb ? "" : str("(big=", big, ", ngb=", ngb, ")");
zoom = is_undef($zoomed) ? 0 : $zoomed;
arglist = str(arg(big, undef, "big"), arg(ngb, false, "ngb"), arg(zoom, 0, "zoomed"));
args = len(arglist) ? str("(", slice(arglist, 2), ")") : "";
echo(str("~", name, "_assembly", args, "{"));
}
no_pose()
@@ -146,17 +165,11 @@ module dxf(name) { //! Name a dxf that will appear on the B
}
}
module use_stl(name) { //! Import an STL to make a build platter
stl(name);
path = is_undef($target) ? "/stls/" : str("/", $target, "/stls/");
import(str($cwd, path, name, ".stl"));
}
module use_stl(name) //! Import an STL to make a build platter
assert(false); // Here for documentation only, real version in core.scad
module use_dxf(name) { //! Import a DXF to make a build panel
dxf(name);
path = is_undef($target) ? "/dxfs/" : str("/", $target, "/dxfs/");
import(str($cwd, path, name, ".dxf"));
}
module use_dxf(name) //! Import a DXF to make a build panel
assert(false); // Here for documentation only, real version in core.scad
function value_string(value) = is_string(value) ? str("\"", value, "\"") : str(value); //! Convert `value` to a string or quote it if it is already a string

View File

@@ -25,3 +25,15 @@ include <../../global_defs.scad>
// Global functions and modules
//
use <global.scad>
module use_stl(name) { //! Import an STL to make a build platter
stl(name);
path = is_undef($target) ? "../stls/" : str($cwd, "/", $target, "/stls/");
import(str(path, name, ".stl"));
}
module use_dxf(name) { //! Import a DXF to make a build panel
dxf(name);
path = is_undef($target) ? "../dxfs/" : str($cwd, "/", $target, "/dxfs/");
import(str(path, name, ".dxf"));
}

View File

@@ -33,20 +33,20 @@
//! When `twist` is set the resulting cylinder is extended by `eps` at each end so that the exact length of the hole can be used without
//! leaving a scar on either surface.
//
function sides(r) = max(round(4 * r), 3); //! Optimium number of sides for specified radius
function corrected_radius(r, n = 0) = r / cos(180 / (n ? n : sides(r))); //! Adjusted radius to make flats lie on the circle
function corrected_diameter(d, n = 0) = d / cos(180 / (n ? n : sides(d / 2))); //! Adjusted diameter to make flats lie on the circle
function sides(r, n = undef) = is_undef(n) ? max(round(4 * r), 3) : n ? max(n, 3) : r2sides(r); //! Optimium number of sides for specified radius
function corrected_radius(r, n = undef) = r / cos(180 / sides(r, n)); //! Adjusted radius to make flats lie on the circle
function corrected_diameter(d, n = undef) = 2 * corrected_radius(d / 2 , n); //! Adjusted diameter to make flats lie on the circle
module poly_circle(r, sides = 0) { //! Make a circle adjusted to print the correct size
n = sides ? sides : sides(r);
circle(r = corrected_radius(r,n), $fn = n);
module poly_circle(r, sides = undef) { //! Make a circle adjusted to print the correct size
n = sides(r, sides);
circle(r = corrected_radius(r, n), $fn = n);
}
module poly_cylinder(r, h, center = false, sides = 0, chamfer = false, twist = 0) {//! Make a cylinder adjusted to print the correct size
module poly_cylinder(r, h, center = false, sides = undef, chamfer = false, twist = 0) {//! Make a cylinder adjusted to print the correct size
if(twist) {
slices = ceil(h / layer_height);
twists = min(twist + 1, slices);
sides = sides ? sides : sides(r);
sides = sides(r, sides);
rot = 360 / sides / twists * (twists < slices ? (1 + 1 / slices) : 1);
if(center)
for(side = [0, 1])
@@ -64,10 +64,10 @@ module poly_cylinder(r, h, center = false, sides = 0, chamfer = false, twist = 0
poly_circle(r, sides);
if(h && chamfer)
poly_cylinder(r + layer_height, center ? layer_height * 2 : layer_height, center, sides = sides ? sides : sides(r));
poly_cylinder(r + layer_height, center ? layer_height * 2 : layer_height, center, sides = sides(r, sides));
}
module poly_ring(or, ir, sides = 0) { //! Make a 2D ring adjusted to have the correct internal radius
module poly_ring(or, ir, sides = undef) { //! Make a 2D ring adjusted to have the correct internal radius
cir = corrected_radius(ir, sides);
filaments = (or - cir) / extrusion_width;
if(filaments > 3 + eps)