From 34a9b0e87bd5bc7fb301c2f409381d120799a77e Mon Sep 17 00:00:00 2001 From: Chris Palmer Date: Sat, 8 Jun 2019 22:10:47 +0100 Subject: [PATCH] Added source code --- .gitignore | 8 + box.scad | 471 +++++++++ box_assembly.scad | 94 ++ butt_box.scad | 261 +++++ cable_grommets.scad | 187 ++++ carriers.scad | 63 ++ core.scad | 27 + corner_block.scad | 181 ++++ door_hinge.scad | 187 ++++ door_latch.scad | 81 ++ fan_guard.scad | 92 ++ fixing_block.scad | 170 +++ foot.scad | 151 +++ global_defs.scad | 90 ++ handle.scad | 103 ++ lib.scad | 89 ++ libtest.scad | 339 ++++++ ribbon_clamp.scad | 119 +++ screw_knob.scad | 77 ++ scripts/blurb.py | 80 ++ scripts/bom.py | 248 +++++ scripts/c14n_stl.py | 108 ++ scripts/deps.py | 49 + scripts/dxfs.py | 32 + scripts/exports.py | 133 +++ scripts/gallery.py | 68 ++ scripts/make_all.py | 35 + scripts/openscad.py | 38 + scripts/render.py | 69 ++ scripts/set_config.py | 84 ++ scripts/stls.py | 32 + scripts/svgs.py | 32 + scripts/tests.py | 230 ++++ scripts/times.py | 71 ++ scripts/views.py | 345 ++++++ socket_box.scad | 127 +++ strap_handle.scad | 189 ++++ tests/annotation.scad | 30 + tests/ball_bearings.scad | 30 + tests/batteries.scad | 41 + tests/belts.scad | 74 ++ tests/bezier.scad | 83 ++ tests/blowers.scad | 41 + tests/bom.scad | 108 ++ tests/box.scad | 54 + tests/bulldogs.scad | 29 + tests/butt_box.scad | 66 ++ tests/buttons.scad | 29 + tests/cable_grommets.scad | 36 + tests/cable_strips.scad | 38 + tests/carriers.scad | 25 + tests/clip.scad | 26 + tests/components.scad | 57 + tests/corner_block.scad | 37 + tests/d_connectors.scad | 38 + tests/displays.scad | 29 + tests/dogbones.scad | 34 + tests/door_hinge.scad | 57 + tests/door_latch.scad | 29 + tests/fan_guard.scad | 29 + tests/fans.scad | 31 + tests/fillet.scad | 26 + tests/fixing_block.scad | 37 + tests/foot.scad | 37 + tests/fuseholder.scad | 27 + tests/global.scad | 31 + tests/handle.scad | 29 + tests/hanging_hole.scad | 28 + tests/hot_ends.scad | 31 + tests/iecs.scad | 30 + tests/inserts.scad | 30 + tests/jack.scad | 35 + tests/layout.scad | 30 + tests/leadnuts.scad | 30 + tests/leds.scad | 29 + tests/light_strips.scad | 38 + tests/linear_bearings.scad | 29 + tests/mains_sockets.scad | 29 + tests/maths.scad | 70 ++ tests/meter.scad | 39 + tests/microswitches.scad | 29 + tests/microview.scad | 21 + tests/modules.scad | 30 + tests/nuts.scad | 51 + tests/o_ring.scad | 25 + tests/offset.scad | 39 + tests/opengrab.scad | 32 + tests/pcbs.scad | 32 + tests/pillars.scad | 29 + tests/polyholes.scad | 77 ++ tests/psus.scad | 36 + tests/pulleys.scad | 32 + tests/quadrant.scad | 29 + tests/rails.scad | 48 + tests/ribbon_clamp.scad | 35 + tests/ring_terminals.scad | 31 + tests/rockers.scad | 29 + tests/rod.scad | 31 + tests/round.scad | 42 + tests/rounded_cylinder.scad | 32 + tests/rounded_polygon.scad | 57 + tests/rounded_rectangle.scad | 30 + tests/screw_knob.scad | 37 + tests/screws.scad | 37 + tests/sealing_strip.scad | 26 + tests/sector.scad | 29 + tests/sheets.scad | 31 + tests/socket_box.scad | 31 + tests/spades.scad | 29 + tests/sphere.scad | 31 + tests/spools.scad | 31 + tests/springs.scad | 29 + tests/ssrs.scad | 30 + tests/stepper_motors.scad | 34 + tests/strap_handle.scad | 34 + tests/sweep.scad | 53 + tests/teardrops.scad | 43 + tests/toggles.scad | 30 + tests/transformers.scad | 31 + tests/tube.scad | 32 + tests/tubings.scad | 29 + tests/variacs.scad | 31 + tests/veroboard.scad | 48 + tests/washers.scad | 47 + tests/zipties.scad | 29 + times.txt | 92 ++ utils/annotation.scad | 40 + utils/bezier.scad | 57 + utils/core/bom.scad | 112 ++ utils/core/clip.scad | 49 + utils/core/global.scad | 64 ++ utils/core/polyholes.scad | 79 ++ utils/core/rounded_rectangle.scad | 33 + utils/core/sphere.scad | 28 + utils/core/teardrops.scad | 53 + utils/dogbones.scad | 46 + utils/fillet.scad | 32 + utils/hanging_hole.scad | 64 ++ utils/layout.scad | 33 + utils/maths.scad | 76 ++ utils/offset.scad | 63 ++ utils/quadrant.scad | 40 + utils/round.scad | 44 + utils/rounded_cylinder.scad | 47 + utils/rounded_polygon.scad | 93 ++ utils/sector.scad | 36 + utils/sweep.scad | 160 +++ utils/tube.scad | 33 + vitamins/ball_bearing.scad | 68 ++ vitamins/ball_bearings.scad | 24 + vitamins/batteries.scad | 50 + vitamins/battery.scad | 161 +++ vitamins/belt.scad | 81 ++ vitamins/belts.scad | 35 + vitamins/blower.scad | 127 +++ vitamins/blowers.scad | 25 + vitamins/bulldog.scad | 83 ++ vitamins/bulldogs.scad | 35 + vitamins/button.scad | 74 ++ vitamins/buttons.scad | 33 + vitamins/cable_strip.scad | 159 +++ vitamins/component.scad | 450 ++++++++ vitamins/components.scad | 50 + vitamins/d_connector.scad | 191 ++++ vitamins/d_connectors.scad | 29 + vitamins/display.scad | 109 ++ vitamins/displays.scad | 52 + vitamins/e3d.scad | 180 ++++ vitamins/fan.scad | 170 +++ vitamins/fans.scad | 46 + vitamins/fuseholder.scad | 133 +++ vitamins/hot_end.scad | 53 + vitamins/hot_ends.scad | 44 + vitamins/iec.scad | 228 ++++ vitamins/iecs.scad | 47 + vitamins/insert.scad | 104 ++ vitamins/inserts.scad | 43 + vitamins/jack.scad | 284 +++++ vitamins/jhead.scad | 202 ++++ vitamins/leadnut.scad | 67 ++ vitamins/leadnuts.scad | 25 + vitamins/led.scad | 56 + vitamins/leds.scad | 34 + vitamins/light_strip.scad | 166 +++ vitamins/light_strips.scad | 33 + vitamins/linear_bearing.scad | 44 + vitamins/linear_bearings.scad | 33 + vitamins/mains_socket.scad | 123 +++ vitamins/mains_sockets.scad | 29 + vitamins/meter.scad | 130 +++ vitamins/microswitch.scad | 117 ++ vitamins/microswitches.scad | 33 + vitamins/microview.scad | 59 ++ .../GKM-002_R05_CHIP_UPPER_HOUSING-1.STL | Bin 0 -> 63084 bytes .../GKM-003_R05_CHIP_LOWER_HOUSING.STL | Bin 0 -> 1261084 bytes vitamins/microview/LICENSE.txt | 21 + vitamins/module.scad | 147 +++ vitamins/modules.scad | 37 + vitamins/nut.scad | 140 +++ vitamins/nuts.scad | 56 + vitamins/o_ring.scad | 40 + vitamins/opengrab.scad | 93 ++ vitamins/pcb.scad | 996 ++++++++++++++++++ vitamins/pcbs.scad | 133 +++ vitamins/pillar.scad | 76 ++ vitamins/pillars.scad | 43 + vitamins/psu.scad | 339 ++++++ vitamins/psus.scad | 104 ++ vitamins/pulley.scad | 143 +++ vitamins/pulleys.scad | 50 + vitamins/rail.scad | 165 +++ vitamins/rails.scad | 44 + vitamins/ring_terminal.scad | 98 ++ vitamins/ring_terminals.scad | 34 + vitamins/rocker.scad | 92 ++ vitamins/rockers.scad | 43 + vitamins/rod.scad | 37 + vitamins/screw.scad | 257 +++++ vitamins/screws.scad | 131 +++ vitamins/sealing_strip.scad | 53 + vitamins/sheet.scad | 88 ++ vitamins/sheets.scad | 50 + vitamins/spade.scad | 59 ++ vitamins/spades.scad | 28 + vitamins/spool.scad | 59 ++ vitamins/spools.scad | 32 + vitamins/spring.scad | 92 ++ vitamins/springs.scad | 25 + vitamins/ssr.scad | 88 ++ vitamins/ssrs.scad | 33 + vitamins/stepper_motor.scad | 123 +++ vitamins/stepper_motors.scad | 36 + vitamins/toggle.scad | 157 +++ vitamins/toggles.scad | 43 + vitamins/transformer.scad | 76 ++ vitamins/transformers.scad | 37 + vitamins/tubing.scad | 45 + vitamins/tubings.scad | 39 + vitamins/variac.scad | 108 ++ vitamins/variacs.scad | 36 + vitamins/veroboard.scad | 175 +++ vitamins/washer.scad | 130 +++ vitamins/washers.scad | 53 + vitamins/wire.scad | 76 ++ vitamins/ziptie.scad | 53 + vitamins/zipties.scad | 27 + 246 files changed, 18858 insertions(+) create mode 100644 .gitignore create mode 100644 box.scad create mode 100644 box_assembly.scad create mode 100644 butt_box.scad create mode 100644 cable_grommets.scad create mode 100644 carriers.scad create mode 100644 core.scad create mode 100644 corner_block.scad create mode 100644 door_hinge.scad create mode 100644 door_latch.scad create mode 100644 fan_guard.scad create mode 100644 fixing_block.scad create mode 100644 foot.scad create mode 100644 global_defs.scad create mode 100644 handle.scad create mode 100644 lib.scad create mode 100644 libtest.scad create mode 100644 ribbon_clamp.scad create mode 100644 screw_knob.scad create mode 100644 scripts/blurb.py create mode 100644 scripts/bom.py create mode 100644 scripts/c14n_stl.py create mode 100644 scripts/deps.py create mode 100644 scripts/dxfs.py create mode 100644 scripts/exports.py create mode 100644 scripts/gallery.py create mode 100644 scripts/make_all.py create mode 100644 scripts/openscad.py create mode 100644 scripts/render.py create mode 100644 scripts/set_config.py create mode 100644 scripts/stls.py create mode 100644 scripts/svgs.py create mode 100644 scripts/tests.py create mode 100644 scripts/times.py create mode 100644 scripts/views.py create mode 100644 socket_box.scad create mode 100644 strap_handle.scad create mode 100644 tests/annotation.scad create mode 100644 tests/ball_bearings.scad create mode 100644 tests/batteries.scad create mode 100644 tests/belts.scad create mode 100644 tests/bezier.scad create mode 100644 tests/blowers.scad create mode 100644 tests/bom.scad create mode 100644 tests/box.scad create mode 100644 tests/bulldogs.scad create mode 100644 tests/butt_box.scad create mode 100644 tests/buttons.scad create mode 100644 tests/cable_grommets.scad create mode 100644 tests/cable_strips.scad create mode 100644 tests/carriers.scad create mode 100644 tests/clip.scad create mode 100644 tests/components.scad create mode 100644 tests/corner_block.scad create mode 100644 tests/d_connectors.scad create mode 100644 tests/displays.scad create mode 100644 tests/dogbones.scad create mode 100644 tests/door_hinge.scad create mode 100644 tests/door_latch.scad create mode 100644 tests/fan_guard.scad create mode 100644 tests/fans.scad create mode 100644 tests/fillet.scad create mode 100644 tests/fixing_block.scad create mode 100644 tests/foot.scad create mode 100644 tests/fuseholder.scad create mode 100644 tests/global.scad create mode 100644 tests/handle.scad create mode 100644 tests/hanging_hole.scad create mode 100644 tests/hot_ends.scad create mode 100644 tests/iecs.scad create mode 100644 tests/inserts.scad create mode 100644 tests/jack.scad create mode 100644 tests/layout.scad create mode 100644 tests/leadnuts.scad create mode 100644 tests/leds.scad create mode 100644 tests/light_strips.scad create mode 100644 tests/linear_bearings.scad create mode 100644 tests/mains_sockets.scad create mode 100644 tests/maths.scad create mode 100644 tests/meter.scad create mode 100644 tests/microswitches.scad create mode 100644 tests/microview.scad create mode 100644 tests/modules.scad create mode 100644 tests/nuts.scad create mode 100644 tests/o_ring.scad create mode 100644 tests/offset.scad create mode 100644 tests/opengrab.scad create mode 100644 tests/pcbs.scad create mode 100644 tests/pillars.scad create mode 100644 tests/polyholes.scad create mode 100644 tests/psus.scad create mode 100644 tests/pulleys.scad create mode 100644 tests/quadrant.scad create mode 100644 tests/rails.scad create mode 100644 tests/ribbon_clamp.scad create mode 100644 tests/ring_terminals.scad create mode 100644 tests/rockers.scad create mode 100644 tests/rod.scad create mode 100644 tests/round.scad create mode 100644 tests/rounded_cylinder.scad create mode 100644 tests/rounded_polygon.scad create mode 100644 tests/rounded_rectangle.scad create mode 100644 tests/screw_knob.scad create mode 100644 tests/screws.scad create mode 100644 tests/sealing_strip.scad create mode 100644 tests/sector.scad create mode 100644 tests/sheets.scad create mode 100644 tests/socket_box.scad create mode 100644 tests/spades.scad create mode 100644 tests/sphere.scad create mode 100644 tests/spools.scad create mode 100644 tests/springs.scad create mode 100644 tests/ssrs.scad create mode 100644 tests/stepper_motors.scad create mode 100644 tests/strap_handle.scad create mode 100644 tests/sweep.scad create mode 100644 tests/teardrops.scad create mode 100644 tests/toggles.scad create mode 100644 tests/transformers.scad create mode 100644 tests/tube.scad create mode 100644 tests/tubings.scad create mode 100644 tests/variacs.scad create mode 100644 tests/veroboard.scad create mode 100644 tests/washers.scad create mode 100644 tests/zipties.scad create mode 100644 times.txt create mode 100644 utils/annotation.scad create mode 100644 utils/bezier.scad create mode 100644 utils/core/bom.scad create mode 100644 utils/core/clip.scad create mode 100644 utils/core/global.scad create mode 100644 utils/core/polyholes.scad create mode 100644 utils/core/rounded_rectangle.scad create mode 100644 utils/core/sphere.scad create mode 100644 utils/core/teardrops.scad create mode 100644 utils/dogbones.scad create mode 100644 utils/fillet.scad create mode 100644 utils/hanging_hole.scad create mode 100644 utils/layout.scad create mode 100644 utils/maths.scad create mode 100644 utils/offset.scad create mode 100644 utils/quadrant.scad create mode 100644 utils/round.scad create mode 100644 utils/rounded_cylinder.scad create mode 100644 utils/rounded_polygon.scad create mode 100644 utils/sector.scad create mode 100644 utils/sweep.scad create mode 100644 utils/tube.scad create mode 100644 vitamins/ball_bearing.scad create mode 100644 vitamins/ball_bearings.scad create mode 100644 vitamins/batteries.scad create mode 100644 vitamins/battery.scad create mode 100644 vitamins/belt.scad create mode 100644 vitamins/belts.scad create mode 100644 vitamins/blower.scad create mode 100644 vitamins/blowers.scad create mode 100644 vitamins/bulldog.scad create mode 100644 vitamins/bulldogs.scad create mode 100644 vitamins/button.scad create mode 100644 vitamins/buttons.scad create mode 100644 vitamins/cable_strip.scad create mode 100644 vitamins/component.scad create mode 100644 vitamins/components.scad create mode 100644 vitamins/d_connector.scad create mode 100644 vitamins/d_connectors.scad create mode 100644 vitamins/display.scad create mode 100644 vitamins/displays.scad create mode 100644 vitamins/e3d.scad create mode 100644 vitamins/fan.scad create mode 100644 vitamins/fans.scad create mode 100644 vitamins/fuseholder.scad create mode 100644 vitamins/hot_end.scad create mode 100644 vitamins/hot_ends.scad create mode 100644 vitamins/iec.scad create mode 100644 vitamins/iecs.scad create mode 100644 vitamins/insert.scad create mode 100644 vitamins/inserts.scad create mode 100644 vitamins/jack.scad create mode 100644 vitamins/jhead.scad create mode 100644 vitamins/leadnut.scad create mode 100644 vitamins/leadnuts.scad create mode 100644 vitamins/led.scad create mode 100644 vitamins/leds.scad create mode 100644 vitamins/light_strip.scad create mode 100644 vitamins/light_strips.scad create mode 100644 vitamins/linear_bearing.scad create mode 100644 vitamins/linear_bearings.scad create mode 100644 vitamins/mains_socket.scad create mode 100644 vitamins/mains_sockets.scad create mode 100644 vitamins/meter.scad create mode 100644 vitamins/microswitch.scad create mode 100644 vitamins/microswitches.scad create mode 100644 vitamins/microview.scad create mode 100644 vitamins/microview/GKM-002_R05_CHIP_UPPER_HOUSING-1.STL create mode 100644 vitamins/microview/GKM-003_R05_CHIP_LOWER_HOUSING.STL create mode 100644 vitamins/microview/LICENSE.txt create mode 100644 vitamins/module.scad create mode 100644 vitamins/modules.scad create mode 100644 vitamins/nut.scad create mode 100644 vitamins/nuts.scad create mode 100644 vitamins/o_ring.scad create mode 100644 vitamins/opengrab.scad create mode 100644 vitamins/pcb.scad create mode 100644 vitamins/pcbs.scad create mode 100644 vitamins/pillar.scad create mode 100644 vitamins/pillars.scad create mode 100644 vitamins/psu.scad create mode 100644 vitamins/psus.scad create mode 100644 vitamins/pulley.scad create mode 100644 vitamins/pulleys.scad create mode 100644 vitamins/rail.scad create mode 100644 vitamins/rails.scad create mode 100644 vitamins/ring_terminal.scad create mode 100644 vitamins/ring_terminals.scad create mode 100644 vitamins/rocker.scad create mode 100644 vitamins/rockers.scad create mode 100644 vitamins/rod.scad create mode 100644 vitamins/screw.scad create mode 100644 vitamins/screws.scad create mode 100644 vitamins/sealing_strip.scad create mode 100644 vitamins/sheet.scad create mode 100644 vitamins/sheets.scad create mode 100644 vitamins/spade.scad create mode 100644 vitamins/spades.scad create mode 100644 vitamins/spool.scad create mode 100644 vitamins/spools.scad create mode 100644 vitamins/spring.scad create mode 100644 vitamins/springs.scad create mode 100644 vitamins/ssr.scad create mode 100644 vitamins/ssrs.scad create mode 100644 vitamins/stepper_motor.scad create mode 100644 vitamins/stepper_motors.scad create mode 100644 vitamins/toggle.scad create mode 100644 vitamins/toggles.scad create mode 100644 vitamins/transformer.scad create mode 100644 vitamins/transformers.scad create mode 100644 vitamins/tubing.scad create mode 100644 vitamins/tubings.scad create mode 100644 vitamins/variac.scad create mode 100644 vitamins/variacs.scad create mode 100644 vitamins/veroboard.scad create mode 100644 vitamins/washer.scad create mode 100644 vitamins/washers.scad create mode 100644 vitamins/wire.scad create mode 100644 vitamins/ziptie.scad create mode 100644 vitamins/zipties.scad diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0bee55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*_canute.bat +Thumbs.db +__pycache__ +*.pyc +tests/bom/ +tests/deps/ +*.log +*.html diff --git a/box.scad b/box.scad new file mode 100644 index 0000000..48b8def --- /dev/null +++ b/box.scad @@ -0,0 +1,471 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! A box made from routed or laser cut sheet sheets and printed profiles and bezels. It can be arbitrarily large +//! compared to the 3D printed parts because they can be cut into interlocking sections and solvent welded +//! together. The box panels can be customised to have holes and parts mounted on them by overriding the +//! definitions of `box_base()`, `box_front()`, etc. +//! +//! `box.scad` should be ```use```d and `box_assembly.scad` ```include```d. +//! +//! A box is defined with a list that specifies the inside dimensions, top, bottom and side sheet materials, the +//! screw type and printed part wall thickness. This diagram shows how the various dimensions are labelled: +//! ![](docs/box.png) +//! +//! Normally the side sheets are the same type but they can be overridden individually as long as the substitute has the same thickness. +// +include +use +use +use + +include +use + +bezel_clearance = 0.2; +sheet_end_clearance = 1; +sheet_slot_clearance = 0.2; + +function box_screw(type) = type[0]; //! Screw type to be used at the corners +function box_wall(type) = type[1]; //! Wall thickness of 3D parts +function box_sheets(type) = type[2]; //! Sheet type used for the sides +function box_top_sheet(type) = type[3]; //! Sheet type for the top +function box_base_sheet(type)= type[4]; //! Sheet type for the bottom +function box_feet(type) = type[5]; //! True to enable feet on the bottom bezel +function box_width(type) = type[6]; //! Internal width +function box_depth(type) = type[7]; //! Internal depth +function box_height(type) = type[8]; //! Internal height + +function box_bezel_clearance(type) = bezel_clearance; + +function box_corner_gap(type) = 3; //! Gap between box_sheets at the corners to connect inside and outside profiles +function box_profile_overlap(type) = 3 + sheet_end_clearance / 2; + +function box_washer(type) = screw_washer(box_screw(type)); +function box_insert(type) = screw_insert(box_screw(type)); + +function box_hole_inset(type) = washer_radius(box_washer(type)) + 1; +function box_insert_r(type) = insert_hole_radius(box_insert(type)); +function box_insert_l(type) = insert_length(box_insert(type)); +function box_boss_r(type) = ceil(corrected_radius(box_insert_r(type)) + box_wall(type)); + +function box_sheet_slot(type) = sheet_thickness(box_sheets(type)) + sheet_slot_clearance; // add some clearance +function box_corner_overlap(type) = box_wall(type); + +function box_corner_rad(type) = box_sheet_slot(type) - sheet_slot_clearance / 2 + box_corner_gap(type) + box_corner_overlap(type); +function box_sheet_r(type) = box_corner_rad(type) - box_sheet_slot(type) - box_corner_overlap(type); + +function box_screw_length(type, top) = screw_longer_than(2 * washer_thickness(box_washer(type)) + + sheet_thickness(top ? box_top_sheet(type) : box_base_sheet(type)) + + box_corner_gap(type) + box_profile_overlap(type) + box_insert_l(type) - 1); + +function box_wall_clearance(type) = box_sheet_slot(type) / 2 - sheet_thickness(box_sheets(type)) / 2; +function box_margin(type) = box_profile_overlap(type) + box_corner_gap(type); //! How much the bezel intrudes on the specified height +function box_intrusion(type) = box_hole_inset(type) + box_boss_r(type); //! Corner profile intrusion +function sheet_reduction(type) = 2 * box_corner_gap(type) + sheet_end_clearance; +function box_outset(type) = box_sheet_slot(type) + box_wall(type) - sheet_slot_clearance / 2; //! How much the bezel expands the specified internal size +function box_inset(type) = box_wall(type) + sheet_slot_clearance / 2; //! How much the bezel intrudes on the specified width and length, away from the corners +function box_bezel_height(type, bottom) = //! Bezel height for top or bottom + let(t1 = sheet_thickness(box_base_sheet(type)), t2 = sheet_thickness(box_top_sheet(type))) + box_corner_rad(type) + box_profile_overlap(type) + (bottom ? max(t1, t2) : t2) - sheet_thickness(box_sheets(type)); + +grill_hole = 5; +grill_gap = 1.9; +module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered array of 5mm holes to make grills in sheets. Can be constrained to be circular. Set ```poly``` ```true``` for printing, ```false``` for milling. + nx = floor(width / (grill_hole + grill_gap)); + xpitch = width / nx; + ny = floor(height / ((grill_hole + grill_gap) * cos(30))); + ypitch = height / ny; + + extrude_if(h) + for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) { + x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch; + y = -height / 2 + (y + 0.5) * ypitch; + if(sqrt(sqr(x) + sqr(y)) + grill_hole / 2 <= r) + translate([x, y]) + if(poly) + poly_circle(r = grill_hole / 2); + else + circle(d = grill_hole); + } +} + +module box_corner_profile_2D(type) { //! The 2D shape of the corner profile. + t = box_sheet_slot(type); + difference() { + union() { + quadrant(box_hole_inset(type) + box_boss_r(type), box_boss_r(type)); // inside corner + + translate([box_corner_gap(type) + box_profile_overlap(type), box_corner_gap(type) + box_profile_overlap(type)]) + rotate(180) + quadrant(box_profile_overlap(type) + box_corner_rad(type), box_corner_rad(type)); // outside corner + } + translate([box_corner_gap(type), -t + sheet_slot_clearance / 2]) + square([100, t]); + + translate([-t + sheet_slot_clearance / 2, box_corner_gap(type)]) + square([t, 100]); + } +} + +module box_corner_profile(type) { //! Generates the corner profile STL for 3D printing. + stl("box_corner_profile"); + + length = box_height(type) - 2 * box_margin(type); + difference() { + linear_extrude(height = length, center = true, convexity = 5) + box_corner_profile_2D(type); + + for(z = [-1, 1]) + translate([box_hole_inset(type), box_hole_inset(type), z * length / 2]) + insert_hole(box_insert(type), 5); + } +} + +module box_corner_profile_section(type, section, sections) { //! Generates interlocking sections of the corner profile to allow it to be taller than the printer + overlap = 4; + length = box_height(type) - 2 * box_margin(type); + section_length = round_to_layer((length - overlap) / sections); + last_section = section >= sections - 1; + h = last_section ? length - (sections - 1) * section_length : section_length; + overlap_wall = 2; + + difference() { + union() { + linear_extrude(height = h, convexity = 5) + box_corner_profile_2D(type); + + if(!last_section) // male end always at the top + translate_z(section_length - 1) + for(i = [0 : 1], offset = i * layer_height) + linear_extrude(height = overlap + 1 - offset) + offset(1 + offset - layer_height) + offset(-overlap_wall - 1) + box_corner_profile_2D(type); + } + if(section > 0) + translate_z(last_section ? h : 0) { // female at bottom unless last section + linear_extrude(height = 2 * (overlap + layer_height), center = true, convexity = 5) + offset(-overlap_wall) + box_corner_profile_2D(type); + + linear_extrude(height = 2 * layer_height, center = true, convexity = 5) + offset(-overlap_wall + layer_height) + box_corner_profile_2D(type); + } + if(!section || last_section) // insert holes always at the bottom + translate([box_hole_inset(type), box_hole_inset(type)]) + insert_hole(box_insert(type), 5); + } +} + +module box_corner_quadrants(type, width, depth) + for(corner = [0:3]) { + x = [-1,1,1,-1][corner]; + y = [-1,-1,1,1][corner]; + + translate([x * width / 2, y * depth / 2, 0]) + rotate(corner * 90) + quadrant(box_intrusion(type), box_boss_r(type)); + } + +module box_bezel(type, bottom) { //! Generates top and bottom bezel STLs + stl(bottom ? "bottom_bezel" : "top_bezel"); + feet = bottom && box_feet(type); + t = box_sheet_slot(type); + outset = box_outset(type); + inner_r = box_sheet_r(type); + foot_height = box_corner_gap(type) + sheet_thickness(box_base_sheet(type)) + washer_thickness(box_washer(type)) + screw_head_height(box_screw(type)) + box_profile_overlap(type) + 2; + foot_length = box_corner_rad(type) * 2; + height = box_bezel_height(type, bottom); + foot_extension = foot_height - height; + + difference() { + translate_z(-box_profile_overlap(type)) difference() { + rounded_rectangle([box_width(type) + 2 * outset, box_depth(type) + 2 * outset, feet ? foot_height : height], box_corner_rad(type), false); + // + // Remove edges between the feet + // + if(feet) + hull() { + translate_z(height + 0.5) + cube([box_width(type) - 2 * foot_length, box_depth(type) + 2 * outset + 1, 1], center = true); + + translate_z(foot_height + 1) + cube([box_width(type) - 2 * (foot_length - foot_extension), box_depth(type) + 2 * outset + 1, 1], center = true); + } + if(feet) + hull() { + translate_z(height + 0.5) + cube([box_width(type) + 2 * outset + 1, box_depth(type) - 2 * foot_length, 1], center = true); + + translate_z(foot_height + 1) + cube([box_width(type) + 2 * outset + 1, box_depth(type) - 2 * (foot_length - foot_extension), 1], center = true); + } + } + // + // slots for side panels + // + translate_z(-box_profile_overlap(type)) + linear_extrude(height = 2 * box_profile_overlap(type), center = true) + for(i = [-1, 1]) { + translate([i * (box_width(type) / 2 + t / 2 - sheet_slot_clearance / 2), 0]) + square([t, box_depth(type) - 2 * box_corner_gap(type)], center = true); + + translate([0, i * (box_depth(type) / 2 + t / 2 - sheet_slot_clearance / 2)]) + square([box_width(type) - 2 * box_corner_gap(type), t], center = true); + } + // + // recess for top / bottom panel + // + translate_z(box_corner_gap(type)) + rounded_rectangle([box_width(type) + bezel_clearance, box_depth(type) + bezel_clearance, height], inner_r + bezel_clearance / 2, false); + // + // leave plastic over the corner profiles + // + translate_z(-box_profile_overlap(type) - 1) + linear_extrude(height = box_profile_overlap(type) + box_corner_gap(type) + 2) + union() { + difference() { + square([box_width(type) - 2 * box_inset(type), + box_depth(type) - 2 * box_inset(type)], center = true); + + box_corner_quadrants(type, box_width(type), box_depth(type)); + } + box_screw_hole_positions(type) + poly_circle(screw_clearance_radius(box_screw(type))); + } + } +} + +dowel_length = 20; +dowel_wall = extrusion_width * 3; +dowel_h_wall = layer_height * 6; + + +module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlocking sections of the bezel to allow it to be bigger than the printer + w = (box_width(type) + 2 * box_outset(type)) / cols; + h = (box_depth(type) + 2 * box_outset(type)) / rows; + bw = box_outset(type) - bezel_clearance / 2; + bw2 = box_outset(type) + box_inset(type); + + dw = bw - 2 * dowel_wall; + dh = box_bezel_height(type, bottom) - dowel_h_wall; + + dh2 = box_profile_overlap(type) + box_corner_gap(type) - dowel_h_wall; + + end_clearance = 0.5; + module male() { + rotate([90, 0, 90]) + linear_extrude(height = dowel_length - 2 * end_clearance, center = true) + difference() { + union() { + h = dh - layer_height; + h2 = dh2 - layer_height; + hull() { + translate([bw / 2, h / 2]) + square([dw - 1, h], center = true); + + translate([bw / 2, (h - 1) / 2]) + square([dw, h - 1], center = true); + } + + hull() { + translate([bw2 / 2, h2 / 2]) + square([bw2 - 2 * dowel_wall - 1, h2], center = true); + + translate([bw2 / 2, (h2 - 1) / 2]) + square([bw2 - 2 * dowel_wall, h2 - 1], center = true); + } + } + translate([bw2 / 2, 0]) + square([box_sheet_slot(type), 2 * box_profile_overlap(type)], center = true); + } + } + + module female() { + union() { + translate([0, bw / 2, dh / 2]) + cube([dowel_length, dw, dh], center = true); + + translate([0, bw2 / 2]) + cube([dowel_length, bw2 - 2 * dowel_wall, dh2 * 2], center = true); + + hull() { + translate([0, bw / 2, dh / 2]) + cube([2, dw, dh], center = true); + + translate([0, bw / 2, dh / 2]) + cube([eps, dw + 2 * extrusion_width, dh], center = true); + + } + hull() { + translate([0, bw2 / 2, dh2 / 2]) + cube([2, bw2 - 2 * dowel_wall, dh2], center = true); + + translate([0, bw2 / 2, dh2 / 2]) + cube([eps, bw2 - 2 * dowel_wall + 2 * extrusion_width, dh2], center = true); + + } + } + } + + module support() { + if(!$preview) + translate([0, bw / 2 + dw / 2]) + cube([dowel_length / 2 - 0.25, 2 * extrusion_width + 0.2, dh2]); + } + + union() { + render() difference() { + union() { + clip(xmin = 0, xmax = w, ymin = 0, ymax = h) + translate([box_width(type) / 2 + box_outset(type) - x * w, box_depth(type) / 2 + box_outset(type) - y * h, box_profile_overlap(type)]) + box_bezel(type, bottom); + + if(x < cols - 1 && y == 0) + translate([w, 0]) + male(); + + if(x > 0 && y == rows - 1) + translate([0, h]) + rotate(180) + male(); + + if(y < rows - 1 && x == cols - 1) + translate([w, h]) + rotate(90) + male(); + + if(y > 0 && x == 0) + rotate(-90) + male(); + } + + if(x < cols - 1 && y == rows - 1) + translate([w, h]) + rotate(180) + female(); + + if(x > 0 && y == 0) + female(); + + if(y < rows - 1 && x == 0) + translate([0, h]) + rotate(-90) + female(); + + if(y > 0 && x == cols - 1) + translate([w, 0]) + rotate(90) + female(); + } + if(x < cols - 1 && y == rows - 1) + translate([w, h]) + rotate(180) + support(); + + if(x > 0 && y == 0) + support(); + + if(y < rows - 1 && x == 0) + translate([0, h]) + rotate(-90) + support(); + + if(y > 0 && x == cols - 1) + translate([w, 0]) + rotate(90) + support(); + } +} + +module box_shelf_blank(type) { //! Generates a 2D template for a shelf sheet + dxf("box_shelf"); + + difference() { + sheet_2D(box_sheets(type), box_width(type) - bezel_clearance, box_depth(type) - bezel_clearance, 1); + + offset(bezel_clearance / 2) + box_corner_quadrants(type, box_width(type), box_depth(type)); + } +} + +module box_screw_hole_positions(type) + for(x = [-1, 1], y = [-1, 1]) + translate([x * (box_width(type) / 2 - box_hole_inset(type)), y * (box_depth(type) / 2 - box_hole_inset(type))]) + children(); + +module box_base_blank(type) { //! Generates a 2D template for the base sheet + dxf("box_base"); + + difference() { + sheet_2D(box_base_sheet(type), box_width(type), box_depth(type), box_sheet_r(type)); + + box_screw_hole_positions(type) + drill(screw_clearance_radius(box_screw(type)), 0); + } +} + +module box_top_blank(type) { //! Generates a 2D template for the top sheet + dxf("box_top"); + + difference() { + sheet_2D(box_top_sheet(type), box_width(type), box_depth(type), box_sheet_r(type)); + + box_screw_hole_positions(type) + drill(screw_clearance_radius(box_screw(type)), 0); + } +} + +function subst_sheet(type, sheet) = + let(s = box_sheets(type)) + sheet ? assert(sheet_thickness(sheet) == sheet_thickness(s)) sheet : s; + +module box_left_blank(type, sheet = false) { //! Generates a 2D template for the left sheet, ```sheet``` can be set to override the type + dxf("box_left"); + + sheet_2D(subst_sheet(type, sheet), box_depth(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1); +} + +module box_right_blank(type, sheet = false) { //! Generates a 2D template for the right sheet, ```sheet``` can be set to override the type + dxf("box_right"); + + sheet_2D(subst_sheet(type, sheet), box_depth(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1); +} + +module box_front_blank(type, sheet = false) { //! Generates a 2D template for the front sheet, ```sheet``` can be set to override the type + dxf("box_front"); + + sheet_2D(subst_sheet(type, sheet), box_width(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1); +} + +module box_back_blank(type, sheet = false) { //! Generates a 2D template for the back sheet, ```sheet``` can be set to override the type + dxf("box_back"); + + sheet_2D(subst_sheet(type, sheet), box_width(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1); +} + +module box_base(type) render_2D_sheet(box_base_sheet(type)) box_base_blank(type); //! Default base, can be overridden to customise +module box_top(type) render_2D_sheet(box_top_sheet(type)) box_top_blank(type); //! Default top, can be overridden to customise +module box_back(type) render_2D_sheet(box_sheets(type)) box_back_blank(type); //! Default back, can be overridden to customise +module box_front(type) render_2D_sheet(box_sheets(type)) box_front_blank(type); //! Default front, can be overridden to customise +module box_left(type) render_2D_sheet(box_sheets(type)) box_left_blank(type); //! Default left side, can be overridden to customise +module box_right(type) render_2D_sheet(box_sheets(type)) box_right_blank(type); //! Default right side, can be overridden to customise diff --git a/box_assembly.scad b/box_assembly.scad new file mode 100644 index 0000000..9618f9e --- /dev/null +++ b/box_assembly.scad @@ -0,0 +1,94 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// The assembly is ```include```d so the panel definitions can be overridden to add holes and components. +// The _box_module also needs to be wrapped in the file that uses it so it can be called without +// parameters to make the assembly views. E.g. module box_assembly() _box_assembly(box); +// +module _box_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true, bezels = true, corners = 4) +assembly("box") { + echo("Box:", box_width(type), box_depth(type), box_height(type)); + + t = sheet_thickness(box_sheets(type)); + + for(corner = [0 : corners - 1]) { + x = [-1,1,1,-1][corner]; + y = [-1,-1,1,1][corner]; + translate([x * (box_width(type) / 2 + 25 * exploded()), y * (box_depth(type) / 2 + 25 * exploded())]) + rotate(corner * 90) { + color(pp2_colour) render() + box_corner_profile(type); + + translate([box_hole_inset(type), box_hole_inset(type)]) + for(z = [-1, 1]) + rotate([z * 90 -90, 0, 0]) + translate_z(box_height(type) / 2 - box_margin(type)) + insert(box_insert(type)); + } + } + + for(z = [-1, 1]) { + sheet_thickness = sheet_thickness(z > 0 ? box_top_sheet(type) : box_base_sheet(type)); + + translate_z(z * (box_height(type) / 2 - box_corner_gap(type) + 50 * exploded())) + rotate([z * 90 - 90, 0, 0]) + if(bezels && (z > 0 ? top : base)) + color(pp1_colour) render() box_bezel(type, z < 0); + + translate_z(z * (box_height(type) / 2 + sheet_thickness + 50 * exploded())) + box_screw_hole_positions(type) + rotate([z * 90 -90, 0, 0]) + explode(50, true) + screw_and_washer(box_screw(type), box_screw_length(type, z > 0), true); + } + for(x = [-1, 1]) + translate([x * (box_width(type) / 2 + t / 2 + 25 * exploded()), 0]) + rotate([90, 0, x * 90]) + if(x > 0) { + if(right) + box_right(type); + } + else + if(left) + box_left(type); + + for(y = [-1, 1]) + translate([0, y * (box_depth(type) / 2 + t / 2 + 25 * exploded())]) + rotate([90, 0, y * 90 + 90]) + if(y < 0) { + if(front) + box_front(type); + } + else + if(back) + box_back(type); + + for(z = [-1, 1]) { + sheet_thickness = sheet_thickness(z > 0 ? box_top_sheet(type) : box_base_sheet(type)); + translate_z(z * (box_height(type) / 2 + sheet_thickness / 2 + eps + 100 * exploded())) + if(z > 0) { + if(top) + box_top(type); + } + else + if(base) + box_base(type); + } +} diff --git a/butt_box.scad b/butt_box.scad new file mode 100644 index 0000000..d473b73 --- /dev/null +++ b/butt_box.scad @@ -0,0 +1,261 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! A box made from CNC cut panels butted together using printed fixing blocks. Useful for making large +//! boxes with minimal 3D printing. More blocks are added as the box gets bigger. +//! +//! Needs to be ```include```d rather than ```use```d to allow the panel definitions to be overridden to add holes +//! and mounted components. +//! +//! A list specifies the internal dimensions, screw type, top, bottom and side sheet types and the block +//! maximum spacing. +//! +//! Uses [fixing blocks](#fixing_block) and [corner blocks](#corner_block). +// + +use +use +use + +function bbox_screw(type) = type[0]; //! Screw type for corner blocks +function bbox_sheets(type) = type[1]; //! Sheet type for the sides +function bbox_base_sheet(type)= type[2]; //! Sheet type for the base +function bbox_top_sheet(type) = type[3]; //! Sheet type for the top +function bbox_span(type) = type[4]; //! Maximum span between fixing blocks +function bbox_width(type) = type[5]; //! Internal width +function bbox_depth(type) = type[6]; //! Internal depth +function bbox_height(type) = type[7]; //! Internal height + +module bbox_shelf_blank(type) { //! 2D template for a shelf + dxf("bbox_shelf"); + + sheet_2D(bbox_sheets(type), bbox_width(type), bbox_depth(type), 1); +} + +function corner_block_positions(type) = let( + width = bbox_width(type), + depth = bbox_depth(type), + height = bbox_height(type) + ) + [for(corner = [0 : 3], z = [-1, 1]) + let( + x = [-1,1,1,-1][corner], + y = [-1,-1,1,1][corner] + ) translate([x * (width / 2), y * (depth / 2), z * height / 2]) * + rotate([z > 0 ? 180 : 0, 0, corner * 90 + (z > 0 ? 90 : 0)]) + + ]; + +module corner_block_positions(type) { + bt = sheet_thickness(bbox_base_sheet(type)); + tt = sheet_thickness(bbox_top_sheet(type)); + for(p = corner_block_positions(type)) + let($thickness = transform([0, 0, 0], p).z > 0 ? tt : bt) + multmatrix(p) + children(); +} + +function corner_holes(type) = [for(p = corner_block_positions(type), q = corner_block_holes(bbox_screw(type))) p * q]; + +function fixing_block_positions(type) = let( + width = bbox_width(type), + depth = bbox_depth(type), + height = bbox_height(type), + span = bbox_span(type), + wspans = floor(width / span), + wspan = width / (wspans + 1), + dspans = floor(depth / span), + dspan = depth / (dspans + 1), + hspans = floor(height / span), + hspan = height / (hspans + 1) + ) + [ + for(i = [0 : 1 : wspans - 1], y = [-1, 1], z = [-1, 1]) + translate([(i - (wspans - 1) / 2) * wspan, y * depth / 2, z * height / 2]) * + rotate([0, z * 90 + 90, y * 90 + 90]), + + for(i = [0 : 1 : dspans - 1], x = [-1, 1], z = [-1, 1]) + translate([x * width / 2, (i - (dspans - 1) / 2) * dspan, z * height / 2]) * + rotate([0, z * 90 + 90, x * 90]), + + for(i = [0 : 1 : hspans - 1], x = [-1, 1], y = [-1, 1]) + translate([x * width / 2, y * depth / 2, (i - (hspans - 1) / 2) * hspan]) * + rotate([y > 0 ? 180 : 0, x * y * 90]), + + ]; + +function side_holes(type) = [for(p = fixing_block_positions(type), q = fixing_block_holes(bbox_screw(type))) p * q]; + +module fixing_block_positions(type) { + t = sheet_thickness(bbox_sheets(type)); + bt = sheet_thickness(bbox_base_sheet(type)); + tt = sheet_thickness(bbox_top_sheet(type)); + h = bbox_height(type) / 2 - 1; + for(p = fixing_block_positions(type)) + let(z = transform([0, 0, 0], p).z, $thickness = z > h ? tt : z < -h ? bt : t) + multmatrix(p) + children(); +} + +module drill_holes(type, t) + for(list = [corner_holes(type), side_holes(type)], p = list) + let(q = t * p) + if(abs(transform([0, 0, 0], q).z) < eps) + multmatrix(q) + drill(screw_clearance_radius(bbox_screw(type)), 0); + +module bbox_base_blank(type) { //! 2D template for the base + dxf("bbox_base"); + + difference() { + sheet_2D(bbox_base_sheet(type), bbox_width(type), bbox_depth(type), 1); + + drill_holes(type, translate(bbox_height(type) / 2)); + } +} + +module bbox_top_blank(type) { //! 2D template for the top + dxf("bbox_top"); + + t = sheet_thickness(bbox_sheets(type)); + + difference() { + translate([0, t / 2]) + sheet_2D(bbox_top_sheet(type), bbox_width(type) + 2 * t, bbox_depth(type) + t); + + drill_holes(type, translate(-bbox_height(type) / 2)); + } +} + +module bbox_left_blank(type) { //! 2D template for the left side + dxf("bbox_left"); + + t = sheet_thickness(bbox_sheets(type)); + bb = sheet_thickness(bbox_base_sheet(type)); + + difference() { + translate([-t / 2, -bb / 2]) + sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb); + + drill_holes(type, rotate([0, 90, 90]) * translate([bbox_width(type) / 2, 0])); + } +} + +module bbox_right_blank(type) { //! 2D template for the right side + dxf("bbox_right"); + + t = sheet_thickness(bbox_sheets(type)); + bb = sheet_thickness(bbox_base_sheet(type)); + + difference() { + translate([t / 2, -bb / 2]) + sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb); + + drill_holes(type, rotate([0, -90, 90]) * translate([-bbox_width(type) / 2, 0])); + } +} + +module bbox_front_blank(type) { //! 2D template for the front + dxf("bbox_front"); + + t = sheet_thickness(bbox_sheets(type)); + bb = sheet_thickness(bbox_base_sheet(type)); + bt = sheet_thickness(bbox_top_sheet(type)); + + difference() { + translate([0, (bt - bb) / 2]) + sheet_2D(bbox_sheets(type), bbox_width(type) + 2 * t, bbox_height(type) + bb + bt); + + drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2])); + } +} + +module bbox_back_blank(type) { //! 2D template for the back + dxf("bbox_back"); + + bb = sheet_thickness(bbox_base_sheet(type)); + t = sheet_thickness(bbox_sheets(type)); + + difference() { + translate([0, -bb / 2]) + sheet_2D(bbox_sheets(type), bbox_width(type), bbox_height(type) + bb); + + drill_holes(type, rotate([90, 0, 0]) * translate([0, -bbox_depth(type) / 2])); + } +} + +module bbox_base(type) render_2D_sheet(bbox_base_sheet(type)) bbox_base_blank(type); //! Default base, can be overridden to customise +module bbox_top(type) render_2D_sheet(bbox_top_sheet(type)) bbox_top_blank(type); //! Default top, can be overridden to customise +module bbox_back(type) render_2D_sheet(bbox_sheets(type)) bbox_back_blank(type); //! Default back, can be overridden to customise +module bbox_front(type) render_2D_sheet(bbox_sheets(type)) bbox_front_blank(type); //! Default front, can be overridden to customise +module bbox_left(type) render_2D_sheet(bbox_sheets(type)) bbox_left_blank(type); //! Default left side, can be overridden to customise +module bbox_right(type) render_2D_sheet(bbox_sheets(type)) bbox_right_blank(type); //! Default right side, can be overridden to customise + +module _bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true) //! The box assembly, wrap with a local copy without parameters +assembly("bbox") { + width = bbox_width(type); + depth = bbox_depth(type); + height = bbox_height(type); + echo("Box:", width, depth, height); + + t = sheet_thickness(bbox_sheets(type)); + bt = sheet_thickness(bbox_base_sheet(type)); + tt = sheet_thickness(bbox_top_sheet(type)); + + corner_block_positions(type) + fastened_corner_block_assembly(t, bbox_screw(type), $thickness); + + fixing_block_positions(type) + fastened_fixing_block_assembly(t, bbox_screw(type), thickness2 = $thickness); + + for(x = [-1, 1]) + translate([x * (width / 2 + t / 2 + eps + 25 * exploded()), 0]) + rotate([90, 0, x * 90]) + if(x > 0) { + if(right) + bbox_right(type); + } + else + if(left) + bbox_left(type); + + for(y = [-1, 1]) + translate([0, y * (depth / 2 + t / 2 + eps + 25 * exploded())]) + rotate([90, 0, y * 90 + 90]) + if(y < 0) { + if(front) + bbox_front(type); + } + else + if(back) + bbox_back(type); + + for(z = [-1, 1]) { + sheet_thickness = z > 0 ? tt : bt; + translate_z(z * (height / 2 + sheet_thickness / 2 + eps + 100 * exploded())) + if(z > 0) { + if(top) + bbox_top(type); + } + else + if(base) + bbox_base(type); + } +} diff --git a/cable_grommets.scad b/cable_grommets.scad new file mode 100644 index 0000000..ac2366c --- /dev/null +++ b/cable_grommets.scad @@ -0,0 +1,187 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Printed cable grommets for passing cables through panels avoiding sharp edges and in the case +//! of conductive panels, an extra layer of insulation. +// +include +use + +base = 1.25; +slot_height = round_to_layer(1.27) + layer_height; +wall = 1.6; +overlap = 1.5; +height = base + slot_height + wall + overlap; +rad = wall + overlap; +clearance = 0.1; + +module ribbon_grommet_hole(ways, h = 50, expand = true) { //! Generate a hole for a ribbon grommet + length = ribbon_clamp_slot(ways) + 2 * wall; + rad = cnc_bit_r; + height = base + slot_height + wall; + extrude_if(h) + offset(expand ? clearance : 0) + hull() { + translate([-length / 2, 0]) + square([length, base]); + + for(end = [-1, 1]) + translate([end * (length / 2 - rad), height - rad]) + drill(rad, 0); + } +} + +module ribbon_grommet(ways, thickness) { //! Generate the STL for a printed ribbon grommet + stl(str("ribbon_grommet_", ways, "_", thickness)); + + width = 2 * (wall + clearance) + thickness; + slot_length = ribbon_clamp_slot(ways); + length = slot_length + 2 * wall + 2 * overlap; + + rotate([90, 0, 0]) + union() { + for(side = [-1, 1]) + translate_z(side * (width - wall) / 2) + linear_extrude(height = wall, center = true, convexity = 5) + difference() { + hull() { + translate([-length / 2, 0]) + square([length, base]); + + for(end = [-1, 1]) + translate([end * (length / 2 - rad), height - rad]) + semi_circle(rad); + } + translate([-slot_length / 2, base]) + square([slot_length, slot_height]); + } + + linear_extrude(height = width -1, center = true) + difference() { + ribbon_grommet_hole(ways, expand = false, h = 0); + + translate([-slot_length / 2, base]) + square([slot_length, slot_height]); + } + } +} + +module round_grommet_top(diameter, thickness, od = undef) { //! Generate the STL for a round grommet top half + stl(str("round_grommet_top_", round(diameter * 10), "_", thickness)); + chamfer = layer_height; + h = wall + thickness + wall; + r1 = diameter / 2; + r2 = od == undef ? corrected_radius(r1) + wall : od / 2; + r3 = r2 + overlap; + r0 = r1 + 1; + union() { + rotate_extrude() + polygon([ + [r0, 0], + [r3 - chamfer, 0], + [r3, chamfer], + [r3, wall], + [r2, wall], + [r2, h - chamfer], + [r2 - chamfer, h], + [r0, h], + ]); + + render() difference() { + cylinder(r = r0 + eps, h = h); + + poly_cylinder(r = r1, h = 100, center = true); + } + } +} + +module round_grommet_bottom(diameter, od = undef) { //! Generate the STL for a round grommet bottom half + stl(str("round_grommet_bottom_", round(diameter * 10))); + chamfer = layer_height; + r1 = diameter / 2; + r2 = od == undef ? corrected_radius(r1) + wall : od / 2; + r3 = r2 + max(overlap, wall + chamfer); + rotate_extrude() + polygon([ + [r2, chamfer], + [r2 + chamfer, 0], + [r3, 0], + [r3, wall - chamfer], + [r3 - chamfer, wall], + [r2, wall], + ]); +} + +module round_grommet_hole(diameter, h = 100) //! Make a hole for a round grommet + drill(diameter / 2 + wall + clearance, h); + +module round_grommet_assembly(diameter, thickness, od = undef) { + color(pp1_colour) + translate_z(wall) + vflip() + round_grommet_top(diameter, thickness, od); + + color(pp2_colour) + translate_z(-thickness) + vflip() + round_grommet_bottom(diameter, od); +} + +module mouse_grommet_hole(r, h = 50, z = undef, expand = wall + clearance) //! Make a hole for a mouse grommet + extrude_if(h) + hull(){ + R = r + expand; + translate([0, z == undef ? R : z]) + semi_circle(R); + + translate([-R, 0]) + square([2 * R, eps]); + } + +module mouse_grommet(r, thickness) { //! Make the STL for a mouse grommet + stl(str("mouse_grommet_", r * 10, "_", thickness)); + + width = 2 * (wall + clearance) + thickness; + length = 2 * r + 2 * wall + 2 * overlap; + + rotate([90, 0, 0]) + union() { + for(side = [-1, 1]) + translate_z(side * (width - wall) / 2) + linear_extrude(height = wall, center = true) + difference() { + mouse_grommet_hole(r + wall + overlap, z = r + wall, h = 0, expand = 0); + + translate([0, wall]) + mouse_grommet_hole(r, h = 0, expand = 0); + } + linear_extrude(height = width - 1, center = true) + difference() { + mouse_grommet_hole(r, h = 0, z = r + wall, expand = wall); + + translate([0, wall]) + mouse_grommet_hole(r, h = 0, expand = 0); + } + } +} + +module ribbon_grommet_20_3_stl() ribbon_grommet(20, 3); +module mouse_grommet_20_3_stl() mouse_grommet(2,3); +module mouse_grommet_30_3_stl() mouse_grommet(3,3); diff --git a/carriers.scad b/carriers.scad new file mode 100644 index 0000000..f0c914a --- /dev/null +++ b/carriers.scad @@ -0,0 +1,63 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Adapts ESP12 module to 0.1" grid. See . +// +$extrusion_width = 0.5; + +include + +module ESP12F_carrier_stl() { //! Generate the STL for an ESP12 carrier + stl("ESP12F_carrier"); + pins = 8; + pitch1 = 2; + pitch2 = 2.54; + hole = pitch1 - squeezed_wall; + hole2 = pitch2 - 3 * extrusion_width; + length1 = (pins - 1) * pitch1 + hole + squeezed_wall * 2; + length2 = (pins - 1) * pitch2 + hole + squeezed_wall * 2; + height = 3; + + wpitch1 = (pins - 1) * pitch1; + wpitch2 = ceil(wpitch1 / 2.54) * 2.54; + + width1 = wpitch1 + hole + squeezed_wall * 2; + width2 = wpitch2 + hole2 + squeezed_wall * 2; + + difference() { + hull() { + translate_z(height - eps / 2) + cube([width1, length1, eps], center = true); + + translate_z(eps / 2) + cube([width2, length2, eps], center = true); + } + + for(side = [-1, 1]) + for(i = [0 : pins - 1]) + hull() { + translate([side * wpitch1 / 2, i * pitch1 - (pins - 1) * pitch1 / 2, height]) + cube([hole, hole, eps], center = true); + + translate([side * wpitch2 / 2, i * pitch2 - (pins - 1) * pitch2 / 2]) + cube([hole2, hole2, eps], center = true); + } + } +} diff --git a/core.scad b/core.scad new file mode 100644 index 0000000..57b7b1d --- /dev/null +++ b/core.scad @@ -0,0 +1,27 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Include this file to use the library +// +include +// +// Global functions and modules +// +use diff --git a/corner_block.scad b/corner_block.scad new file mode 100644 index 0000000..ffb1808 --- /dev/null +++ b/corner_block.scad @@ -0,0 +1,181 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Corner brackets using threaded inserts for fastening three sheets together at right angles. +//! Defaults to M3 but other screws sizes can be specified provided they have inserts defined. +//! +//! See [butt_box](#Butt_box) for an example of usage. +//! +//! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +// +include +include +include +use +use + +def_screw = M3_cap_screw; +wall = 3; +overshoot = 2; // how far screw can overshoot the insert + +function corner_block_screw() = def_screw; //! Default screw type + +function corner_block_hole_offset(screw = def_screw) = //! Hole offset from the edge + let(insert = screw_insert(screw)) + insert_length(insert) + max(overshoot + screw_clearance_radius(screw), insert_hole_radius(insert)) + 1; + +function corner_block_width(screw = def_screw) = //! Block width, depth and height + corner_block_hole_offset(screw) + insert_outer_d(screw_insert(screw)) / 2 + wall; + +function corner_block_v_hole(screw = def_screw) = let(offset = corner_block_hole_offset(screw)) translate([offset, offset]) * rotate([180, 0, 0]); //! Transform to bottom hole + +function corner_block_h_holes(screw = def_screw) = //! List of transforms to side holes + let(offset = corner_block_hole_offset(screw)) + [translate([offset, 0, offset]) * rotate([90, 0, 0]), + translate([0, offset, offset - layer_height]) * rotate([90, 0, -90])]; + +function corner_block_holes(screw) = concat([corner_block_v_hole(screw)], corner_block_h_holes(screw)); //! List of transforms to all holes + +module corner_block_v_hole(screw = def_screw) //! Place children at the bottom screw hole + multmatrix(corner_block_v_hole(screw)) + children(); + +module corner_block_h_holes(screw = def_screw) //! Place children at the side screw holes + for(p = corner_block_h_holes(screw)) + multmatrix(p) + children(); + +module corner_block_holes(screw = def_screw) //! Place children at all the holes + for(p = corner_block_holes(screw)) + multmatrix(p) + children(); + +module corner_block(screw = def_screw, name = false) { //! Generate the STL for a printed corner block + stl(name ? name : str("corner_block", "_M", screw_radius(screw) * 20)); + + r = 1; + cb_width = corner_block_width(screw); + cb_height = cb_width; + cb_depth = cb_width; + insert = screw_insert(screw); + corner_rad = insert_outer_d(insert) / 2 + wall; + offset = corner_block_hole_offset(screw); + difference() { + hull() { + translate([r, r]) + rounded_cylinder(r = r, h = cb_height, r2 = r); + + translate([r, cb_depth - r]) + cylinder(r = r, h = cb_height - corner_rad); + + translate([cb_width - r, r]) + cylinder(r = r, h = cb_height - corner_rad); + + translate([offset, offset, offset]) + sphere(corner_rad); + + translate([offset, offset]) + cylinder(r = corner_rad, h = offset); + + translate([offset, r, offset]) + rotate([-90, 0, 180]) + rounded_cylinder(r = corner_rad, h = r, r2 = r); + + translate([r, offset, offset]) + rotate([0, 90, 180]) + rounded_cylinder(r = corner_rad, h = r, r2 = r); + } + corner_block_v_hole(screw) + insert_hole(insert, overshoot); + + corner_block_h_holes(screw) + insert_hole(insert, overshoot, true); + + children(); + } +} + +module corner_block_assembly(screw = def_screw, name = false) //! The printed block with inserts +assembly(str("corner_block_M", 20 * screw_radius(screw))) { + insert = screw_insert(screw); + + color(name ? pp2_colour : pp1_colour) + render() corner_block(screw, name) children(); + + corner_block_h_holes(screw) + insert(insert); + + corner_block_v_hole(screw) + insert(insert); +} + +module fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, name = false) { //! Printed block with all fasteners + washer = screw_washer(screw); + insert = screw_insert(screw); + screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + insert_length(insert) + overshoot); + + corner_block_assembly(screw, name) children(); + + corner_block_h_holes(screw) + translate_z(thickness) + screw_and_washer(screw, screw_length, true); + + thickness2 = thickness_below ? thickness_below : thickness; + screw_length2 = screw_shorter_than(2 * washer_thickness(washer) + thickness2 + insert_length(insert) + overshoot); + corner_block_v_hole(screw) + translate_z(thickness2) + screw_and_washer(screw, screw_length2, true); +} + +module corner_block_M20_stl() corner_block(M2_cap_screw); +module corner_block_M25_stl() corner_block(M2p5_cap_screw); +module corner_block_M30_stl() corner_block(M3_cap_screw); +module corner_block_M40_stl() corner_block(M4_cap_screw); +// +//! 1. Lay the blocks out and place an M2 insert in each upward facing hole. +//! 1. Push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on each of their other two flat sides and repeat. +// +module corner_block_M20_assembly() corner_block_assembly(M2_cap_screw); + +// +//! 1. Lay the blocks out and place an M2.5 insert in each upward facing hole. +//! 1. Push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on each of their other two flat sides and repeat. +// +module corner_block_M25_assembly() corner_block_assembly(M2p5_cap_screw); + +// +//! 1. Lay the blocks out and place an M3 insert in each upward facing hole. +//! 1. Push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on each of their other two flat sides and repeat. +// +module corner_block_M30_assembly() corner_block_assembly(M3_cap_screw); + +// +//! 1. Lay the blocks out and place an M4 insert in each upward facing hole. +//! 1. Push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on each of their other two flat sides and repeat. +// +module corner_block_M40_assembly() corner_block_assembly(M4_cap_screw); diff --git a/door_hinge.scad b/door_hinge.scad new file mode 100644 index 0000000..060a22e --- /dev/null +++ b/door_hinge.scad @@ -0,0 +1,187 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Door hinges to hang an acrylic sheet door on a 3D printer, default 6mm thick. +//! +//! The screws are tapped into the acrylic. +//! Rubber door [sealing strip](#sealing_strip) is used to make it airtight and a [door_latch](#door_latch) holds it closed. +// +include +include + +width = 18; +thickness = 4; +rad = 3; +dia = 12; + +pin_screw = M3_cap_screw; +screw = M3_dome_screw; +stat_screw = M4_dome_screw; + +stat_width = 15; +stat_length = 34; +stat_clearance = 0.75; + +function door_hinge_pin_x() = -dia / 2; //! X offset of the hinge pin +function door_hinge_pin_y() = -dia / 2 - stat_clearance; //! Y offset of the hinge pin +function door_hinge_screw() = screw; //! Screw type used for hinge pin +function door_hinge_stat_screw() = stat_screw; //! Screw use to fasten the stationary part +function door_hinge_stat_width() = stat_width; //! Width of the stationary part +function door_hinge_stat_length() = stat_length; //! Length of the stationary part + +module door_hinge_hole_positions(dir = 0) { //! Position chidren at the door hole positions + hole_pitch = width - 10; + + for(side = [-1, 1]) + translate([width / 2 + side * hole_pitch / 2, -dir * width / 2 -side * hole_pitch / 2]) + children(); +} + +module door_hinge(door_thickness) { //! Generates STL for the moving part of the hinge + stl(str("door_hinge_", door_thickness)); + + hole_pitch = width - 10; + + union() { + rotate([90, 0, 0]) + linear_extrude(height = width, center = true) + difference() { + hull() { + translate([dia / 2, thickness + door_thickness / 2]) + intersection() { + rotate(180) + teardrop(r = dia / 2, h = 0, truncate = false); + + square([dia + 1, 2 * thickness + door_thickness], center = true); + } + + square([1, thickness + door_thickness]); + } + translate([dia / 2, thickness + door_thickness / 2]) + teardrop(r = screw_clearance_radius(pin_screw), h = 0); + } + linear_extrude(height = thickness) + difference() { + hull() { + translate([0, -width / 2]) + square([1, width]); + + for(side = [-1, 1]) + translate([-width + rad, side * (width / 2 - rad)]) + circle4n(rad); + } + rotate(180) + vflip() + door_hinge_hole_positions() + poly_circle(screw_clearance_radius(screw)); + } + } +} + +module door_hinge_6_stl() door_hinge(6); + +module door_hinge_stat_hole_positions(dir = 0) { //! Position children over the screws holes of the stationary part + hole_pitch = dia + (stat_length - dia) / 2; + + for(side = [-1, 1]) + translate([side * hole_pitch / 2, dir * (stat_width / 2 + washer_thickness(screw_washer(pin_screw))), thickness]) + children(); +} + +module door_hinge_stat_stl() { //! Generates the STL for the stationary part + stl("door_hinge_stat"); + + union() { + linear_extrude(height = thickness) + difference() { + rounded_square([stat_length, stat_width], rad); + + door_hinge_stat_hole_positions() + poly_circle(screw_clearance_radius(stat_screw)); + } + + rotate([90, 0, 0]) + linear_extrude(height = stat_width, center = true) + difference() { + hull() { + translate([0, dia / 2 + stat_clearance]) + circle(d = dia); + + translate([0, 0.5]) + square([dia, 1], center = true); + } + translate([0, dia / 2 + stat_clearance]) + teardrop(r = screw_clearance_radius(pin_screw), h = 0); + } + } +} + +module door_hinge_assembly(top, door_thickness = 6) { //! The moving assembly that goes on the door + dir = top ? -1 : 1; + pin_x = door_hinge_pin_x(); + pin_y = door_hinge_pin_y(); + washer = screw_washer(screw); + screw_length = screw_shorter_than(thickness + door_thickness + washer_thickness(washer)); + + translate([0, pin_y - (thickness + door_thickness / 2), dir * width / 2]) { + rotate([90, 0, 180]) + color("red") door_hinge(door_thickness); + + rotate([90, 0, 0]) + door_hinge_hole_positions() + screw_and_washer(screw, screw_length); + } + + translate([pin_x, pin_y, top ? 0 : -washer_thickness(screw_washer(pin_screw))]) + washer(screw_washer(pin_screw)); + + translate([pin_x, pin_y, top ? washer_thickness(screw_washer(pin_screw)) + stat_width : width]) + screw_and_washer(pin_screw, screw_longer_than(2 * washer_thickness(screw_washer(pin_screw)) + width + stat_width)); +} + +module door_hinge_static_assembly(top, sheet_thickness = 3) { //! The stationary assembly + dir = top ? -1 : 1; + pin_x = door_hinge_pin_x(); + + stat_washer = screw_washer(stat_screw); + stat_nut = screw_nut(stat_screw); + stat_screw_length = screw_longer_than(thickness + sheet_thickness + 2 * washer_thickness(stat_washer) + nut_thickness(stat_nut, true)); + + translate([pin_x, 0, -dir * (stat_width / 2 + washer_thickness(screw_washer(pin_screw)))]) + rotate([90, 0, 0]) { + color("lime") door_hinge_stat_stl(); + + door_hinge_stat_hole_positions() { + screw_and_washer(stat_screw, stat_screw_length); + translate_z(-thickness - sheet_thickness) + vflip() + nut_and_washer(stat_nut, true); + } + } +} + + +module door_hinge_parts_stl() { //! Generates the STL for both parts of the hinge + translate([2, width / 2 + 1]) + door_hinge_6_stl(); + + translate([0, -stat_width / 2 - 1]) + door_hinge_stat_stl(); +} diff --git a/door_latch.scad b/door_latch.scad new file mode 100644 index 0000000..79e43d4 --- /dev/null +++ b/door_latch.scad @@ -0,0 +1,81 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Door latch for 6mm acrylic door for 3D printer. See [door_hinge](#door_hinge). +// +include +use +include + +length = 35; +width = 12; +height = 14.25; + +thickness = 5; +rad = 3; + +screw = M4_hex_screw; + +function door_latch_screw() = screw; //! The screw used for the axle +function door_latch_offset() = width / 2 + 1; //! Offset of the axle from the door edge + +nut_trap_depth = round_to_layer(screw_head_height(screw)) + 4 * layer_height; + +module door_latch_stl() { //! Generates the STL for the printed part + stl("door_latch"); + + ridge = 4; + difference() { + union() { + hull() { + rounded_rectangle([length, width, thickness - tan(30) * (width - ridge) / 2], rad, center = false); + + translate_z(thickness / 2) + cube([length, ridge, thickness], center = true); + } + + cylinder(d = width, h = height); + } + hanging_hole(nut_trap_depth, screw_clearance_radius(screw)) + circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6); + } +} + +module door_latch_assembly(sheet_thickness = 3) { //! The assembly for a specified sheet thickess + washer = screw_washer(screw); + nut = screw_nut(screw); + + screw_length = screw_longer_than(height - nut_trap_depth + sheet_thickness + 2 * washer_thickness(washer) + nut_thickness(nut, true)); + + translate([0, -height - washer_thickness(washer)]) + rotate([-90, 0, 0]) { + color("lime") render() door_latch_stl(); + + translate_z(nut_trap_depth) + vflip() + screw(screw, screw_length); + + translate_z(height) + washer(washer); + + translate_z(height + sheet_thickness + washer_thickness(washer)) + nut_and_washer(nut, true); + } +} diff --git a/fan_guard.scad b/fan_guard.scad new file mode 100644 index 0000000..7fc10fc --- /dev/null +++ b/fan_guard.scad @@ -0,0 +1,92 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . + +// +//! Pintable fan finger guard to match the specified fan. To be ```include```d, not ```use```d. +//! +//! The ring spacing as well as the number of spokes can be specified, if zero a gasket is generated instead of a guard. +// +use + +function fan_guard_thickness() = 2; //! Default thickness + +function fan_guard_wall() = extrusion_width - layer_height / 2 + nozzle / 2 + extrusion_width / 2; +function fan_guard_corner_r(type) = washer_diameter(screw_washer(fan_screw(type))) / 2 + 0.5; //! Corner radius of the guard +function fan_guard_width(type) = max(2 * (fan_hole_pitch(type) + fan_guard_corner_r(type)), fan_bore(type) + 4 * fan_guard_wall()); //! Width of the guard + +module fan_guard(type, name = false, thickness = fan_guard_thickness(), spokes = 4, finger_width = 7, grill = false) { //! Generate the STL + if(thickness) + stl(name ? name : str("fan_guard_", fan_width(type))); + hole_pitch = fan_hole_pitch(type); + corner_radius = fan_guard_corner_r(type); + wall = grill ? 2 : fan_guard_wall(); + spoke = grill ? 3 : wall; + width = fan_guard_width(type); + + hole = fan_aperture(type) / 2; + max_ring_pitch = finger_width + wall; + inner_ring = max_ring_pitch / 2; + gap = hole + wall / 2 - inner_ring; + rings = ceil(gap / max_ring_pitch); + ring_pitch = gap / rings; + spoke_end = grill && fan_aperture(type) > fan_bore(type) ? hole - ring_pitch : hole; + spoke_start = grill && rings > 1 ? inner_ring + ring_pitch : inner_ring; + rounding = grill ? 1.5 : 0; + + extrude_if(thickness) { + difference() { + offset(-rounding) offset(rounding) { + difference() { + rounded_square([width, width], r = width / 2 - hole_pitch); + + fan_holes(type, !grill, !grill, h = 0); + } + if(spokes) { + intersection() { + union() { + for(i = [(grill ? 1 : 0) : 1 : rings - 1]) { + r = inner_ring + i * ring_pitch; + + ring(or = r + wall / 2, ir = r - wall / 2); + } + for(i = [0 : spokes - 1]) + rotate(i * 360 / spokes + 45) + translate([spoke_start, -spoke / 2]) + square([spoke_end - spoke_start + eps, spoke]); + } + square(width - eps, center = true); + } + } + } + if(grill) + fan_hole_positions(type, z = 0) + drill(screw_clearance_radius(fan_screw(type)), 0); + + } + if(grill) + difference() { + r = min(inner_ring + ring_pitch, fan_hub(type) / 2); + circle(r); + + for(a = [45 : 90 : 360]) + rotate(a) + translate([r / 2, 0]) + circle(wall); + } + } +} diff --git a/fixing_block.scad b/fixing_block.scad new file mode 100644 index 0000000..4f11d44 --- /dev/null +++ b/fixing_block.scad @@ -0,0 +1,170 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Fixing block to mount two sheets at right angles using threaded inserts. +//! Defaults to M3 but other screw sizes can be specified provided they have inserts defined. +//! +//! See [butt_box](#Butt_box) for an example of usage. +//! +//! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +// +include +include +include +use + +def_screw = M3_cap_screw; +wall = 2.5; + +function fixing_block_screw() = def_screw; //! Default screw type +function fixing_block_width(screw = def_screw) = 4 * wall + 3 * insert_outer_d(screw_insert(screw)); //! Width given screw size +function fixing_block_depth(screw = def_screw) = //! Depth given screw size + let(insert = screw_insert(screw)) + max(insert_length(insert) + wall, insert_outer_d(insert) + 2 * wall); + +function fixing_block_height(screw = def_screw) = fixing_block_depth(screw); //! Height given screw size, same as depth + +function fixing_block_h_hole(screw = def_screw) = translate(fixing_block_depth(screw) / 2) * rotate([90, 0, 0]); //! Returns transform to position the horizontal screw +function fixing_block_v_holes(screw = def_screw) = //! Returns a list of transforms to position the vertical screws + let(pitch = 2 * insert_outer_d(screw_insert(screw)) + 2 * wall, offset = fixing_block_depth(screw) / 2) + [for(end = [-1, 1]) translate([end * pitch / 2, offset]) * rotate([180, 0, 0])]; + +function fixing_block_holes(screw) = concat([fixing_block_h_hole(screw)], fixing_block_v_holes(screw)); //! Returns a list of transforms to position all the screws + +module fixing_block_h_hole(screw = def_screw) //! Position children on the horizontal hole + multmatrix(fixing_block_h_hole(screw)) + children(); + +module fixing_block_v_holes(screw = def_screw) //! Position children on the vertical holes + for(p = fixing_block_v_holes(screw)) + multmatrix(p) + children(); + +module fixing_block_holes(screw = def_screw) //! Position children on all the holes + for(p = fixing_block_holes(screw)) + multmatrix(p) + children(); + +module fixing_block_h_hole_2D(screw = def_screw) //! Position 2D child on the horizontal hole + translate([0, fixing_block_depth(screw) / 2]) + children(); + +module fixing_block(screw = def_screw) { //! Generate the STL + stl(str("fixing_block_M", screw_radius(screw) * 20)); + r = 1; + insert = screw_insert(screw); + corner_rad = insert_outer_d(insert) / 2 + wall; + fb_width = fixing_block_width(screw); + fb_height = fixing_block_height(screw); + fb_depth = fixing_block_depth(screw); + + difference() { + union() { + linear_extrude(height = fb_height, convexity = 5) + difference() { + hull() { + for(side = [-1, 1]) { + translate([side * (fb_width / 2 - corner_rad), fb_depth - corner_rad]) + circle4n(corner_rad); + + translate([side * (fb_width / 2 - r), r]) + circle4n(r); + } + } + fixing_block_v_holes(screw) + poly_circle(screw_clearance_radius(screw)); + } + } + translate_z(fb_height) + fixing_block_v_holes(screw) + insert_hole(insert); + + fixing_block_h_hole(screw) + insert_hole(insert, 10, true); + } +} + +module fixing_block_assembly(screw = def_screw) pose([55, 180, 25], [0, 4.8, 4.8]) //! Printed part with the inserts inserted +assembly(str("fixing_block_M", 20 * screw_radius(screw))) { + translate_z(fixing_block_height(screw)) + rotate([0, 180, 0]) + color(pp1_colour) render() fixing_block(screw); + + insert = screw_insert(screw); + + fixing_block_v_holes(screw) + insert(insert); + + fixing_block_h_hole(screw) + insert(insert); +} + +module fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef) { //! Assembly with fasteners in place + module fb_screw(screw, thickness) { + washer = screw_washer(screw); + insert = screw_insert(screw); + screw_length = screw_longer_than(2 * washer_thickness(washer) + thickness + insert_length(insert)); + + translate_z(thickness) + screw_and_washer(screw, screw_length, true); + } + + no_pose() fixing_block_assembly(screw); + + fixing_block_v_holes(screw) + fb_screw(screw, thickness2 ? thickness2 : thickness); + + fixing_block_h_hole(screw) + fb_screw(screw2 ? screw2 : screw, thickness); +} + +module fixing_block_M20_stl() fixing_block(M2_cap_screw); +module fixing_block_M25_stl() fixing_block(M2p5_cap_screw); +module fixing_block_M30_stl() fixing_block(M3_cap_screw); +module fixing_block_M40_stl() fixing_block(M4_cap_screw); + +// +//! 1. Lay the blocks out with the two larger holes facing upwards. +//! 1. Place two M2 inserts into the two vertical holes of each block and push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on their backs and insert a third insert the same way. +// +module fixing_block_M20_assembly() fixing_block_assembly(M2_cap_screw); +// +//! 1. Lay the blocks out with the two larger holes facing upwards. +//! 1. Place two M2.5 inserts into the two vertical holes of each block and push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on their backs and insert a third insert the same way. +// +module fixing_block_M25_assembly() fixing_block_assembly(M2p5_cap_screw); +// +//! 1. Lay the blocks out with the two larger holes facing upwards. +//! 1. Place two M3 inserts into the two vertical holes of each block and push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on their backs and insert a third insert the same way. +// +module fixing_block_M30_assembly() fixing_block_assembly(M3_cap_screw); +// +//! 1. Lay the blocks out with the two larger holes facing upwards. +//! 1. Place two M4 inserts into the two vertical holes of each block and push them home with a soldering iron with a conical bit heated to 200°C. +//! When removing the iron it helps to twist it a little anti-clockwise to release it from the thread. +//! 1. Lay the blocks on their backs and insert a third insert the same way. +// +module fixing_block_M40_assembly() fixing_block_assembly(M4_cap_screw); diff --git a/foot.scad b/foot.scad new file mode 100644 index 0000000..536587d --- /dev/null +++ b/foot.scad @@ -0,0 +1,151 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Customisable printed rubber feet for equipment cases. The insert variant is better for solid feet because +//! inserts don't grip well in rubber. +// +include +include +include + +foot = [25, 12, 3, 2, M4_cap_screw, 10]; +insert_foot = [20, 10, 0, 2, M3_cap_screw, 10]; + +function foot() = foot; //! Default foot used unless a list of parameters is passed +function insert_foot() = insert_foot; //! Default foot with insert + +function foot_diameter(type = foot) = type[0]; //! Outside maximum diameter +function foot_height(type = foot) = type[1]; //! Total height +function foot_thickness(type = foot)= type[2]; //! Thickness under the screw +function foot_rad(type = foot) = type[3]; //! Rounded corner radius +function foot_screw(type = foot) = type[4]; //! Screw type +function foot_slant(type = foot) = type[5]; //! Taper angle + +module foot(type = foot) { //! Generate STL + stl("foot"); + h = foot_height(type); + t = foot_thickness(type); + r1 = washer_radius(screw_washer(foot_screw(type))); + r3 = foot_diameter(type) / 2; + r2 = r3 - h * tan(foot_slant(type)); + r = foot_rad(type); + + union() { + rotate_extrude(convexity = 3) { + hull() { + translate([r1, 0]) + square([r3 - r1, eps]); + + for(x = [r1 + r, r2 - r]) + translate([x, h - r]) + circle4n(r); + } + } + linear_extrude(height = t) + difference() { + circle(r1 + eps); + + poly_circle( screw_clearance_radius(foot_screw(type))); + } + } +} + +module foot_assembly(t = 0, type = foot) { //! Assembly with fasteners in place for specified sheet thickness + screw = foot_screw(type); + washer = screw_washer(screw); + nut = screw_nut(screw); + squeeze = 0.5; + screw_length = screw_longer_than(foot_thickness(type) + t + 2 * washer_thickness(washer) + nut_thickness(nut, true) - squeeze); + + vflip() explode(15, true) { + color(pp4_colour) foot(type); + + if(t) + explode(15, true) + translate_z(foot_thickness(type)) + screw_and_washer(screw, screw_length); + } + if(t) + translate_z(t) + nut_and_washer(nut, true); +} + +module insert_foot(type = insert_foot) { //! Generate STL for foot with insert + stl("insert_foot"); + h = foot_height(type); + r3 = foot_diameter(type) / 2; + r2 = r3 - h * tan(foot_slant(type)); + r = foot_rad(type); + + insert = screw_insert(foot_screw(type)); + h2 = insert_hole_length(insert); + r4 = insert_hole_radius(insert); + r5 = r4 + 1; + union() { + rotate_extrude() { + union() { + hull() { + translate([r5, 0]) { + square([r3 - r5, eps]); + square([eps, h]); + } + + translate([r2 - r, h - r]) + circle4n(r); + } + } + } + linear_extrude(height = h2 + eps) + difference() { + circle(r5 + eps); + + poly_circle(r4); + } + + translate_z(h2) + cylinder(r = r5 + eps, h = h - h2); + } +} +// +//! Place the insert in the bottom of the foot and push home with a soldering iron with a conical bit heated to 200°C. +// +module insert_foot_assembly(type = insert_foot) //! Printed part with insert in place +assembly("insert_foot") { + screw = foot_screw(type); + insert = screw_insert(screw); + + vflip() + color(pp1_colour) insert_foot(type); + + translate_z(-foot_thickness(type)) + insert(insert); +} + +module fastened_insert_foot_assembly(t = 3, type = insert_foot) { //! Assembly with fasteners in place for specified sheet thickness + screw = foot_screw(type); + washer = screw_washer(screw); + insert = screw_insert(screw); + screw_length = screw_shorter_than(insert_length(insert) + t + 2 * washer_thickness(washer)); + + explode(-10) insert_foot_assembly(type); + + translate_z(t) + screw_and_washer(screw, screw_length, true); +} diff --git a/global_defs.scad b/global_defs.scad new file mode 100644 index 0000000..1d015fd --- /dev/null +++ b/global_defs.scad @@ -0,0 +1,90 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// This file included directly or indirectly in every scad file. +// +// This scheme allows the following: +// bom defaults to 0 +// Setting $bom on the command line or in the main file before including lib.scad overrides it everywhere. +// Setting $bom after including lib overrides bom in the libs but not in the local file. +// Setting $_bom in the local file overrides it in the local file but not in the libs. +// +//function is_undef(x) = x == undef; + +$_bom = is_undef($bom) ? 0 : $bom; // 0 no bom, 1 assemblies and stls, 2 vitamins as well +$exploded = is_undef($explode) ? 0 : $explode; // 1 for exploded view +layer_height = is_undef($layer_height) ? 0.25 : $layer_height; // layer heigth when printing +extrusion_width = is_undef($extrusion_width) ? 0.5 : $extrusion_width; // filament width when printing +nozzle = is_undef($nozzle) ? 0.45 : $nozzle; // 3D printer nozzle +cnc_bit_r = is_undef($cnc_bit_r) ? 1.2 : $cnc_bit_r; // miniumum tool radius when milling 2D objects +pp1_colour = is_undef($pp1_colour) ? "lime" : $pp1_colour; // printed part colour 1 +pp2_colour = is_undef($pp2_colour) ? "red" : $pp2_colour; // printed part colour 2 +pp3_colour = is_undef($pp3_colour) ? "blue" : $pp3_colour; // printed part colour 3 +pp4_colour = is_undef($pp4_colour) ? "darkorange" : $pp4_colour;// printed part colour 4 +show_rays = is_undef($show_rays) ? false : $show_rays; // show camera sight lines and light direction + +// Minimum wall is about two filaments wide but we extrude it closer to get better bonding +squeezed_wall = $preview ? 2 * extrusion_width - layer_height * (1 - PI / 4) + : extrusion_width - layer_height / 2 + nozzle / 2 + extrusion_width / 2; + +inf = 1e10; // very big +eps = 1/128; // small fudge factor to stop CSG barfing on coincident faces. +$fa = 6; +$fs = extrusion_width / 2; + +function round_to_layer(z) = ceil(z / layer_height) * layer_height; +// Some additional named colors +grey20 = [0.2, 0.2, 0.2]; +grey30 = [0.3, 0.3, 0.3]; +grey40 = [0.4, 0.4, 0.4]; +grey50 = [0.5, 0.5, 0.5]; +grey60 = [0.6, 0.6, 0.6]; +grey70 = [0.7, 0.7, 0.7]; +grey80 = [0.8, 0.8, 0.8]; +grey90 = [0.9, 0.9, 0.9]; +brass = "gold"; + +/* + * Enums + */ +// +// Screws +// +hs_cap = 0; +hs_pan = 1; +hs_cs = 2; // counter sunk +hs_hex = 3; +hs_grub = 4; // pulley set screw +hs_cs_cap = 5; +hs_dome = 6; +// +// Hot end descriptions +// +jhead = 1; +e3d = 2; +// +// Face enumeration +// +f_bottom = 0; +f_top = 1; +f_left = 2; +f_right = 3; +f_front = 4; +f_back = 5; diff --git a/handle.scad b/handle.scad new file mode 100644 index 0000000..aa295d2 --- /dev/null +++ b/handle.scad @@ -0,0 +1,103 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Printed handle that can be printed without needing support material due to its truncated teardrop profile. +// +include +include +include + +dia = 18; +length = 90; // inside length +height = 30; // inside height +screw = M4_cap_screw; +insert = screw_insert(screw); + +pitch = length + dia; + +function handle_length() = pitch + dia; //! Outside length +function handle_width() = dia; //! The width, i.e. diameter +function handle_height() = height + dia; //! Total height +function handle_screw() = screw; //! The screw type + +module handle_screw_positions() //! Position children at the screw positions + for(end = [-1, 1]) + translate([end * pitch / 2, 0]) + children(); + +module handle_holes(h = 100) //! Drills holes for the screws + handle_screw_positions() + drill(screw_clearance_radius(screw), h); + +module handle_stl() { //! generate the STL + stl("handle"); + + module end(end) + translate([end * pitch / 2, 0]) + rotate_extrude() + intersection() { + rotate(180) + teardrop(r = dia / 2, h = 0); + + translate([0, - (dia + 1) / 2]) + square([dia / 2 + 1, dia + 1]); + } + + translate_z(dia / 2) + union() { + hull() { + end(-1); + + end(1); + } + + handle_screw_positions() + render() difference() { + h = height + dia / 2; + cylinder(d = dia, h = h); + + translate_z(h) + insert_hole(insert, 6); + } + } +} +// +//! Place inserts in the bottom of the posts and push them home with a soldering iron with a conical bit heated to 200°C. +// +module handle_assembly() pose([225, 0, 150], [0, 0, 14]) //! Printed part with inserts in place +assembly("handle") { + translate_z(handle_height()) + color(pp1_colour) vflip() handle_stl(); + + handle_screw_positions() + vflip() + insert(insert); +} + +module handle_fastened_assembly(thickness) { //! Assembly with fasteners in place + screw_length = screw_longer_than(thickness + insert_length(insert) + 2 * washer_thickness(screw_washer(screw))); + + handle_assembly(); + + handle_screw_positions() + vflip() + translate_z(thickness) + screw_and_washer(screw, screw_length, true); +} diff --git a/lib.scad b/lib.scad new file mode 100644 index 0000000..05f6efc --- /dev/null +++ b/lib.scad @@ -0,0 +1,89 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Include this file to use the library +// +include + +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include +include + +use +use +use + +use +use +use +use +use +use +use + +use +use +use +use +use +use +use +use +use +use +use +use +use +use diff --git a/libtest.scad b/libtest.scad new file mode 100644 index 0000000..436e15b --- /dev/null +++ b/libtest.scad @@ -0,0 +1,339 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// This file shows all the parts in the library. +// +include + +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use + +use +use +use +use +use +use +use +use +use +use +use +use +use +use +use + +x5 = 800; + +cable_grommets_y = 0; + +translate([x5, cable_grommets_y]) + cable_grommets(); + +translate([x5, cable_grommets_y + 50]) + feet(); + +translate([x5, cable_grommets_y + 75]) + fixing_blocks(); + +translate([x5, cable_grommets_y + 100]) + corner_blocks(); + +translate([x5, cable_grommets_y + 150]) + ribbon_clamps(); + +translate([x5 + 70, cable_grommets_y + 150]) + screw_knobs(); + +translate([x5, cable_grommets_y + 470]) { + door_hinges() + door_latches(); +} + +translate([x5, cable_grommets_y + 370]) + no_explode() socket_boxes(); + +translate([x5 + 60, cable_grommets_y + 200]) + strap_handles(); + +translate([x5, cable_grommets_y + 250]) + handle(); + +translate([900, 600]) + box_test(); + +translate([850, 1170]) + bbox_test(); + +x0 = 0; +inserts_y = 0; +nuts_y = inserts_y + 20; +washers_y = nuts_y + 60; +screws_y = washers_y + 120; +o_rings_y = screws_y + 130; +springs_y = o_rings_y + 20; +sealing_strip_y = springs_y + 20; +tubings_y = sealing_strip_y + 20; +pillars_y = tubings_y + 20; +leadnuts_y = pillars_y + 40; +pulleys_y = leadnuts_y +40; +hot_ends_y = pulleys_y + 60; +linear_bearings_y = hot_ends_y + 50; +sheets_y = linear_bearings_y + 50; +pcbs_y = sheets_y + 40; +displays_y = pcbs_y + 150; +fans_y = displays_y + 100; +transformers_y = fans_y + 120; +psus_y = transformers_y + 190; + +translate([x0 + 20, inserts_y]) + inserts(); + +translate([x0, inserts_y]) + ring_terminals(); + +translate([x0, nuts_y]) + nuts(); + +translate([x0, washers_y]) + washers(); + +translate([x0, screws_y]) + screws(); + +translate([x0, o_rings_y]) + o_rings(); + +translate([x0, springs_y]) + springs(); + +translate([x0 + 50, sealing_strip_y]) + sealing_strip_test(); + +translate([x0, tubings_y]) + tubings(); + +translate([x0, pillars_y]) + pillars(); + +translate([x0, leadnuts_y ]) + leadnuts(); + +translate([x0 + 80, leadnuts_y]) + ball_bearings(); + +translate([x0, pulleys_y]) + pulleys(); + +translate([x0, linear_bearings_y]) { + linear_bearings(); + rods(); +} + +translate([x0 + 10, hot_ends_y]) + hot_ends(); + +translate([x0, sheets_y]) + sheets(); + +translate([x0, pcbs_y]) + pcbs(); + +translate([x0, displays_y]) + displays(); + +translate([x0, fans_y]) { + fans(); + + translate_z(3) + fan_guards(); +} + +translate([x0, transformers_y]) + variacs(); + +translate([x0, psus_y]) + psus(); + + +x1 = x0 + 100; +zipties_y = 0; +bulldogs_y = zipties_y + 40; + +translate([x1, zipties_y]) + zipties(); + +translate([x1, bulldogs_y]) + bulldogs(); + +x2 = x1 + 90; +leds_y = 0; +carriers_y = leds_y + 40; +spades_y = carriers_y + 40; +buttons_y = spades_y + 40; +jacks_y = buttons_y + 40; +microswitches_y = jacks_y + 40; +rockers_y = microswitches_y + 40; +toggles_y = rockers_y + 40; +components_y = toggles_y + 40; + +translate([x2, leds_y]) + leds(); + +translate([x2 + 8, carriers_y]) + carriers(); + +translate([x2+ 38, carriers_y]) + meters(); + +translate([x2 + 68, carriers_y]) + fuseholders(); + +translate([x2, spades_y]) + spades(); + +translate([x2, buttons_y]) + buttons(); + +translate([x2, jacks_y]) + jacks(); + +translate([x2, microswitches_y]) + microswitches(); + +translate([x2, rockers_y]) + rockers(); + +translate([x2, toggles_y]) + toggles(); + +translate([x2, components_y]) + components(); + + +x3 = x2 + 150; +veroboard_y = 0; +d_connectors_y = veroboard_y + 110; +iecs_y = d_connectors_y + 80; +modules_y = iecs_y + 60; +ssrs_y = modules_y + 80; +blowers_y = ssrs_y + 60; +batteries_y = blowers_y + 100; +steppers_y = batteries_y + 70; + +translate([x3, veroboard_y]) + veroboard_test(); + +translate([x3, d_connectors_y]) + d_connectors(); + +translate([x3, iecs_y]) + iecs(); + +translate([x3 + 15, modules_y]) + microview(); + +translate([x3 + 40, modules_y]) + modules(); + +translate([x3, ssrs_y]) + ssrs(); + +translate([x3, blowers_y]) + blowers(); + +translate([x3, batteries_y]) + batteries(); + +translate([x2, steppers_y]) // interloper + stepper_motors(); + +translate([x3, transformers_y]) + transformers(); + + +x4 = x3 + 220; +belts_y = 0; +rails_y = belts_y + 200; +cable_strips_y = rails_y + 300; + +translate([x4 + 112, belts_y + 58]) { + belt_test(); + + translate([0, 60]) + opengrab_test(); +} + +translate([x4, rails_y + 130]) + rails(); + +translate([x4, cable_strips_y]) + cable_strips(); + +x6 = x5 + 150; +translate([x6, 125]) + light_strips(); diff --git a/ribbon_clamp.scad b/ribbon_clamp.scad new file mode 100644 index 0000000..e8aa642 --- /dev/null +++ b/ribbon_clamp.scad @@ -0,0 +1,119 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Clamp for ribbon cable and polypropylene strip. +// +include +include +include +use + +wall = 2; +min_wall = 2 * extrusion_width; +screw = M3_cap_screw; +insert = screw_insert(screw); +screw_depth = insert_length(insert) + 1; + +function ribbon_clamp_hole_pitch(ways) = ribbon_clamp_slot(ways) + 2 * min_wall + 2 * corrected_radius(insert_hole_radius(insert)); //! Hole pitch +function ribbon_clamp_width() = 2 * (insert_hole_radius(insert) + 2); //! Width +function ribbon_clamp_length(ways) = ribbon_clamp_hole_pitch(ways) + ribbon_clamp_width(); //! Length given ways +function ribbon_clamp_height() = screw_depth + 1; //! Height + +module ribbon_clamp_hole_positions(ways, side = undef) //! Place children at hole positions + for(x = is_undef(side) ? [-1, 1] : side) + translate([x * ribbon_clamp_hole_pitch(ways) / 2, 0]) + children(); + +module ribbon_clamp_holes(ways, h = 20) //! Drill screw holes + ribbon_clamp_hole_positions(ways) + drill(screw_clearance_radius(screw), h); + +module ribbon_clamp(ways) { //! Generate STL for given number of ways + stl(str("ribbon_clamp_", ways)); + + pitch = ribbon_clamp_hole_pitch(ways); + d = ribbon_clamp_width(); + h = ribbon_clamp_height(); + t = h - ribbon_clamp_slot_depth() - wall; + + difference() { + union() { + hull() { + translate_z(h - t / 2) + cube([ribbon_clamp_hole_pitch(ways), d, t], center = true); + + translate_z(1) + cube([pitch, max(wall, d - 2 * (h - t)), 2], center = true); + } + ribbon_clamp_hole_positions(ways, -1) + cylinder(d = d, h = h); + + ribbon_clamp_hole_positions(ways, 1) + cylinder(d = d, h = h); + + } + + translate_z(h) + cube([ribbon_clamp_slot(ways), d + 1, ribbon_clamp_slot_depth() * 2], center = true); + + ribbon_clamp_hole_positions(ways) + translate_z(h) + rotate(22.5) + insert_hole(insert, screw_depth - insert_length(insert)); + } +} + +module ribbon_clamp_assembly(ways) pose([55, 180, 25]) //! Printed part with inserts in place + assembly(str("ribbon_clamp_", ways)) { + h = ribbon_clamp_height(); + + color(pp1_colour) render() + translate_z(h) vflip() ribbon_clamp(ways); + + ribbon_clamp_hole_positions(ways) + vflip() + insert(insert); +} + +module ribbon_clamp_fastened_assembly(ways, thickness, screw = screw) { //! Clamp with fasteners in place + tape_l = floor(ribbon_clamp_slot(ways)); + tape_width = 25; + tape_thickness = 0.5; + + vitamin(str(": Tape self amalgamating silicone ",tape_l," x 25mm")); + + washer = screw_washer(screw); + screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + screw_depth); + + ribbon_clamp_assembly(ways); + + color("red") translate_z(tape_thickness / 2) + cube([tape_l, tape_width, tape_thickness], center = true); + + ribbon_clamp_hole_positions(ways) + vflip() + translate_z(thickness) + screw_and_washer(screw, screw_length, true); +} + +module ribbon_clamp_20_stl() ribbon_clamp(20); + +//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C. +module ribbon_clamp_20_assembly() ribbon_clamp_assembly(20); diff --git a/screw_knob.scad b/screw_knob.scad new file mode 100644 index 0000000..d140eb5 --- /dev/null +++ b/screw_knob.scad @@ -0,0 +1,77 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Knob with embedded hex head screw. +// +include +include +use + +knob_wall = 2; +function knob_nut_trap_depth(screw) = round_to_layer(screw_head_height(screw)); +knob_stem_h = 6; +knob_thickness = 4; +knob_r = 8; +knob_wave = 1; +knob_waves = 9; +knob_height = knob_stem_h + knob_thickness; +function knob_height() = knob_height; + +module screw_knob(screw) { //! Generate the STL foe a knob to fit the specified hex screw + stl(str("screw_knob_M", screw_radius(screw) * 20)); + + knob_stem_r = nut_trap_radius(screw_nut(screw)) + knob_wall; + + function wave(a) = knob_r + sin(a * knob_waves) * knob_wave; + + union() { + render() difference() { + cylinder(r = knob_stem_r, h = knob_thickness + knob_stem_h); + + hanging_hole(knob_nut_trap_depth(screw), screw_clearance_radius(screw)) + rotate(45) + circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6); + } + linear_extrude(height = knob_thickness, convexity = 3) + difference() { + polygon(points = [for(a = [0 : 359]) [wave(a) * sin(a), wave(a) * cos(a)]]); + + circle(knob_stem_r - eps); + } + } +} + +//! Place the screw through the printed part +module screw_knob_assembly(screw, length) //! Assembly with the screw in place +assembly(str("screw_knob_M", 20 * screw_radius(screw), "_", length)) { + translate_z(knob_height) + vflip() + color(pp1_colour) screw_knob(screw); + + translate_z(knob_height - knob_nut_trap_depth(screw)) + rotate(-45) + screw(screw, length); +} + +module screw_knob_M30_stl() screw_knob(M3_hex_screw); +module screw_knob_M40_stl() screw_knob(M4_hex_screw); + +module screw_knob_M30_16_assembly() screw_knob_assembly(M3_hex_screw, 16); +module screw_knob_M40_16_assembly() screw_knob_assembly(M4_hex_screw, 16); diff --git a/scripts/blurb.py b/scripts/blurb.py new file mode 100644 index 0000000..73c8787 --- /dev/null +++ b/scripts/blurb.py @@ -0,0 +1,80 @@ +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# +""" +Capture Markup lines in OpenSCAD source code denoted by '//!'. +""" +import re + +def parse_line(line): + """ process a line, add blurb to text and return true if got to a module or function """ + if line[:3] == '//!': + line = line.replace('~\n', ' \n') + start = 4 if line[3] == ' ' else 3 + return False, line[start :] + else: + words = line.split() + return len(words) and (words[0] == "module" or words[0] == "function"), "" + +def _scrape_blurb(lines): + """ Find Markup lines before the first function or module given a list of lines.""" + text = "" + for line in lines: + b, t = parse_line(line) + if b: + break + text += t + if len(text): + text += '\n' + return text + +def scrape_blurb(scad_file): + """ Find Markup lines before the first function or module.""" + with open(scad_file, "rt") as file: + lines = file.readlines() + return _scrape_blurb(lines) + +def scrape_module_blurb(lines): + """ Find the Markup lines before the last function or module. """ + text = "" + for line in lines: + b, t = parse_line(line) + text = "" if b else text + t + return text + +def scrape_code(scad_file): + """ Find the Markup lines on the first line of functions and modules. """ + with open(scad_file, "rt") as file: + lines = file.readlines() + blurb = _scrape_blurb(lines) + properties = {} + functions = {} + modules = {} + for line in lines: + match = re.match(r'^function (.*\(type\)|.*\(type ?= ?.*?\)) *= *type\[.*\].*?(?://! ?(.*))?$', line) + if match: + properties[match.group(1)] = match.group(2) + else: + match = re.match(r'^function (.*?\(.*?\)).*?(?://! ?(.*))$', line) + if match: + functions[match.group(1)] = match.group(2) + match = re.match(r'^module (.*?\(.*?\)).*?(?://! ?(.*))$', line) + if match: + modules[match.group(1)] = match.group(2) + + return { "blurb" : blurb, "properties" : properties, "functions" : functions, "modules": modules} diff --git a/scripts/bom.py b/scripts/bom.py new file mode 100644 index 0000000..1b79b36 --- /dev/null +++ b/scripts/bom.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function + +import os +import sys +import shutil +import openscad +from time import * +from set_config import * +import json + +def find_scad_file(mname): + for filename in os.listdir(source_dir): + if filename[-5:] == ".scad": + # + # look for module which makes the assembly + # + with open(source_dir + "/" + filename, "r") as f: + for line in f.readlines(): + words = line.split() + if len(words) and words[0] == "module": + module = words[1].split('(')[0] + if module == mname: + return filename + return None + +class BOM: + def __init__(self, name): + self.name = name + self.count = 1 + self.vitamins = {} + self.printed = {} + self.routed = {} + self.assemblies = {} + + def data(self, main): + return { + "name" : self.name, + "count" : self.count, + "assemblies" : [main.assemblies[ass].data(main) for ass in self.assemblies], + "vitamins" : self.vitamins, + "printed" : self.printed, + "routed" : self.routed + } + + def flat_data(self): + assemblies = {} + for ass in self.assemblies: + assemblies[ass] = self.assemblies[ass].count + return { + "assemblies" : assemblies, + "vitamins" : self.vitamins, + "printed" : self.printed, + "routed" : self.routed + } + + def add_part(self, s): + if s[-4:] == ".stl": + parts = self.printed + else: + if s[-4:] == ".dxf": + parts = self.routed + else: + parts = self.vitamins + if s in parts: + parts[s] += 1 + else: + parts[s] = 1 + + def add_assembly(self, ass): + if ass in self.assemblies: + self.assemblies[ass].count += 1 + else: + self.assemblies[ass] = BOM(ass) + + def make_name(self, ass): + if self.count == 1: + return ass + return ass.replace("assembly", "assemblies") + + def print_bom(self, breakdown, file = None): + if self.vitamins: + print("Vitamins:", file=file) + if breakdown: + longest = 0 + for ass in self.assemblies: + name = ass.replace("_assembly","") + longest = max(longest, len(name)) + for i in range(longest): + line = "" + for ass in sorted(self.assemblies): + name = ass.replace("_assembly","").replace("_"," ").capitalize() + index = i - (longest - len(name)) + if index < 0: + line += " " + else: + line += (" %s " % name[index]) + print(line[:-1], file=file) + + for part in sorted(self.vitamins): + if ': ' in part: + part_no, description = part.split(': ') + else: + part_no, description = "", part + if breakdown: + for ass in sorted(self.assemblies): + bom = self.assemblies[ass] + if part in bom.vitamins: + file.write("%2d|" % bom.vitamins[part]) + else: + file.write(" |") + print("%3d" % self.vitamins[part], description, file=file) + + if self.printed: + if self.vitamins: + print(file=file) + print("Printed:", file=file) + for part in sorted(self.printed): + if breakdown: + for ass in sorted(self.assemblies): + bom = self.assemblies[ass] + if part in bom.printed: + file.write("%2d|" % bom.printed[part]) + else: + file.write(" |") + print("%3d" % self.printed[part], part, file=file) + + if self.routed: + print(file=file) + print("CNC cut:", file=file) + for part in sorted(self.routed): + if breakdown: + for ass in sorted(self.assemblies): + bom = self.assemblies[ass] + if part in bom.routed: + file.write("%2d|" % bom.routed[part]) + else: + file.write(" |") + print("%3d" % self.routed[part], part, file=file) + + if self.assemblies: + print(file=file) + print("Assemblies:", file=file) + for ass in sorted(self.assemblies): + print("%3d %s" % (self.assemblies[ass].count, self.assemblies[ass].make_name(ass)), file=file) + +def parse_bom(file = "openscad.log", name = None): + main = BOM(name) + stack = [] + + for line in open(file): + pos = line.find('ECHO: "~') + if pos > -1: + s = line[pos + 8 : line.rfind('"')] + if s[-1] == '{': + ass = s[:-1] + if stack: + main.assemblies[stack[-1]].add_assembly(ass) #add to nested BOM + stack.append(ass) + main.add_assembly(ass) #add to flat BOM + else: + if s[0] == '}': + if s[1:] != stack[-1]: + raise Exception("Mismatched assembly " + s[1:] + str(stack)) + stack.pop() + else: + main.add_part(s) + if stack: + main.assemblies[stack[-1]].add_part(s) + return main + +def boms(target = None, assembly = None): + bom_dir = set_config(target) + "bom" + if assembly: + bom_dir += "/accessories" + if not os.path.isdir(bom_dir): + os.makedirs(bom_dir) + else: + assembly = "main_assembly" + if os.path.isdir(bom_dir): + shutil.rmtree(bom_dir) + sleep(0.1) + os.makedirs(bom_dir) + # + # Find the scad file that makes the module + # + scad_file = find_scad_file(assembly) + if not scad_file: + raise Exception("can't find source for " + assembly) + # + # make a file to use the module + # + bom_maker_name = source_dir + "/bom.scad" + with open(bom_maker_name, "w") as f: + f.write("use <%s>\n" % scad_file) + f.write("%s();\n" % assembly); + # + # Run openscad + # + openscad.run("-D","$bom=2","-D","$preview=true","-o", "openscad.echo", bom_maker_name) + os.remove(bom_maker_name) + print("Generating bom ...", end=" ") + + main = parse_bom("openscad.echo", assembly) + + if assembly == "main_assembly": + main.print_bom(True, open(bom_dir + "/bom.txt","wt")) + + for ass in sorted(main.assemblies): + with open(bom_dir + "/" + ass + ".txt", "wt") as f: + bom = main.assemblies[ass] + print(bom.make_name(ass) + ":", file=f) + bom.print_bom(False, f) + + with open(bom_dir + "/bom.json", 'w') as outfile: + json.dump(main.assemblies[assembly].data(main), outfile, indent = 4) + + print("done") + +if __name__ == '__main__': + args = len(sys.argv) + if args > 1: + if args > 2: + boms(sys.argv[1], sys.argv[2]) + else: + boms(sys.argv[1]) + else: + boms(); diff --git a/scripts/c14n_stl.py b/scripts/c14n_stl.py new file mode 100644 index 0000000..89cb8ff --- /dev/null +++ b/scripts/c14n_stl.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# +# OpenSCAD produces randomly ordered STL files so source control like GIT can't tell if they have changed or not. +# This scrip orders each triangle to start with the lowest vertex first (comparing x, then y, then z) +# It then sorts the triangles to start with the one with the lowest vertices first (comparing first vertex, second, then third) +# This has no effect on the model but makes the STL consistent. I.e. it makes a canonical form. +# + +from __future__ import print_function + +import sys + +def cmz(x): + ''' Convert "-0" to "0". ''' + return '0' if x == '-0' else x + +class Vertex: + def __init__(self, x, y, z): + self.x, self.y, self.z = x, y, z + self.key = (float(x), float(y), float(z)) + +class Normal: + def __init__(self, dx, dy, dz): + self.dx, self.dy, self.dz = dx, dy, dz + +class Facet: + def __init__(self, normal, v1, v2, v3): + self.normal = normal + if v1.key < v2.key: + if v1.key < v3.key: + self.vertices = (v1, v2, v3) #v1 is the smallest + else: + self.vertices = (v3, v1, v2) #v3 is the smallest + else: + if v2.key < v3.key: + self.vertices = (v2, v3, v1) #v2 is the smallest + else: + self.vertices = (v3, v1, v2) #v3 is the smallest + + def key(self): + return (self.vertices[0].x, self.vertices[0].y, self.vertices[0].z, + self.vertices[1].x, self.vertices[1].y, self.vertices[1].z, + self.vertices[2].x, self.vertices[2].y, self.vertices[2].z) + +class STL: + def __init__(self, fname): + self.facets = [] + + with open(fname) as f: + words = [cmz(s.strip()) for s in f.read().split()] + + if words[0] == 'solid' and words[1] == 'OpenSCAD_Model': + i = 2 + while words[i] == 'facet': + norm = Normal(words[i + 2], words[i + 3], words[i + 4]) + v1 = Vertex(words[i + 8], words[i + 9], words[i + 10]) + v2 = Vertex(words[i + 12], words[i + 13], words[i + 14]) + v3 = Vertex(words[i + 16], words[i + 17], words[i + 18]) + i += 21 + self.facets.append(Facet(norm, v1, v2, v3)) + + self.facets.sort(key = Facet.key) + else: + print("Not an OpenSCAD ascii STL file") + sys.exit(1) + + def write(self, fname): + with open(fname,"wt") as f: + print('solid OpenSCAD_Model', file=f) + for facet in self.facets: + print(' facet normal %s %s %s' % (facet.normal.dx, facet.normal.dy, facet.normal.dz), file=f) + print(' outer loop', file=f) + for vertex in facet.vertices: + print(' vertex %s %s %s' % (vertex.x, vertex.y, vertex.z), file=f) + print(' endloop', file=f) + print(' endfacet', file=f) + print('endsolid OpenSCAD_Model', file=f) + +def canonicalise(fname): + stl = STL(fname) + stl.write(fname) + +if __name__ == '__main__': + if len(sys.argv) == 2: + canonicalise(sys.argv[1]) + else: + print("usage: c14n_stl file") + sys.exit(1) diff --git a/scripts/deps.py b/scripts/deps.py new file mode 100644 index 0000000..cd0ec69 --- /dev/null +++ b/scripts/deps.py @@ -0,0 +1,49 @@ +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# +import os + +def mtime(file): + if os.path.isfile(file): + return os.path.getmtime(file) + return 0 + +def deps_name(dir, scad_name): + return dir + '/' + scad_name[:-5] + '.deps' + +def read_deps(dname): + with open(dname, "rt") as file: + lines = file.readlines() + deps = [] + for line in lines: + if line.startswith('\t'): + dep = line[1 : -1].rstrip(' \\') + if not dep in ['stl.scad', 'dxf.scad', 'svf.scad', 'png.scad']: + deps.append(dep) + return deps + +def check_deps(target_mtime, dname): + if not target_mtime: + return "target missing" + if not os.path.isfile(dname): + return "no deps" + deps = read_deps(dname) + for dep in deps: + if mtime(dep) > target_mtime: + return dep + ' changed' + return None diff --git a/scripts/dxfs.py b/scripts/dxfs.py new file mode 100644 index 0000000..cac98d2 --- /dev/null +++ b/scripts/dxfs.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +import sys + +from exports import make_parts + +if __name__ == '__main__': + if len(sys.argv) > 1 and not '.' in sys.argv[1]: + target, parts = sys.argv[1], sys.argv[2:] + else: + target, parts = None, sys.argv[1:] + make_parts(target, 'dxf', parts) diff --git a/scripts/exports.py b/scripts/exports.py new file mode 100644 index 0000000..ad66134 --- /dev/null +++ b/scripts/exports.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +import os +import openscad +import sys +import c14n_stl +from set_config import * +import time +import times +from deps import * + +def bom_to_parts(target_dir, part_type, assembly = None): + # + # Make a list of all the parts in the BOM + # + part_files = [] + bom = assembly + '.txt' if assembly else "bom.txt" + suffix = ".dxf" if part_type == 'svg' else '.' + part_type + with open(target_dir + "/../bom/" + bom, "rt") as f: + for line in f.readlines(): + words = line.split() + if words: + last_word = words[-1] + if last_word.endswith(suffix): + part_files.append(last_word[:-4] + '.' + part_type) + return part_files + +def make_parts(target, part_type, parts = None): + # + # Make the target directory + # + top_dir = set_config(target) + target_dir = top_dir + part_type + 's' + deps_dir = top_dir + "deps" + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + if not os.path.isdir(deps_dir): + os.makedirs(deps_dir) + times.read_times(target_dir) + # + # Decide which files to make + # + if parts: + targets = list(parts) #copy the list so we dont modify the list passed in + else: + targets = bom_to_parts(target_dir, part_type) + for file in os.listdir(target_dir): + if file.endswith('.' + part_type): + if not file in targets: + print("Removing %s" % file) + os.remove(target_dir + '/' + file) + # + # Find all the scad files + # + lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib' + used = [] + module_suffix = '_dxf' if part_type == 'svg' else '_' + part_type + for dir in [source_dir, lib_dir]: + for filename in os.listdir(dir): + if filename[-5:] == ".scad": + # + # find any modules ending in _ + # + with open(dir + "/" + filename, "r") as f: + for line in f.readlines(): + words = line.split() + if(len(words) and words[0] == "module"): + module = words[1].split('(')[0] + if module.endswith(module_suffix): + base_name = module[:-4] + part = base_name + '.' + part_type + if part in targets: + # + # make a file to use the module + # + part_maker_name = part_type + ".scad" + with open(part_maker_name, "w") as f: + f.write("use <%s/%s>\n" % (dir, filename)) + f.write("%s();\n" % module); + # + # Run openscad on the created file + # + part_file = target_dir + "/" + part + dname = deps_name(deps_dir, filename) + changed = check_deps(mtime(part_file), dname) + changed = times.check_have_time(changed, part) + if changed: + print(changed) + t = time.time() + openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name) + times.add_time(part, t) + if part_type == 'stl': + c14n_stl.canonicalise(part_file) + targets.remove(part) + os.remove(part_maker_name) + # + # Add the files on the BOM to the used list for plates.py + # + for line in open("openscad.log"): + if line[:7] == 'ECHO: "' and line[-6:] == '.' + part_type + '"\n': + used.append(line[7:-2]) + # + # List the ones we didn't find + # + if targets: + for part in targets: + if part[-4:] != '.' + part_type: + print(part, "is not a", part_type, "file") + else: + print("Could not find a module called", part[:-4] + module_suffix, "to make", part) + sys.exit(1) + times.print_times() + return used diff --git a/scripts/gallery.py b/scripts/gallery.py new file mode 100644 index 0000000..4713847 --- /dev/null +++ b/scripts/gallery.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# +# Find projects and add them to the gallery +# +from __future__ import print_function +import os +from colorama import Fore, init +from tests import do_cmd +import re +from shutil import copyfile + +project_dir = '../..' +target_dir = 'gallery' +output_name = target_dir + '/readme.md' + +def gallery(): + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + + projects = [i for i in os.listdir(project_dir) if os.path.isdir(project_dir + '/' + i + '/assemblies')] + with open(output_name, 'wt') as output_file: + for project in projects: + path = project_dir + '/' + project + print(project) + document = path + '/readme.md' + if os.path.isfile(document): + with open(document, 'rt') as readme: + for line in readme.readlines(): + match = re.match(r"^.*!(\[.*\]\(.*\)).*$", line) + if match: + image = match.group(0) + if image.startswith('![Main Assembly](assemblies/'): + file = image[17 : -1] + line = line.replace(image, '![](%s.png)' % project) + copyfile(path + '/' + file, '%s/%s.png' %(target_dir, project)) + else: + line = line.replace(image, '') + print(line[:-1], file = output_file) + if line == '---\n': + break; + else: + print(Fore.MAGENTA + "Can't find", document, Fore.WHITE); + with open(target_dir + "/readme.html", "wt") as html_file: + do_cmd(("python -m markdown -x tables " + output_name).split(), html_file) + +if __name__ == '__main__': + init() + gallery() diff --git a/scripts/make_all.py b/scripts/make_all.py new file mode 100644 index 0000000..bb6c4b1 --- /dev/null +++ b/scripts/make_all.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +import sys + +from exports import make_parts +from bom import boms +from render import render +from views import views + +if __name__ == '__main__': + target = None if len(sys.argv) == 1 else sys.argv[1] + boms(target) + for part in ['stl', 'dxf']: + make_parts(target, part) + render(target, part) + views(target) diff --git a/scripts/openscad.py b/scripts/openscad.py new file mode 100644 index 0000000..b33e547 --- /dev/null +++ b/scripts/openscad.py @@ -0,0 +1,38 @@ +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# +# Run openscad +# +from __future__ import print_function + +import subprocess, sys + +def run(*args): + cmd = ["openscad"] + list(args) + for arg in cmd: + print(arg, end=" ") + print() + with open("openscad.log", "w") as log: + rc = subprocess.call(cmd, stdout = log, stderr = log) + for line in open("openscad.log", "rt"): + if 'ERROR:' in line or 'WARNING:' in line: + print(line[:-1]) + if rc: + sys.exit(rc) diff --git a/scripts/render.py b/scripts/render.py new file mode 100644 index 0000000..6b065d1 --- /dev/null +++ b/scripts/render.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +from set_config import * +from exports import bom_to_parts +import os +import openscad +from tests import do_cmd +from deps import mtime + +def render(target, type): + # + # Make the target directory + # + target_dir = set_config(target) + type + 's' + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + # + # Find all the parts + # + parts = bom_to_parts(target_dir, type) + # + # Remove unused png files + # + for file in os.listdir(target_dir): + if file.endswith('.png'): + if not file[:-4] + '.' + type in parts: + print("Removing %s" % file) + os.remove(target_dir + '/' + file) + + for part in parts: + part_file = target_dir + '/' + part + png_name = target_dir + '/' + part[:-4] + '.png' + # + # make a file to import the stl + # + if mtime(part_file) > mtime(png_name): + png_maker_name = "png.scad" + with open(png_maker_name, "w") as f: + f.write('color("lime") import("%s");\n' % part_file) + 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' else "--render" + openscad.run("--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall", "-o", png_name, png_maker_name); + do_cmd(("magick "+ png_name + " -trim -resize 280x280 -background #ffffe5 -gravity Center -extent 280x280 -bordercolor #ffffe5 -border 10 " + png_name).split()) + os.remove(png_maker_name) + +if __name__ == '__main__': + target = sys.argv[1] if len(sys.argv) > 1 else None + render(target, 'stl') + render(target, 'dxf') diff --git a/scripts/set_config.py b/scripts/set_config.py new file mode 100644 index 0000000..69a6af9 --- /dev/null +++ b/scripts/set_config.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# +# Set target configuration for multi-target projects that have variable configurations. +# +from __future__ import print_function + +source_dir = 'scad' + +import sys +import os + +def valid_targets(): + return [i[7:-5] for i in os.listdir(source_dir) if i[0:7] == "config_" and i[-5:] == ".scad"] + +def valid_targets_string(): + result = '' + targets = valid_targets() + for t in targets: + if result: + if t == targets[-1]: + result += ' and ' + else: + result += ', ' + result += t + return result + + +def set_config(target): + targets = valid_targets() + if not target: + if not targets: + return "" + print("Must specify a configuration: " + valid_targets_string()) + sys.exit(1) + + if not targets: + print("Not a muli-configuration project (no config_.scad files found)") + sys.exit(1) + + if not target in targets: + print(target + " is not a configuration, avaliable configurations are: " + valid_targets_string()) + sys.exit(1) + + fname = source_dir + "/target.scad" + text = "include \n" % target; + line = "" + try: + with open(fname,"rt") as f: + line = f.read() + except: + pass + + if line != text: + with open(fname,"wt") as f: + f. write(text); + return target + "/" + +if __name__ == '__main__': + args = len(sys.argv) + if args == 2: + set_config(sys.argv[1]) + else: + print("usage: set_config config_name") + sys.exit(1) diff --git a/scripts/stls.py b/scripts/stls.py new file mode 100644 index 0000000..01d27d0 --- /dev/null +++ b/scripts/stls.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +import sys + +from exports import make_parts + +if __name__ == '__main__': + if len(sys.argv) > 1 and not '.' in sys.argv[1]: + target, parts = sys.argv[1], sys.argv[2:] + else: + target, parts = None, sys.argv[1:] + make_parts(target, 'stl', parts) diff --git a/scripts/svgs.py b/scripts/svgs.py new file mode 100644 index 0000000..4cca176 --- /dev/null +++ b/scripts/svgs.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +import sys + +from exports import make_parts + +if __name__ == '__main__': + if len(sys.argv) > 1 and not '.' in sys.argv[1]: + target, parts = sys.argv[1], sys.argv[2:] + else: + target, parts = None, sys.argv[1:] + make_parts(target, 'svg', parts) diff --git a/scripts/tests.py b/scripts/tests.py new file mode 100644 index 0000000..393a304 --- /dev/null +++ b/scripts/tests.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +from __future__ import print_function +import os +import sys +import openscad +import subprocess +import bom +import times +import time +import json +from deps import * +from blurb import * + +w = 4096 +h = w + +def do_cmd(cmd, output = sys.stdout): + for arg in cmd: + print(arg, end = " ") + print() + subprocess.call(cmd, stdout = output) + +def depluralise(name): + if name[-3:] == "ies" and name != "zipties": + return name[:-3] + 'y' + if name[-3:] == "hes": + return name[:-2] + if name[-1:] == 's': + return name[:-1] + return name + +def is_plural(name): + return name != depluralise(name) + +def tests(tests): + scad_dir = "tests" + deps_dir = scad_dir + "/deps" + png_dir = scad_dir + "/png" + bom_dir = scad_dir + "/bom" + for dir in [deps_dir, png_dir, bom_dir]: + if not os.path.isdir(dir): + os.makedirs(dir) + doc_name = "readme.md" + index = {} + bodies = {} + times.read_times() + # + # Make cover pic if does not exist as very slow. Delete it to force an update. + # + png_name = "libtest.png" + scad_name = "libtest.scad" + if not os.path.isfile(png_name): + openscad.run("--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name); + do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", "#ffffe5", "-border", "10", png_name]) + # + # List of individual part files + # + scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] + + for scad in scads: + base_name = scad[:-5] + if not tests or base_name in tests: + print(base_name) + cap_name = base_name[0].capitalize() + base_name[1:] + scad_name = scad_dir + '/' + scad + png_name = png_dir + '/' + base_name + '.png' + bom_name = bom_dir + '/' + base_name + '.json' + + objects_name = None + vits_name = 'vitamins/' + base_name + '.scad' + if is_plural(base_name) and os.path.isfile(vits_name): + objects_name = vits_name + + locations = [ + ('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'), + (base_name + '.scad', 'Printed'), + ('utils/' + base_name + '.scad', 'Utilities'), + ('utils/core/' + base_name + '.scad', 'Core Utilities'), + ] + + for name, type in locations: + if os.path.isfile(name): + impl_name = name + break + else: + print("Can't find implementation!") + continue + + vsplit = "N" + vtype = locations[0][1] + types = [vtype + ' A-' + vsplit[0], vtype + ' ' + chr(ord(vsplit) + 1) + '-Z'] + [loc[1] for loc in locations[1 :]] + if type == vtype: + type = types[0] if cap_name[0] <= vsplit else types[1] + + if not type in bodies: + bodies[type] = [] + index[type] = [] + body = bodies[type] + + index[type] += [cap_name] + body += ['' % cap_name] + body += ["## " + cap_name] + + doc = None + if impl_name: + doc = scrape_code(impl_name) + blurb = doc["blurb"] + else: + blurb = scrape_blurb(scad_name) + + if not len(blurb): + print("Blurb not found!") + else: + body += [ blurb ] + + if objects_name: + body += ["[%s](%s) Object definitions.\n" % (objects_name, objects_name)] + + if impl_name: + body += ["[%s](%s) Implementation.\n" % (impl_name, impl_name)] + + body += ["[%s](%s) Code for this example.\n" % (scad_name.replace('\\','/'), scad_name)] + + if doc: + for thing in ["properties", "functions", "modules"]: + things = doc[thing] + if things: + body += ['### %s\n| | |\n|:--- |:--- |' % thing.title()] + for item in sorted(things): + body += ['| ```%s``` | %s |' % (item, things[item])] + body += [''] + + body += ["![%s](%s)\n" %(base_name, png_name)] + + dname = deps_name(deps_dir, scad) + oldest = min(mtime(png_name), mtime(bom_name)) + changed = check_deps(oldest, dname) + changed = times.check_have_time(changed, scad_name) + if changed: + print(changed) + t = time.time() + openscad.run("-D", "$bom=2", "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", png_name, scad_name); + times.add_time(scad_name, t) + do_cmd(["magick", png_name, "-trim", "-resize", "1000x600", "-bordercolor", "#ffffe5", "-border", "10", png_name]) + BOM = bom.parse_bom() + with open(bom_name, 'wt') as outfile: + json.dump(BOM.flat_data(), outfile, indent = 4) + + with open(bom_name, "rt") as bom_file: + BOM = json.load(bom_file) + for thing in ["vitamins", "printed", "routed", "assemblies"]: + things = BOM[thing] + if things: + body += ['### %s\n| | | |\n| ---:|:--- |:---|' % thing.title()] + for item in sorted(things, key = lambda s: s.split(":")[-1]): + name = item + desc = '' + if thing == "vitamins": + vit = item.split(':') + name = '```' + vit[0] + '```' if vit[0] else '' + while '[[' in name and ']]' in name: + i = name.find('[[') + j = name.find(']]') + 2 + name = name.replace(name[i : j], '[ ... ]') + desc = vit[1] + body += ['| %3d | %s | %s |' % (things[item], name, desc)] + body += [''] + + body += ['\nTop'] + body += ["\n---"] + + with open(doc_name, "wt") as doc_file: + print('# NopSCADlib', file = doc_file) + print('''\ +An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc. + +It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and +some utilities. There are also Python scripts to generate Bills of Materials (BOMs), + STL files for all the printed parts and DXF files for CNC routed parts in a project. + +\n +''', file = doc_file) + + print('## Table of Contents', file = doc_file) + print('', file = doc_file) + n = 0 + for type in types: + print('' % type, end = '', file = doc_file) + n = max(n, len(index[type])) + print('', file = doc_file) + for i in range(n): + print('', file = doc_file, end = '') + for type in types: + if i < len(index[type]): + name = index[type][i] + print('', file = doc_file, end = '') + else: + print('', file = doc_file, end = '') + print('', file = doc_file) + print('
%s
' + name + '
\n\n---', file = doc_file) + for type in types: + for line in bodies[type]: + print(line, file = doc_file) + with open("readme.html", "wt") as html_file: + do_cmd("python -m markdown -x tables readme.md".split(), html_file) + times.print_times() + do_cmd('codespell -L od readme.md'.split()) + +if __name__ == '__main__': + tests(sys.argv[1:]) diff --git a/scripts/times.py b/scripts/times.py new file mode 100644 index 0000000..f6e7acf --- /dev/null +++ b/scripts/times.py @@ -0,0 +1,71 @@ +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# show execution times and how they have changed. + +import json +import time +from colorama import Fore, init + +def read_times(dir = '.'): + global times, last_times, times_fname + times_fname = dir + '/times.txt' + init() + try: + with open(times_fname) as json_file: + times = json.load(json_file) + last_times = dict(times) + except: + times = {} + last_times = {} + +def write_times(): + with open(times_fname, 'w') as outfile: + json.dump(times, outfile, indent = 4) + +def got_time(name): + return name in last_times + +def check_have_time(changed, name): + if not changed and not got_time(name): + changed = "no previous time" + return changed + +def add_time(name, start): + times[name] = round(time.time() - start, 3) + +def print_times(): + write_times() + sorted_times = sorted(times.items(), key=lambda kv: kv[1]) + total = 0 + for entry in sorted_times: + colour = Fore.WHITE + key = entry[0] + new = entry[1] + delta = 0 + if key in last_times: + old = last_times[key] + delta = new - old + if delta > 0.3: + colour = Fore.RED + if delta < -0.3: + colour = Fore.GREEN + print(colour + "%5.1f %5.1f %s" % (new, delta, key)) + total += new + print(Fore.WHITE + "%5.1f" % total) diff --git a/scripts/views.py b/scripts/views.py new file mode 100644 index 0000000..8bdb095 --- /dev/null +++ b/scripts/views.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# 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 . +# + +# +#: Generate assembly views and intructions +# +from __future__ import print_function +from set_config import * +import openscad +from tests import do_cmd +import time +import times +from deps import * +import os +import json +import blurb +import bom +from colorama import Fore + +def is_assembly(s): + return s[-9:] == '_assembly' or s[-11:] == '_assemblies' + +def add_assembly(flat_bom, bom): + if not bom in flat_bom: + big = False + for ass in bom["assemblies"]: + add_assembly(flat_bom, ass) + if ass["routed"]: + big = True + bom["big"] = big or bom["routed"] + flat_bom.append(bom) + +def bom_to_assemblies(bom_dir): + global flat_bom + # + # Make a list of all the parts in the BOM + # + bom = {} + bom_file = bom_dir + "/bom.json" + with open(bom_file) as json_file: + bom = json.load(json_file) + flat_bom = [] + add_assembly(flat_bom, bom) + ass = flat_bom[-1] + if len(ass["assemblies"]) < 2 and not ass["vitamins"] and not ass["printed"] and not ass["routed"]: + flat_bom = flat_bom[:-1] + return [assembly["name"] for assembly in flat_bom] + +def eop(print_mode, project, doc_file, last = False, first = False): + if print_mode: + print('\n
', file = doc_file) + else: + if not first: + print('[Top](#%s)' % project.lower().replace(' ', '-'), file = doc_file) + if not last: + print("\n---", file = doc_file) + +def pad(s, before, after = 0): + return ' ' * before + str(s) + ' ' * after + +def views(target, do_assemblies = None): + done_assemblies = [] + # + # Make the target directory + # + top_dir = set_config(target) + target_dir = top_dir + 'assemblies' + deps_dir = top_dir + "deps" + bom_dir = top_dir + "bom" + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + if not os.path.isdir(deps_dir): + os.makedirs(deps_dir) + times.read_times(target_dir) + # + # Find all the assemblies + # + assemblies = bom_to_assemblies(bom_dir) + for file in os.listdir(target_dir): + if file.endswith('.png'): + assembly = file[:-4].replace('_assembled', '_assembly') + if assembly.endswith('_tn'): + assembly = assembly[:-3] + if not assembly in assemblies: + print("Removing %s" % file) + os.remove(target_dir + '/' + file) + # + # Find all the scad files + # + main_blurb = None + lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib' + for dir in [source_dir, lib_dir]: + for filename in os.listdir(dir): + if filename.endswith('.scad'): + # + # find any modules with names ending in _assembly + # + with open(dir + "/" + filename, "r") as f: + lines = f.readlines() + line_no = 0 + for line in lines: + words = line.split() + if len(words) and words[0] == "module": + module = words[1].split('(')[0] + if is_assembly(module): + if module in assemblies: + # + # Scrape the assembly instructions + # + for ass in flat_bom: + if ass["name"] == module: + if not "blurb" in ass: + ass["blurb"] = blurb.scrape_module_blurb(lines[:line_no]) + break + if not do_assemblies or module in do_assemblies: + # + # make a file to use the module + # + png_maker_name = 'png.scad' + with open(png_maker_name, "w") as f: + f.write("use <%s/%s>\n" % (dir, filename)) + f.write("%s();\n" % module); + # + # Run openscad on the created file + # + dname = deps_name(deps_dir, filename) + for explode in [0, 1]: + png_name = target_dir + '/' + module + '.png' + if not explode: + png_name = png_name.replace('_assembly', '_assembled') + changed = check_deps(mtime(png_name), dname) + changed = times.check_have_time(changed, png_name) + if changed: + print(changed) + t = time.time() + openscad.run("-D$pose=1", "-D$explode=%d" % explode, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", png_name, png_maker_name); + times.add_time(png_name, t) + do_cmd(["magick", png_name, "-trim", "-resize", "1004x1004", "-bordercolor", "#ffffe5", "-border", "10", png_name]) + tn_name = png_name.replace('.png', '_tn.png') + if mtime(png_name) > mtime(tn_name): + do_cmd(("magick "+ png_name + " -trim -resize 280x280 -background #ffffe5 -gravity Center -extent 280x280 -bordercolor #ffffe5 -border 10 " + tn_name).split()) + os.remove(png_maker_name) + done_assemblies.append(module) + else: + if module == 'main_assembly': + main_blurb = blurb.scrape_module_blurb(lines[:line_no]) + line_no += 1 + times.print_times() + # + # Build the document + # + for print_mode in [True, False]: + doc_name = top_dir + "readme.md" + with open(doc_name, "wt") as doc_file: + # + # Title, description and picture + # + project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_')) + print('# %s' % project, file = doc_file) + main_file = bom.find_scad_file('main_assembly') + if not main_file: + raise Exception("can't find source for main_assembly") + text = blurb.scrape_blurb(source_dir + '/' + main_file) + if len(text): + print(text, file = doc_file, end = '') + else: + if print_mode: + print(Fore.MAGENTA + "Missing project description" + Fore.WHITE) + print('![Main Assembly](assemblies/%s.png)\n' % flat_bom[-1]["name"].replace('_assembly', '_assembled'), file = doc_file) + eop(print_mode, project, doc_file, first = True) + # + # Build TOC + # + print('## Table of Contents', file = doc_file) + print('[TOC]\n', file = doc_file) + eop(print_mode, project, doc_file) + # + # Global BOM + # + print('## Parts list', file = doc_file) + vitamins = {} + printed = {} + routed = {} + for ass in flat_bom: + for v in ass["vitamins"]: + if v in vitamins: + vitamins[v] += ass["vitamins"][v] + else: + vitamins[v] = ass["vitamins"][v] + for p in ass["printed"]: + if p in printed: + printed[p] += ass["printed"][p] + else: + printed[p] = ass["printed"][p] + for ass in flat_bom: + name = ass["name"][:-9].replace('_', ' ').title().replace(' ',' ') + print('| %s ' % name, file = doc_file, end = '') + print('| TOTALS | |', file = doc_file) + print(('|--:' * len(flat_bom) + '|--:|:--|'), file = doc_file) + for v in sorted(vitamins, key = lambda s: s.split(":")[-1]): + for ass in flat_bom: + count = ass["vitamins"][v] if v in ass["vitamins"] else '.' + print('| %s ' % pad(count, 2, 1), file = doc_file, end = '') + print('| %s | %s |' % (pad(vitamins[v], 2, 1), pad(v.split(":")[1], 2)), file = doc_file) + print(('| ' * len(flat_bom) + '| | **3D Printed parts** |'), file = doc_file) + for p in sorted(printed): + for ass in flat_bom: + count = ass["printed"][p] if p in ass["printed"] else '.' + print('| %s ' % pad(count, 2, 1), file = doc_file, end = '') + print('| %s | %s |' % (pad(printed[p], 2, 1), pad(p, 3)), file = doc_file) + eop(print_mode, project, doc_file) + # + # Assembly instructions + # + for ass in flat_bom: + name = ass["name"] + cap_name = name.replace('_', ' ').title() + if ass["count"] > 1: + print("## %d x %s" % (ass["count"], cap_name), file = doc_file) + else: + print("## %s" % cap_name, file = doc_file) + vitamins = ass["vitamins"] + if vitamins: + print("### Vitamins", file = doc_file) + print("|Qty|Description|", file = doc_file) + print("|--:|:----------|", file = doc_file) + for v in vitamins: + print("|%d|%s|" % (vitamins[v], v.split(":")[1]), file = doc_file) + print("\n", file = doc_file) + + printed = ass["printed"] + if printed: + print('### 3D Printed parts', file = doc_file) + i = 0 + for p in printed: + print('%s %d x %s |' % ('\n|' if not (i % 3) else '', printed[p], p), file = doc_file, end = '') + if (i % 3) == 2 or i == len(printed) - 1: + n = (i % 3) + 1 + print('\n|%s' % ('--|' * n), file = doc_file) + for j in range(n): + part = list(printed.keys())[i - n + j + 1] + print('| ![%s](stls/%s) %s' % (part, part.replace('.stl','.png'), '|\n' if j == j - 1 else ''), end = '', file = doc_file) + print('\n', file = doc_file) + i += 1 + print('\n', file = doc_file) + + routed = ass["routed"] + if routed: + print("### CNC Routed parts", file = doc_file) + i = 0 + for r in routed: + print('%s %d x %s |' % ('\n|' if not (i % 3) else '', routed[r], r), file = doc_file, end = '') + if (i % 3) == 2 or i == len(routed) - 1: + n = (i % 3) + 1 + print('\n|%s' % ('--|' * n), file = doc_file) + for j in range(n): + part = list(routed.keys())[i - n + j + 1] + print('| ![%s](dxfs/%s) %s' % (part, part.replace('.dxf','.png'), '|\n' if j == j - 1 else ''), end = '', file = doc_file) + print('\n', file = doc_file) + i += 1 + print('\n', file = doc_file) + + sub_assemblies = ass["assemblies"] + if sub_assemblies: + print("### Sub-assemblies", file = doc_file) + i = 0 + for a in sub_assemblies: + print('%s %d x %s |' % ('\n|' if not (i % 3) else '', a["count"], a["name"]), file = doc_file, end = '') + if (i % 3) == 2 or i == len(sub_assemblies) - 1: + n = (i % 3) + 1 + print('\n|%s' % ('--|' * n), file = doc_file) + for j in range(n): + a = sub_assemblies[i - n + j + 1]["name"].replace('_assembly', '_assembled') + print('| ![%s](assemblies/%s) %s' % (a, a + '_tn.png', '|\n' if j == j - 1 else ''), end = '', file = doc_file) + print('\n', file = doc_file) + i += 1 + print('\n', file = doc_file) + + small = not ass["big"] + suffix = '_tn.png' if small else '.png' + print('### Assembly instructions', file = doc_file) + print('![%s](assemblies/%s)\n' % (name, name + suffix), file = doc_file) + + if "blurb" in ass and ass["blurb"]: + print(ass["blurb"], file = doc_file) + else: + if print_mode: + print(Fore.MAGENTA + "Missing instructions for %s" % name, Fore.WHITE) + + name = name.replace('_assembly', '_assembled') + print('![%s](assemblies/%s)\n' % (name, name + suffix), file = doc_file) + eop(print_mode, project, doc_file, last = ass == flat_bom[-1] and not main_blurb) + # + # If main module is suppressed print any blurb here + # + if main_blurb: + print(main_blurb, file = doc_file) + eop(print_mode, project, doc_file, last = True) + # + # Convert to HTML + # + html_name = "printme.html" if print_mode else "readme.html" + with open(top_dir + html_name, "wt") as html_file: + do_cmd(("python -m markdown -x tables -x toc -x sane_lists " + doc_name).split(), html_file) + # + # Spell check + # + do_cmd('codespell -L od readme.md'.split()) + # + # List the ones we didn't find + # + missing = set() + for assembly in assemblies + (do_assemblies if do_assemblies else []): + if assembly not in done_assemblies: + missing.add(assembly) + if missing: + for assembly in missing: + print(Fore.MAGENTA + "Could not find a module called", assembly, Fore.WHITE) + sys.exit(1) + +if __name__ == '__main__': + if len(sys.argv) > 1 and sys.argv[1][-9:] != "_assembly": + target, assemblies = sys.argv[1], sys.argv[2:] + else: + target, assemblies = None, sys.argv[1:] + + views(target, assemblies) diff --git a/socket_box.scad b/socket_box.scad new file mode 100644 index 0000000..aa02bc9 --- /dev/null +++ b/socket_box.scad @@ -0,0 +1,127 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! UK 13A socket and printed backbox with earth terminal for the panel it is mounted on. +// +include +include +include +include +include + +box_height = 19; +base_thickness = 2; +wall = 3.5; + +cable_d = 7; +cable_z = cable_d / 2 + base_thickness + 1; +function cable_y(type) = -mains_socket_depth(type) / 2; +cable_x = 0; + +earth = M3_ringterm; +earth_screw = ringterm_screw(earth); + +height = base_thickness + box_height; +function socket_box_depth() = height; //! Outside depth of the backbox + +module socket_box(type) { //! Generate STL of the backbox for the specified socket + stl(str("socket_box_",type[0])); + + screw = mains_socket_screw(type); + screw_clearance_radius = screw_clearance_radius(screw); + + insert = screw_insert(screw); + insert_length = insert_length(insert); + insert_boss = mains_socket_insert_boss(type); + insert_hole_radius = insert_hole_radius(insert); + + difference() { + linear_extrude(height = height, convexity = 5) + face_plate(type); + + difference() { + translate_z(base_thickness) + linear_extrude(height = height, convexity = 5) + offset(-wall) offset(1) face_plate(type); + + for(side = [-1, 1]) + hull() + for(x = [1, 2]) + translate([side * mains_socket_pitch(type) / x, 0]) + cylinder(d = insert_boss, h = 100); + } + // + // Socket holes + // + translate_z(height) + mains_socket_hole_positions(type) { + poly_cylinder(r = screw_clearance_radius, h = 2 * box_height, center = true); + + poly_cylinder(r = insert_hole_radius, h = 2 * insert_length, center = true); + } + // + // Cable hole + // + translate([cable_x, cable_y(type), cable_z]) + rotate([90, 0, 0]) + teardrop_plus(r = cable_d / 2, h = 30); + } +} + +module socket_box_MKLOGIC_stl() socket_box(MKLOGIC); +module socket_box_Contactum_stl() socket_box(Contactum); + +module socket_box_assembly(type) //! The box with inserts fitted +assembly(str("socket_box_", type[0])) { + screw = mains_socket_screw(type); + insert = screw_insert(screw); + + color("lime") render() socket_box(type); + + mains_socket_hole_positions(type) + translate_z(height) + insert(insert); + +} + +//! * Place two inserts into the holes in the lugs and press them home with a soldering iron with a tapered bit heated to 200°C. +module socket_box_MKLOGIC_assembly() socket_box_assembly(MKLOGIC); + +module socket_box_fastened_assembly(type, thickness) { //! The socket and backbox on each side of the specified panel thickness + screw = mains_socket_screw(type); + insert = screw_insert(screw); + screw_length = screw_longer_than(mains_socket_height(type) + thickness + insert_length(insert)); + + explode(-50) + translate_z(-height - thickness) + socket_box_assembly(type); + + explode(50, true) { + mains_socket(type); + + mains_socket_hole_positions(type) + translate_z(mains_socket_height(type)) + screw(screw, screw_length); + } + + mains_socket_earth_position(type) + rotate(-90) + ring_terminal_assembly(earth, thickness); +} diff --git a/strap_handle.scad b/strap_handle.scad new file mode 100644 index 0000000..de20184 --- /dev/null +++ b/strap_handle.scad @@ -0,0 +1,189 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Retracting strap handle. Print the strap with flexible filament. Shown with default dimensions but can +//! be fully customised by passing a list of properties. +// +include +include +include + +strap = [18, 2, M3_pan_screw, 3, 25]; +function strap() = strap; + +wall = 2; +clearance = 0.5; +step = 0.3; +overlap = 3; +panel_clearance = 0.25; +counterbore = 1; + +function strap_width(type = strap) = type[0]; //! Width of strap +function strap_thickness(type = strap) = type[1]; //! Thickness of strap +function strap_screw(type = strap) = type[2]; //! Screw type +function strap_panel(type = strap) = type[3]; //! Panel thickness +function strap_extension(type = strap) = type[4]; //! How much length of the strap that can pull out + +function strap_insert(type) = screw_insert(strap_screw(type)); //! The insert type +function strap_key(type) = strap_panel(type) - panel_clearance; +function strap_height(type) = wall + max(insert_length(strap_insert(type)) - strap_key(type), strap_thickness(type) + clearance); //! Height of the ends +function strap_end_width(type = strap) = strap_width(type) + 2 * (wall + clearance); //! Width of the ends +function strap_boss_r(type) = wall + insert_hole_radius(strap_insert(type)); + +function strap_min_width(type) = (strap_width(type) - 2 * (strap_boss_r(type) + clearance)) / 2; + +module strap_boss_shape(type) + hull() { + r = strap_boss_r(type); + + circle4n(r); + + translate([r / 2, 0]) + rounded_square([r, 2 * r], r = 1); + } + +module strap_screw_positions(length, type = strap) //! Place children at the screw positions + for(end = [-1, 1]) + translate([end * (length / 2 - wall - 2 * clearance - strap_min_width(type) - strap_boss_r(type) - strap_extension(type) / 2), 0]) + rotate(end * 90 + 90) + children(); + +module strap_holes(length, type = strap, h = 100) //! The panel cut outs + extrude_if(h) + strap_screw_positions(length, type) + offset(cnc_bit_r + 0.05) + offset(-step - cnc_bit_r) + strap_boss_shape(type); + +module strap(length, type = strap) { //! Generate the STL for the rubber strap + stl("strap"); + + len = length - 2 * (wall + clearance); + w = strap_width(type); + + linear_extrude(height = strap_thickness(type), convexity = 3) + difference() { + rounded_square([len, w], w / 2 - eps); + + for(end = [-1, 1]) + translate([end * (len / 2 - strap_min_width(type) - strap_boss_r(type) - clearance), 0]) + rotate(end * 90 + 90) + hull() { + offset(clearance) + strap_boss_shape(type); + + translate([strap_extension(type) / 2, 0]) + offset(clearance) + strap_boss_shape(type); + } + } +} + +module strap_end(type = strap) { //! Generate the STL for end piece + stl("strap_end"); + + z1 = strap_height(type) - strap_thickness(type) - clearance; + z2 = strap_height(type) + strap_key(type); + r1 = strap_boss_r(type) - 1; + + module outer() + hull() { + translate([0, -strap_end_width(type) / 2]) + square([strap_boss_r(type) + overlap, strap_end_width(type)]); + + translate([-strap_extension(type) / 2, 0]) + circle(d = strap_end_width(type)); + } + + module with_hole() + difference() { + children(); + + circle(r1); + } + + union() { + linear_extrude(height = z1) + with_hole() + outer(); + + translate_z(z1) + linear_extrude(height = strap_height(type) - z1) + difference() { + outer(); + + hull() { + translate([0, -strap_width(type) / 2 - clearance]) + square([strap_boss_r(type) + overlap, strap_width(type) + 2 * clearance]); + + translate([-strap_extension(type) / 2, 0]) + circle(d = strap_width(type) + 2 * clearance); + } + } + + linear_extrude(height = strap_height(type) - layer_height) + with_hole() + strap_boss_shape(type); + + linear_extrude(height = z2) + with_hole() + offset(cnc_bit_r) + offset(-step - cnc_bit_r) + strap_boss_shape(type); + + render() difference() { + cylinder(r = r1 + eps, h = z2); + + translate_z(z2) + insert_hole(strap_insert(type), counterbore); + } + } +} +// +//! * Place the insert into the hole and push home with a soldering iron with a tapered bit heated to 200°C. +// +module strap_end_assembly(type = strap) +assembly("strap_end") { + color(pp1_colour) + strap_end(type); + + translate_z(strap_height(type) + strap_key(type)) + insert(strap_insert(type)); +} + +module strap_assembly(length, type = strap) { //! Assembly with screws in place + screw = strap_screw(type); + washer = screw_washer(screw); + penny = penny_washer(washer); + insert = strap_insert(type); + + screw_length = screw_shorter_than(washer_thickness(washer) + washer_thickness(penny) + insert_length(insert) + panel_clearance + counterbore); + + color(pp4_colour) strap(length, type); + + strap_screw_positions(length, type) + translate_z(strap_height(type)) + vflip() { + explode(-50) strap_end_assembly(type); + + translate_z(strap_height(type) + strap_panel(type)) + screw_and_washer(screw, screw_length, true, true); + } +} diff --git a/tests/annotation.scad b/tests/annotation.scad new file mode 100644 index 0000000..37d3c1b --- /dev/null +++ b/tests/annotation.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/annotation.scad> + +module annotations() { + arrow(); + + translate_z(21) + label("Text", halign = "center", valign = "bottom"); +} + +if($preview) + annotations(); diff --git a/tests/ball_bearings.scad b/tests/ball_bearings.scad new file mode 100644 index 0000000..4b5f356 --- /dev/null +++ b/tests/ball_bearings.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +use <../utils/layout.scad> + +include <../vitamins/ball_bearings.scad> + +module ball_bearings() + layout([for(b = ball_bearings) bb_diameter(b)]) + ball_bearing(ball_bearings[$i]) + bearing_ball(3); + +if($preview) + ball_bearings(); diff --git a/tests/batteries.scad b/tests/batteries.scad new file mode 100644 index 0000000..a7d2b5f --- /dev/null +++ b/tests/batteries.scad @@ -0,0 +1,41 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/springs.scad> +include <../vitamins/batteries.scad> + +module batteries() + layout([for(b = batteries) battery_diameter(b)], 5) let(battery=batteries[$i]) { + battery = batteries[$i]; + rotate(-135) // To show Lumintop USB socket and LEDs + battery(battery); + + contact = battery_contact(battery); + translate_z(battery_length(battery) / 2 + contact_pos(contact).x) + rotate([0, 180, 0]) + battery_contact(contact); + + translate_z(-battery_length(battery) / 2 - contact_neg(contact).x) + battery_contact(contact, false); + } + +if($preview) + batteries(); diff --git a/tests/belts.scad b/tests/belts.scad new file mode 100644 index 0000000..d7d8d40 --- /dev/null +++ b/tests/belts.scad @@ -0,0 +1,74 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +include <../vitamins/belts.scad> +include <../vitamins/screws.scad> +include <../vitamins/inserts.scad> +include <../vitamins/pulleys.scad> +use <../utils/layout.scad> + +module belt_test() { + p1 = [75, -50]; + p2 = [-75, -50]; + p3 = [-75, 100]; + p4 = [75, 100]; + + p5 = [75 - pulley_pr(GT2x20ob_pulley) - pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)]; + p6 = [-75 + pulley_pr(GT2x20ob_pulley) + pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)]; + + translate(p1) pulley_assembly(GT2x20ob_pulley); + translate(p2) pulley_assembly(GT2x20ob_pulley); + translate(p3) pulley_assembly(GT2x20_toothed_idler); + translate(p4) pulley_assembly(GT2x20_toothed_idler); + + translate(p5) { + pulley = GT2x16_plain_idler; + screw = find_screw(hs_cs_cap, pulley_bore(pulley)); + insert = screw_insert(screw); + + pulley_assembly(pulley); + translate_z(pulley_height(pulley) + pulley_offset(pulley) + screw_head_depth(screw, pulley_bore(pulley))) + screw(screw, 20); + + translate_z(pulley_offset(pulley) - insert_length(insert)) + vflip() + insert(insert); + + } + translate(p6) pulley_assembly(GT2x16_plain_idler); + + path = [ [p1.x, p1.y, pulley_pr(GT2x20ob_pulley)], + [p5.x, p5.y, -pulley_pr(GT2x16_plain_idler)], + [p6.x, p6.y, -pulley_pr(GT2x16_plain_idler)], + [p2.x, p2.y, pulley_pr(GT2x20ob_pulley)], + [p3.x, p3.y, pulley_pr(GT2x20ob_pulley)], + [p4.x, p4.y, pulley_pr(GT2x20ob_pulley)] + ]; + belt = GT2x6; + belt(belt, path, 80, [0, belt_pitch_height(belt) - belt_thickness(belt) / 2]); + + translate([-25, 0]) + layout([for(b = belts) belt_width(b)], 10) + rotate([0, 90, 0]) + belt(belts[$i], [[0, 0, 20], [0, 1, 20]]); +} + +if($preview) + belt_test(); diff --git a/tests/bezier.scad b/tests/bezier.scad new file mode 100644 index 0000000..7a7cd69 --- /dev/null +++ b/tests/bezier.scad @@ -0,0 +1,83 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/bezier.scad> +use <../utils/sweep.scad> +use <../utils/annotation.scad> + +function find_min_z(path, i = 1, best_i = 0) = + i >= len(path) ? best_i + : find_min_z(path, i + 1, path[i].z < path[best_i].z ? i : best_i); + +module beziers() { + // + // Make Bezier control points to give specified length + // + control_points = adjust_bezier_length([[0, 0, 100], [0, 0, 0], [200, 0, 20], [100, -100, 50]], 250); + // + // Draw control points + // + color("green") + for(p = control_points) + translate(p) + sphere(1); + // + // Lines joining the control points + // + color("red") + sweep(control_points, circle_points(0.5, $fn = 64)); + // + // Bezier curve path from control points + // + curve = bezier_path(control_points); + // + // Draw the curve + // + sweep(curve, circle_points(2, $fn = 64)); + // + // Length computed from control points + // + length = bezier_length(control_points); + // + // Length computed from curve + // + length2 = path_length(curve); + assert(str(length) == str(length2), str(length, " ", length2)); + // + // Minimum Z + // + min_z = bezier_min_z(control_points); + i = find_min_z(curve); + assert(str(min_z) == str(curve[i].z)); + + color("blue") { + translate(curve[i] + [0, 0, 2]) + arrow(); + + translate(curve[i] - [0, 0, 2]) + vflip() + arrow(); + } + + translate(control_points[1] - [0, 0, 2]) + label(str("bezier_length = ", length, ", bezier_min_z = ", bezier_min_z(curve)), valign = "top"); +} + +if($preview) + beziers(); diff --git a/tests/blowers.scad b/tests/blowers.scad new file mode 100644 index 0000000..7e28072 --- /dev/null +++ b/tests/blowers.scad @@ -0,0 +1,41 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/blowers.scad> + +module blowers() + layout([for(b = blowers) blower_width(b)], 10, true) let(b = blowers[$i]){ + screw = blower_screw(b); + washer = screw_washer(screw); + h = blower_lug(b); + + blower(b); + + blower_hole_positions(b) + translate_z(h) + screw_and_washer(screw, screw_longer_than(h + washer_thickness(washer) + 5)); + + + } + +if($preview) + blowers(); diff --git a/tests/bom.scad b/tests/bom.scad new file mode 100644 index 0000000..f6da423 --- /dev/null +++ b/tests/bom.scad @@ -0,0 +1,108 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! BOM and assembly demonstration +// +include <../core.scad> +include <../vitamins/screws.scad> +include <../vitamins/inserts.scad> +include <../vitamins/sheets.scad> +$explode = 1; // Normally set on the command line when generating assembly views with views.py + +screw = M3_cap_screw; +sheet = PMMA3; +height = 10; + +insert = screw_insert(screw); +washer = screw_washer(screw); + +module widget(thickness) { + vitamin(str("widget(", thickness, "): Rivit like thing for ", thickness, "mm sheets")); + t = 1; + color("silver") { + cylinder(d = 3, h = thickness + 2 * eps, center = true); + + for(end = [-1, 1]) + translate_z(end * (thickness / 2 + t / 2 + eps)) + cylinder(d = 4, h = t, center = true); + } +} + +module widgit_stl() { + stl("widget"); + + union() { + rounded_rectangle([30, 30, 3], 2); + + render() insert_boss(insert, height, 2.2); + } +} + +module widgit_dxf() { + dxf("widget"); + + difference() { + sheet_2D(sheet, 20, 20, 1); + + drill(screw_clearance_radius(screw), 0); + } +} + +//! * Push the insert into the base with a soldering iron heated to 200°C +module widgit_base_assembly() +assembly("widgit_base") { + color(pp1_colour) + widgit_stl(); + + translate_z(height) + insert(insert); +} + +//! * Magically insert the widget into the acrylic sheet +module widget_top_assembly() +assembly("widget_top") { + translate([-5, 5]) + widget(sheet_thickness(sheet)); + + render_2D_sheet(sheet) // Must be last because it is transparent + widgit_dxf(); +} + +//! * Screw the two assemblies together +module widgit_assembly() +assembly("wigdit") { + + widgit_base_assembly(); // Note this is not exloded because it is sub-assembly + + translate_z(height) { + translate_z(sheet_thickness(sheet)) + screw_and_washer(screw, screw_longer_than(sheet_thickness(sheet) + 2 * washer_thickness(washer) + 3), true); + + explode(5) + translate_z(sheet_thickness(sheet) / 2 + eps) + widget_top_assembly(); + } +} + +module boms() { + widgit_assembly(); +} + +boms(); diff --git a/tests/box.scad b/tests/box.scad new file mode 100644 index 0000000..531b06e --- /dev/null +++ b/tests/box.scad @@ -0,0 +1,54 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +include <../vitamins/screws.scad> +include <../vitamins/sheets.scad> +include <../vitamins/inserts.scad> + +use <../box.scad> + +box = [M3_dome_screw, 3, DiBond, PMMA3, DiBond6, true, 150, 100, 70]; + +include <../box_assembly.scad> + +module box_assembly() _box_assembly(box); + +module box_test() { + translate_z(box_height(box) / 2 + box_bezel_height(box, true)) + box_assembly(); + + rows = 3; + cols = 3; + gap = 30; + + x_pitch = (box_width(box) + 2 * box_outset(box)) / cols + gap; + y_pitch = (box_depth(box) + 2 * box_outset(box)) / rows + gap; + for(x = [0 : cols - 1], y = [0 : rows - 1]) + translate([(x - cols / 2) * x_pitch + gap / 2, (y - rows / 2) * y_pitch + gap / 2]) + color((x + y) % 2 ? pp1_colour : pp2_colour) box_bezel_section(box, true, rows, cols, x, y); + + translate([-cols / 2 * x_pitch - 20, 0]) + for(i = [0 : 2]) + translate([0, (i - 1) * 20, 0]) + color(i % 2 ? pp1_colour : pp2_colour) box_corner_profile_section(box, i, 3); +} + +if($preview) + box_test(); diff --git a/tests/bulldogs.scad b/tests/bulldogs.scad new file mode 100644 index 0000000..13b2d18 --- /dev/null +++ b/tests/bulldogs.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/bulldogs.scad> + +module bulldogs() + layout([for(b = bulldogs) bulldog_depth(b)], 5, true) + bulldog(bulldogs[$i]); + +if($preview) + bulldogs(); diff --git a/tests/butt_box.scad b/tests/butt_box.scad new file mode 100644 index 0000000..1a8d419 --- /dev/null +++ b/tests/butt_box.scad @@ -0,0 +1,66 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +include <../vitamins/screws.scad> +include <../vitamins/sheets.scad> +include <../vitamins/inserts.scad> + +include <../butt_box.scad> + +$explode = 0; + +box = [M3_dome_screw, DiBond, DiBond6, PMMA3, 250, 400, 300, 120]; + +module bbox_assembly() _bbox_assembly(box); + +module bbox_test() { + translate_z(bbox_height(box) / 2) + bbox_assembly(); +} + +if($preview) + bbox_test(); +else { + gap = 2; + bb = sheet_thickness(bbox_base_sheet(box)); + bt = sheet_thickness(bbox_top_sheet(box)); + h = bbox_height(box) + bb; + h2 = h + bt; + + bbox_base_blank(box); + + translate([bbox_width(box) / 2 + gap + h / 2 - bb / 2, 0]) + rotate(90) + bbox_right_blank(box); + + translate([-bbox_width(box) / 2 - gap - h / 2 + bb / 2, 0]) + rotate(-90) + bbox_left_blank(box); + + translate([0, bbox_depth(box) / 2 + gap + h / 2 - bb / 2]) + rotate(180) + bbox_back_blank(box); + + translate([0, -bbox_depth(box) / 2 - gap - h2 / 2 - (bt - bb) / 2]) + bbox_front_blank(box); + + translate([0, -bbox_depth(box) / 2 - gap - h2 - gap - bbox_depth(box) / 2 - sheet_thickness(bbox_sheets(box))]) + bbox_top_blank(box); +} diff --git a/tests/buttons.scad b/tests/buttons.scad new file mode 100644 index 0000000..8445e43 --- /dev/null +++ b/tests/buttons.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/buttons.scad> + +module buttons() + layout([for(b = buttons) square_button_width(b)], 5) + square_button(buttons[$i]); + +if($preview) + buttons(); diff --git a/tests/cable_grommets.scad b/tests/cable_grommets.scad new file mode 100644 index 0000000..e0faa28 --- /dev/null +++ b/tests/cable_grommets.scad @@ -0,0 +1,36 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../cable_grommets.scad> + +module cable_grommets() { + rotate(90) + color(pp1_colour) ribbon_grommet(20, 3); + + translate([20, 0]) + round_grommet_assembly(6, 3); + + translate([40, 0]) + rotate(90) + color(pp1_colour) mouse_grommet(5, 3); +} + +if($preview) + cable_grommets(); diff --git a/tests/cable_strips.scad b/tests/cable_strips.scad new file mode 100644 index 0000000..18b214c --- /dev/null +++ b/tests/cable_strips.scad @@ -0,0 +1,38 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../vitamins/cable_strip.scad> + +module cable_strips() { + depth = 50; + rotate(-90) + for(pos = [-100, 0, 100]) { + bezier_cable_strip(ways = 20, depth = depth, length = 150, travel = 100, pos = pos, below = 100, extra = 10); + + translate([0, depth * 2]) + rotate([0, -90, 0]) + cable_strip(ways =20, depth = depth / 2, travel = 100, x = pos, extra = 30); + + + } +} + +if($preview) + cable_strips(); diff --git a/tests/carriers.scad b/tests/carriers.scad new file mode 100644 index 0000000..db4e1ce --- /dev/null +++ b/tests/carriers.scad @@ -0,0 +1,25 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../carriers.scad> + +module carriers() + color(pp1_colour) ESP12F_carrier_stl(); + +carriers(); diff --git a/tests/clip.scad b/tests/clip.scad new file mode 100644 index 0000000..28f62a1 --- /dev/null +++ b/tests/clip.scad @@ -0,0 +1,26 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> + +module clips() { + clip(xmin = 0, ymin = 0, zmin = 0, zmax = 40) sphere(50); +} + +clips(); diff --git a/tests/components.scad b/tests/components.scad new file mode 100644 index 0000000..054cf0d --- /dev/null +++ b/tests/components.scad @@ -0,0 +1,57 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/components.scad> + +module resistors() + layout([for(r = resistors) resistor_diameter(r)], 10) + resistor(resistors[$i]); + +module al_clad_resistors() + layout([for(a = al_clad_resistors) al_clad_width(a)]) + rotate(90) + al_clad_resistor_assembly(al_clad_resistors[$i], 4.7) + screw(al_clad_hole(al_clad_resistors[$i]) > 3 ? M3_pan_screw : M2p5_pan_screw, 16); + + +module thermal_cutouts() + layout([for(t = thermal_cutouts) tc_length(t)]) + thermal_cutout(thermal_cutouts[$i]); + +module components() { + resistors(); + + translate([0, 50]) + TO220("Generic TO220 package"); + + translate([30, 50]) + panel_USBA(); + + translate([0,80]) + thermal_cutouts(); + + translate([0, 130]) + al_clad_resistors(); +} + +if($preview) + components(); diff --git a/tests/corner_block.scad b/tests/corner_block.scad new file mode 100644 index 0000000..6972430 --- /dev/null +++ b/tests/corner_block.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../corner_block.scad> + +include <../vitamins/screws.scad> + +screws = [M2_cap_screw, M2p5_pan_screw, M3_dome_screw, M4_dome_screw]; + +module do_corner_block(screw) + if($preview) + fastened_corner_block_assembly(3, screw = screw); + else + corner_block(screw); + +module corner_blocks() + for(i = [0 : len(screws) - 1]) + translate([i * 30, 0]) + do_corner_block(screws[i]); + +corner_blocks(); diff --git a/tests/d_connectors.scad b/tests/d_connectors.scad new file mode 100644 index 0000000..3398208 --- /dev/null +++ b/tests/d_connectors.scad @@ -0,0 +1,38 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/d_connectors.scad> + +module d_connectors() + for(socket = [false, true]) + translate([socket ? len(d_connectors) * (d_flange_width(d_connectors[0]) + 10) : 0, 0]) + layout([for(d = d_connectors) d_flange_width(d)], 10) let(d = d_connectors[$i]) + rotate(90) { + d_plug(d, socket, pcb = $i == 2, idc = $i == 1); + + if(socket) + translate_z(d_flange_thickness(d)) + d_connector_holes(d) + d_pillar(); + } + +if($preview) + d_connectors(); diff --git a/tests/displays.scad b/tests/displays.scad new file mode 100644 index 0000000..7f4e129 --- /dev/null +++ b/tests/displays.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +use <../utils/layout.scad> + +include <../vitamins/displays.scad> +use <../vitamins/pcb.scad> + +module displays() + layout([for(d = displays) pcb_length(display_pcb(d))], 10) + display(displays[$i]); + +if($preview) + displays(); diff --git a/tests/dogbones.scad b/tests/dogbones.scad new file mode 100644 index 0000000..44b64ca --- /dev/null +++ b/tests/dogbones.scad @@ -0,0 +1,34 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../global_defs.scad> +use <../utils/dogbones.scad> + +module dogbones() { + #linear_extrude(height = eps) + dogbone_square([10, 20]); + + #translate([15, 0]) + dogbone_rectangle([10, 20, 5], center = false); + + sq = 3; + translate([-5 + sq / 2 + eps, -10 + sq / 2 + eps]) + %cube([sq, sq, 1], center = true); +} + +dogbones(); diff --git a/tests/door_hinge.scad b/tests/door_hinge.scad new file mode 100644 index 0000000..ace09fb --- /dev/null +++ b/tests/door_hinge.scad @@ -0,0 +1,57 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../door_hinge.scad> + +include <../vitamins/sheets.scad> +use <../vitamins/screw.scad> + +door_w = 50; +door_h = 50; +sheet = PMMA6; + +module door_hinges() { + translate([door_hinge_stat_length() / 2 - door_hinge_pin_x(), 0]) + if($preview) { + for(side = [-1, 1]) + translate_z(side * door_h / 2) { + door_hinge_assembly(side > 0, sheet_thickness(sheet)); + + door_hinge_static_assembly(side > 0, 3); + } + + translate([door_w / 2 + eps, door_hinge_pin_y() - eps]) + rotate([90, 0, 0]) + render_2D_sheet(sheet) + difference() { + sheet_2D(sheet, door_w, door_h, 3); + + for(z =[-1, 1]) + translate([-door_w / 2, z * door_h / 2]) + door_hinge_hole_positions(z) + drill(screw_pilot_hole(door_hinge_screw()), 0); + } + translate([door_w, 0]) + children(); + } + else + door_hinge_parts_stl(); +} + +door_hinges(); diff --git a/tests/door_latch.scad b/tests/door_latch.scad new file mode 100644 index 0000000..9ee24dc --- /dev/null +++ b/tests/door_latch.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../door_latch.scad> + +module door_latches() + translate([door_latch_offset(), 0]) + if($preview) + door_latch_assembly(3); + else + door_latch_stl(); + +door_latches(); diff --git a/tests/fan_guard.scad b/tests/fan_guard.scad new file mode 100644 index 0000000..4ac5c8b --- /dev/null +++ b/tests/fan_guard.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../fan_guard.scad> +include <../vitamins/fans.scad> + +module fan_guards() + layout([for(f = fans) fan_width(f)], 10) + color(pp1_colour) fan_guard(fans[$i], spokes = fan_width(fans[$i]) > 40 ? 8 : 4); + +fan_guards(); diff --git a/tests/fans.scad b/tests/fans.scad new file mode 100644 index 0000000..539beb1 --- /dev/null +++ b/tests/fans.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../fan_guard.scad> + +include <../vitamins/fans.scad> + +module fans() + layout([for(f = fans) fan_width(f)], 10) + fan_assembly(fans[$i], 3 + fan_guard_thickness(), true); + +if($preview) + fans(); diff --git a/tests/fillet.scad b/tests/fillet.scad new file mode 100644 index 0000000..4a5d0bb --- /dev/null +++ b/tests/fillet.scad @@ -0,0 +1,26 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../global_defs.scad> +use <../utils/fillet.scad> + +module fillets() { + fillet(3, 25); +} + +fillets(); diff --git a/tests/fixing_block.scad b/tests/fixing_block.scad new file mode 100644 index 0000000..145bae6 --- /dev/null +++ b/tests/fixing_block.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../fixing_block.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> + +screws = [M2_cap_screw, M2p5_pan_screw, M3_dome_screw, M4_dome_screw]; + +module fixing_block_test(screw) + if($preview) + fastened_fixing_block_assembly(3, screw = screw); + else + fixing_block(screw); + +module fixing_blocks() + layout([for(s = screws) fixing_block_width(s)], 5) + fixing_block_test(screws[$i]); + +fixing_blocks(); diff --git a/tests/foot.scad b/tests/foot.scad new file mode 100644 index 0000000..39e8bb8 --- /dev/null +++ b/tests/foot.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../foot.scad> + +module feet() + if($preview) { + translate([50, 0]) + foot_assembly(3); + + translate([foot_diameter(insert_foot()) / 2, 0]) + fastened_insert_foot_assembly(3); + } + else { + translate([50, 0]) + foot(); + + insert_foot(); + } + +feet(); diff --git a/tests/fuseholder.scad b/tests/fuseholder.scad new file mode 100644 index 0000000..313b852 --- /dev/null +++ b/tests/fuseholder.scad @@ -0,0 +1,27 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../vitamins/fuseholder.scad> + +module fuseholders() + fuseholder(6); + +if($preview) + fuseholders(); diff --git a/tests/global.scad b/tests/global.scad new file mode 100644 index 0000000..e987814 --- /dev/null +++ b/tests/global.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> + +module globals() { + linear_extrude(height = eps) { + semi_circle(r = 10); + + translate([30, 0]) + ellipse(15, 7); + } +} + +rotate([70, 0, 315]) globals(); diff --git a/tests/handle.scad b/tests/handle.scad new file mode 100644 index 0000000..42edac4 --- /dev/null +++ b/tests/handle.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../handle.scad> + +module handle() + translate([handle_length() / 2, 0]) + if($preview) + handle_fastened_assembly(3); + else + handle_stl(); + +handle(); diff --git a/tests/hanging_hole.scad b/tests/hanging_hole.scad new file mode 100644 index 0000000..0490908 --- /dev/null +++ b/tests/hanging_hole.scad @@ -0,0 +1,28 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../global_defs.scad> +use <../utils/hanging_hole.scad> + +module hanging_holes() { + rotate(45) + #hanging_hole(z = 10, ir = 3, h = 30) + circle(r = 10); +} + +hanging_holes(); diff --git a/tests/hot_ends.scad b/tests/hot_ends.scad new file mode 100644 index 0000000..d1d7b33 --- /dev/null +++ b/tests/hot_ends.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/hot_ends.scad> + +module hot_ends() + layout([for(h = hot_ends) 40]) + translate([-20, 0]) + rotate(90) + hot_end(hot_ends[$i], 3); + +if($preview) + hot_ends(); diff --git a/tests/iecs.scad b/tests/iecs.scad new file mode 100644 index 0000000..971bf0a --- /dev/null +++ b/tests/iecs.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/iecs.scad> + +module iecs() + layout([for(i = iecs) iec_flange_h(i)], 10) + rotate(90) + iec_assembly(iecs[$i], 3); + +if($preview) + iecs(); diff --git a/tests/inserts.scad b/tests/inserts.scad new file mode 100644 index 0000000..df39dc8 --- /dev/null +++ b/tests/inserts.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/inserts.scad> + +module inserts() + for(i = [0: len(inserts) -1]) + translate([10 * i, 0]) + insert(inserts[i]); + +if($preview) + inserts(); diff --git a/tests/jack.scad b/tests/jack.scad new file mode 100644 index 0000000..f5bfcc5 --- /dev/null +++ b/tests/jack.scad @@ -0,0 +1,35 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../vitamins/jack.scad> + +module jacks() { + translate([0, 0]) + jack_4mm("blue",3, "royalblue"); + + translate([20, 0]) + jack_4mm_shielded("brown", 3, "sienna"); + + translate([40, 0]) + post_4mm("red",3); +} + +if($preview) + jacks(); diff --git a/tests/layout.scad b/tests/layout.scad new file mode 100644 index 0000000..be64a27 --- /dev/null +++ b/tests/layout.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../global_defs.scad> +use <../utils/layout.scad> + +diams = [3, 7, 5, 11]; + +module layouts() { + linear_extrude(height = eps) + layout(diams, gap = 1) + circle(d = diams[$i]); +} + +rotate([70, 0, 315]) layouts(); diff --git a/tests/leadnuts.scad b/tests/leadnuts.scad new file mode 100644 index 0000000..1b5251d --- /dev/null +++ b/tests/leadnuts.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/leadnuts.scad> + +module leadnuts() + layout([for(n = leadnuts) leadnut_flange_dia(n)], 5) + leadnut(leadnuts[$i]); + +if($preview) + leadnuts(); diff --git a/tests/leds.scad b/tests/leds.scad new file mode 100644 index 0000000..53f0842 --- /dev/null +++ b/tests/leds.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/leds.scad> + +module leds() + layout([for(l = LEDs) led_diameter(l)], 5) + led(LEDs[$i], ["green", "blue", "red"][$i % 3]); + +if($preview) + leds(); diff --git a/tests/light_strips.scad b/tests/light_strips.scad new file mode 100644 index 0000000..0451630 --- /dev/null +++ b/tests/light_strips.scad @@ -0,0 +1,38 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/light_strips.scad> + +module light_strips() + layout([for(s = light_strips) light_strip_width(s)], 10) + rotate(90) let(light = light_strips[$i], segs = light_strip_segments(light, 260), d = light_strip_clip_depth(light)) { + light_strip(light, segs); + + for(end = [-1, 1]) + translate([end * (light_strip_cut_length(light, segs) / 2 - d / 2), 0]) + rotate([90, 0, 90]) + color("lime") render() + translate_z(-d / 2) + light_strip_clip(light); + } + +if($preview) + light_strips(); diff --git a/tests/linear_bearings.scad b/tests/linear_bearings.scad new file mode 100644 index 0000000..ebe6485 --- /dev/null +++ b/tests/linear_bearings.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/linear_bearings.scad> + +module linear_bearings() + layout([for(b = linear_bearings) 2 * bearing_radius(b)]) + linear_bearing(linear_bearings[$i]); + +if($preview) + linear_bearings(); diff --git a/tests/mains_sockets.scad b/tests/mains_sockets.scad new file mode 100644 index 0000000..b9d7832 --- /dev/null +++ b/tests/mains_sockets.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/mains_sockets.scad> + +module mains_sockets() + layout([for(s = mains_sockets) mains_socket_width(s)], 5) + mains_socket(mains_sockets[$i]); + +if($preview) + mains_sockets(); diff --git a/tests/maths.scad b/tests/maths.scad new file mode 100644 index 0000000..0f36dda --- /dev/null +++ b/tests/maths.scad @@ -0,0 +1,70 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/maths.scad> +use <../utils/annotation.scad> + +tip = [0, 0, 20]; + +module shape() { + cylinder(d1 = 20, d2 = 0, h = tip.z); +} + +t = [10, 20, 30]; +r = [45, 20, 70]; +s = [1, 0.5, 0.75]; + +module maths() { + // + // Translate, rotate and scale the shape + // + translate(t) + rotate(r) + scale(s) + shape(); + // + // Apply the same transformations to the vector position of the tip + // + p = transform(tip, translate(t) * rotate(r) * scale(s)); + // + // Place an arrow where the tip ends up + // + translate(p) + arrow(); + // + // Unit vector pointing at p + // + u = unit(p); + // + // Point arrow in same direction + // + z = [0, 0, 1]; + v = cross(u, z); + a = acos(u * z); + + + l = 20; + rotate(-a, v) + translate_z(l) + vflip() + arrow(l); +} + +rotate(45) + maths(); diff --git a/tests/meter.scad b/tests/meter.scad new file mode 100644 index 0000000..a010603 --- /dev/null +++ b/tests/meter.scad @@ -0,0 +1,39 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../vitamins/meter.scad> + +module meters() + if($preview) { + meter_assembly(); + + translate([0, meter_bezel_width() + 5]) + vflip() + meter_assembly(); + + translate([0, -meter_bezel_width()]) + rotate([0, 180, 0]) + meter(colour = "blue", value = "123"); + } + else + meter_bezel(); + + +meters(); diff --git a/tests/microswitches.scad b/tests/microswitches.scad new file mode 100644 index 0000000..95ad753 --- /dev/null +++ b/tests/microswitches.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/microswitches.scad> + +module microswitches() + layout([for(i = microswitches) microswitch_length(i)], 5) + microswitch(microswitches[$i]); + +if($preview) + microswitches(); diff --git a/tests/microview.scad b/tests/microview.scad new file mode 100644 index 0000000..7a582be --- /dev/null +++ b/tests/microview.scad @@ -0,0 +1,21 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +use <../vitamins/microview.scad> + +microview(!$preview); diff --git a/tests/modules.scad b/tests/modules.scad new file mode 100644 index 0000000..5bfd1d3 --- /dev/null +++ b/tests/modules.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/modules.scad> + +module modules() + layout([for(m = modules) mod_length(m)], 5) + module_assembly(modules[$i], 3); + +if($preview) + modules(); diff --git a/tests/nuts.scad b/tests/nuts.scad new file mode 100644 index 0000000..4004702 --- /dev/null +++ b/tests/nuts.scad @@ -0,0 +1,51 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> + +module nuts() { + for(nyloc = [false, true]) + translate([0, nyloc ? 20 : 0]) + layout([for(n = nuts) 2 * nut_radius(n)], 5) + nut(nuts[$i], nyloc); + + translate([0, 40]) + layout([for(n = nuts) 2 * nut_radius(n)], 5) let(n = nuts[$i]) { + if(n == M3_nut) + nut(n, brass = true); + + if(n == M2p5_nut) + nut(n, nylon = true); + + if(n == M4_nut) + rotate(-45) + wingnut(M4_wingnut); + + if(n == M6_nut) + nut_and_washer(M6_half_nut, false); + + if(n == M8_nut) + #nut_trap(M8_cap_screw, n, h = 30); + } +} + +if($preview) + nuts(); diff --git a/tests/o_ring.scad b/tests/o_ring.scad new file mode 100644 index 0000000..28ca861 --- /dev/null +++ b/tests/o_ring.scad @@ -0,0 +1,25 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +use <../vitamins/o_ring.scad> + +module o_rings() + O_ring(2.5, 1.6, 3); + +if($preview) + o_rings(); diff --git a/tests/offset.scad b/tests/offset.scad new file mode 100644 index 0000000..bd81d11 --- /dev/null +++ b/tests/offset.scad @@ -0,0 +1,39 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> +use <../utils/offset.scad> + +module shape() + difference() { + cube(40, center = true); + + cube([20, 20, 41], center = true); + } + +module offsets() { + $fn = 20; + + for(x = [-1, 0, 1]) + translate([50 * x, 0]) + offset_3D(3 * x, chamfer_base = true) + shape(); +} + +offsets(); diff --git a/tests/opengrab.scad b/tests/opengrab.scad new file mode 100644 index 0000000..146e023 --- /dev/null +++ b/tests/opengrab.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +use <../vitamins/opengrab.scad> + +module opengrab_test() { + opengrab_target(); + + rotate(45) + translate_z(opengrab_target_thickness()) + opengrab(); +} + +if($preview) + opengrab_test(); diff --git a/tests/pcbs.scad b/tests/pcbs.scad new file mode 100644 index 0000000..195a617 --- /dev/null +++ b/tests/pcbs.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/d_connectors.scad> +include <../vitamins/pcbs.scad> + +module pcbs() + layout([for(p = pcbs) pcb_width(p)], 15) + translate([0, pcb_length(pcbs[$i]) / 2]) + rotate(90) + pcb_assembly(pcbs[$i], 5 + $i, 3); + +if($preview) + pcbs(); diff --git a/tests/pillars.scad b/tests/pillars.scad new file mode 100644 index 0000000..828efee --- /dev/null +++ b/tests/pillars.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/pillars.scad> + +module pillars() + layout([for(p = pillars) pillar_od(p)], 5) + pillar(pillars[$i]); + +if($preview) + pillars(); diff --git a/tests/polyholes.scad b/tests/polyholes.scad new file mode 100644 index 0000000..fa61b2b --- /dev/null +++ b/tests/polyholes.scad @@ -0,0 +1,77 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> +use <../vitamins/rod.scad> +include <../vitamins/sheets.scad> + +module polyholes() { + module positions() + for(i = [1 : 10]) { + translate([(i * i + i) / 2 + 3 * i , 8]) + let($r = i / 2) + children(); + + let(d = i + 0.5) + translate([(d * d + d) / 2 + 3 * d, 19]) + let($r = d / 2) + children(); + } + + color(pp1_colour) linear_extrude(height = 3, center = true) + difference() { + square([100, 27]); + + positions() + poly_circle(r = $r); + } + + positions() + rod(d = 2 * $r, l = 8 * $r + 5); + // + // Poly rings + // + ir = 3 / 2; + cir = corrected_radius(ir); + sizes = [1.5, 2, 3, 4]; + for(i = [0 : len(sizes) - 1]) + translate([i * 10, -10]) { + color(pp1_colour) linear_extrude(height = 1) + poly_ring(ir = ir, or = cir + sizes[i] * extrusion_width); + + rod(2 * ir, 3); + } + // + // Drill and slot + // + sheet = Steel06; + translate([10, -30]) + render_2D_sheet(sheet) + difference() { + sheet_2D(sheet, 20, 20, 1); + + translate([0, 5]) + slot(1.5, 6, 0); + + translate([0, -5]) + drill(2, 0); + } +} + +polyholes(); diff --git a/tests/psus.scad b/tests/psus.scad new file mode 100644 index 0000000..9c934e7 --- /dev/null +++ b/tests/psus.scad @@ -0,0 +1,36 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/psus.scad> + +module psus() + layout([for(p = psus) psu_width(p)], 10) let(p = psus[$i]) + rotate(atx_psu(p) ? 0 : 90) { + psu(p); + + psu_screw_positions(p) + translate_z(3) + screw_and_washer(psu_screw(p), 8); + } + +if($preview) + psus(); diff --git a/tests/pulleys.scad b/tests/pulleys.scad new file mode 100644 index 0000000..79212d6 --- /dev/null +++ b/tests/pulleys.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/belts.scad> +include <../vitamins/pulleys.scad> + +module pulleys() + layout([for(p = pulleys) pulley_flange_dia(p)]) + rotate(-45) + pulley_assembly(pulleys[$i]); + +if($preview) + pulleys(); diff --git a/tests/quadrant.scad b/tests/quadrant.scad new file mode 100644 index 0000000..2149f00 --- /dev/null +++ b/tests/quadrant.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../global_defs.scad> +use <../utils/quadrant.scad> + + +module quadrants() { + linear_extrude(height = eps) + quadrant(10, 4); +} + +rotate([70, 0, 315]) quadrants(); diff --git a/tests/rails.scad b/tests/rails.scad new file mode 100644 index 0000000..f91b950 --- /dev/null +++ b/tests/rails.scad @@ -0,0 +1,48 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/rails.scad> +use <../vitamins/nut.scad> + +sheet = 3; + +module rails() + layout([for(l = rails) carriage_width(rail_carriage(l))], 25) + rotate(-90) { + rail = rails[$i]; + length = rail == MGN15 ? 260 : 200; + screw = rail_screw(rail); + nut = screw_nut(screw); + washer = screw_washer(screw); + + rail_assembly(rail, length, rail_travel(rail, length) / 2); + + rail_screws(rail, length, sheet + nut_thickness(nut, true) + washer_thickness(washer)); + + rail_hole_positions(rail, length, 0) + translate_z(-sheet) + vflip() + nut_and_washer(nut, true); + } + +if($preview) + rails(); diff --git a/tests/ribbon_clamp.scad b/tests/ribbon_clamp.scad new file mode 100644 index 0000000..d797965 --- /dev/null +++ b/tests/ribbon_clamp.scad @@ -0,0 +1,35 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../ribbon_clamp.scad> +use <../vitamins/wire.scad> + +ways = 20; + +module ribbon_clamps() + translate([ribbon_clamp_length(ways) / 2, 0]) + if($preview) { + ribbon_clamp_fastened_assembly(ways, 3); + + ribbon_cable(ways, 100); + } + else + ribbon_clamp(ways); + +ribbon_clamps(); diff --git a/tests/ring_terminals.scad b/tests/ring_terminals.scad new file mode 100644 index 0000000..b46b8b3 --- /dev/null +++ b/tests/ring_terminals.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/ring_terminals.scad> + +module ring_terminals() + layout([for(t = ring_terminals) ringterm_od(t)], 5) + rotate(90) + ring_terminal_assembly(ring_terminals[$i], 3); + +if($preview) + ring_terminals(); diff --git a/tests/rockers.scad b/tests/rockers.scad new file mode 100644 index 0000000..5dd840d --- /dev/null +++ b/tests/rockers.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/rockers.scad> + +module rockers() + layout([for(r = rockers) rocker_flange_w(r)], 5) + rocker(rockers[$i]); + +if($preview) + rockers(); diff --git a/tests/rod.scad b/tests/rod.scad new file mode 100644 index 0000000..f83873a --- /dev/null +++ b/tests/rod.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/linear_bearings.scad> + +use <../vitamins/rod.scad> + +module rods() + layout([for(b = linear_bearings) 2 * bearing_radius(b)]) + rod(bearing_rod_dia(linear_bearings[$i]), 80); + +if($preview) + rods(); diff --git a/tests/round.scad b/tests/round.scad new file mode 100644 index 0000000..dc552b0 --- /dev/null +++ b/tests/round.scad @@ -0,0 +1,42 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> +use <../utils/round.scad> + +module shape() + difference() { + square(40, center = true); + + square([20, 20], center = true); + } + +module rounds() { + linear_extrude(height = eps) + round(or = 4, ir = 2) + shape(); + + + translate([50, 0]) + round_3D(or = 4, ir = 2, chamfer_base = true, $fn = 16) + linear_extrude(height = 40, center = true) + shape(); +} + +rounds(); diff --git a/tests/rounded_cylinder.scad b/tests/rounded_cylinder.scad new file mode 100644 index 0000000..a55b73b --- /dev/null +++ b/tests/rounded_cylinder.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../global_defs.scad> +use <../utils/rounded_cylinder.scad> + + +module rounded_cylinders() { + linear_extrude(height = eps) + rounded_corner(10, 20, 3, 5); + + translate([30, 10]) + rounded_cylinder(10, 20, 3, 5, 270); +} + +rounded_cylinders(); diff --git a/tests/rounded_polygon.scad b/tests/rounded_polygon.scad new file mode 100644 index 0000000..ad4f786 --- /dev/null +++ b/tests/rounded_polygon.scad @@ -0,0 +1,57 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../global_defs.scad> +use <../utils/rounded_polygon.scad> +use <../utils/annotation.scad> + +r = 5; +h = 40; +h2 = 20; +h3 = 30; +w = 100; +w2 = 70; +w3 = 30; + +profile = [ + [ -w / 2 + r, r, r], + [ -w / 2 + r / 2, 2 * r + eps, -eps], + [-w2 / 2, h - r, r], + [-w3 / 2, h2 + r,-r], + [ 0, h3 - r, r], + [ w3 / 2, h2 + r,-r], + [ w2 / 2, h - r, r], + [ w / 2 - r / 2, 2 * r + eps, -eps], + [ w / 2 - r, r, r], +]; + +module rounded_polygons() { + tangents = rounded_polygon_tangents(profile); + length = rounded_polygon_length(profile, tangents); + + rotate([70, 0, 315]) + linear_extrude(height = eps) + rounded_polygon(profile, tangents); + + translate([0, -10]) + label(str("perimeter length = ", length), valign = "top", halign = "right"); + +} + +rounded_polygons(); diff --git a/tests/rounded_rectangle.scad b/tests/rounded_rectangle.scad new file mode 100644 index 0000000..8d903cd --- /dev/null +++ b/tests/rounded_rectangle.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> + +module rounded_rectangles() { + linear_extrude(height = eps) + rounded_square([30, 20], 3); + + translate([40, 0]) + rounded_rectangle([30, 20, 10], 3); +} + +rounded_rectangles(); diff --git a/tests/screw_knob.scad b/tests/screw_knob.scad new file mode 100644 index 0000000..28b3058 --- /dev/null +++ b/tests/screw_knob.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../screw_knob.scad> + +include <../vitamins/screws.scad> + +screws = [M3_hex_screw, M4_hex_screw]; + +module do_screw_knob(screw) + if($preview) + screw_knob_assembly(screw, 16); + else + screw_knob(screw); + +module screw_knobs() + for(i = [0 : len(screws) - 1]) + translate([i * 30, 0]) + do_screw_knob(screws[i]); + +screw_knobs(); diff --git a/tests/screws.scad b/tests/screws.scad new file mode 100644 index 0000000..37b22be --- /dev/null +++ b/tests/screws.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +include <../vitamins/screws.scad> + +module screws() +for(y = [0 : len(screw_lists) -1]) + for(x = [0 : len(screw_lists[y]) -1]) { + screw = screw_lists[y][x]; + if(screw) { + length = screw_max_thread(screw) + ? screw_longer_than(screw_max_thread(screw) + 5) + : screw_head_type(screw) == hs_grub ? 6 : 30; + translate([x * 20, y * 20]) + screw(screw, length); + } + } + +if($preview) + screws(); diff --git a/tests/sealing_strip.scad b/tests/sealing_strip.scad new file mode 100644 index 0000000..215f43b --- /dev/null +++ b/tests/sealing_strip.scad @@ -0,0 +1,26 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../vitamins/sealing_strip.scad> + +module sealing_strip_test() + sealing_strip(100); + +if($preview) + sealing_strip_test(); diff --git a/tests/sector.scad b/tests/sector.scad new file mode 100644 index 0000000..da05bb1 --- /dev/null +++ b/tests/sector.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../global_defs.scad> +use <../utils/sector.scad> + + +module sectors() { + linear_extrude(height = eps) + sector(50, 45, 180); +} + +rotate([70, 0, 315]) sectors(); diff --git a/tests/sheets.scad b/tests/sheets.scad new file mode 100644 index 0000000..c7fcdbb --- /dev/null +++ b/tests/sheets.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/sheets.scad> + +width = 30; + +module sheets() + layout([for(s = sheets) width], 5) + render_sheet(sheets[$i]) sheet(sheets[$i], width, width, 2); + +if($preview) + sheets(); diff --git a/tests/socket_box.scad b/tests/socket_box.scad new file mode 100644 index 0000000..851b8d0 --- /dev/null +++ b/tests/socket_box.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +$explode = 1; +include <../core.scad> +use <../utils/layout.scad> +include <../vitamins/mains_sockets.scad> +use <../socket_box.scad> +module socket_boxes() + layout([for(s = mains_sockets) mains_socket_width(s)], 20) + if($preview) + socket_box_fastened_assembly(mains_sockets[$i], 3); + else + socket_box_stl(mains_sockets[$i]); + +socket_boxes(); diff --git a/tests/spades.scad b/tests/spades.scad new file mode 100644 index 0000000..b76c8a6 --- /dev/null +++ b/tests/spades.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/spades.scad> + +module spades() + layout([for(s = spades) spade_w(s)], 5) + spade(spades[$i], 10); + +if($preview) + spades(); diff --git a/tests/sphere.scad b/tests/sphere.scad new file mode 100644 index 0000000..d4b531f --- /dev/null +++ b/tests/sphere.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> + +r = 20; + +verts = [4, 8, 12, 16, 20, 24, 28, 32]; + +module spheres() + for(i = [0: len(verts)-1], $fn =verts[i]) + translate([i * 2 * r, 0]) + sphere(r); + + +spheres(); diff --git a/tests/spools.scad b/tests/spools.scad new file mode 100644 index 0000000..d39c246 --- /dev/null +++ b/tests/spools.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/spools.scad> + +module spools() + layout([for(s = spools) spool_height(s)], 100) + rotate([90, 0, 90]) + spool(spools[$i]); + +if($preview) + spools(); diff --git a/tests/springs.scad b/tests/springs.scad new file mode 100644 index 0000000..8d9c8ee --- /dev/null +++ b/tests/springs.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/springs.scad> + +module springs() + layout([for(s = springs) spring_od2(s)], 5) + comp_spring(springs[$i]); + +if($preview) + springs(); diff --git a/tests/ssrs.scad b/tests/ssrs.scad new file mode 100644 index 0000000..8636cce --- /dev/null +++ b/tests/ssrs.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/ssrs.scad> + +module ssrs() + layout([for(s = ssrs) ssr_length(s)], 15) + ssr_assembly(ssrs[$i], M4_cap_screw, 3); + +if($preview) + ssrs(); diff --git a/tests/stepper_motors.scad b/tests/stepper_motors.scad new file mode 100644 index 0000000..ef13bc1 --- /dev/null +++ b/tests/stepper_motors.scad @@ -0,0 +1,34 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/stepper_motors.scad> + +module stepper_motors() + layout([for(s = stepper_motors) NEMA_width(s)], 5) { + rotate(180) + NEMA(stepper_motors[$i]); + + NEMA_screws(stepper_motors[$i], M3_pan_screw); + } + +if($preview) + stepper_motors(); diff --git a/tests/strap_handle.scad b/tests/strap_handle.scad new file mode 100644 index 0000000..d14aa4b --- /dev/null +++ b/tests/strap_handle.scad @@ -0,0 +1,34 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../strap_handle.scad> + +length = 150; + +module strap_handles() + if($preview) + strap_assembly(length); + else { + strap(length); + + translate([0, 30]) + strap_end(); + } + +strap_handles(); diff --git a/tests/sweep.scad b/tests/sweep.scad new file mode 100644 index 0000000..900199f --- /dev/null +++ b/tests/sweep.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/sweep.scad> +use <../utils/maths.scad> +use <../utils/bezier.scad> + +L_points = [[0, 5, 0], [ 2, 5, 0], [2, 2, 0], [10, 2, 0], [10, 0, 0], [0, 0, 0]]; + +rad = 90; +loop = circle_points(rad, $fn = 180); + +loop_x = transform_points(loop, rotate([90, -90, $t * 360])); + +loop_y = transform_points(loop, rotate([0, -90, $t * 360])); + +loop_z = transform_points(loop, rotate([$t * 360, 0, 0])); + +sweep(loop_z, L_points, loop = true); + +sweep(loop_x, L_points, loop = true); + +sweep(loop_y, L_points, loop = true); + + +knot = [ for(i=[0:.2:359]) + [ (19*cos(3*i) + 40)*cos(2*i), + (19*cos(3*i) + 40)*sin(2*i), + 19*sin(3*i) ] ]; + +sweep(knot, L_points, loop = true, twist = 0); + +p = transform_points([[0,0,0], [20,0,5], [10,30,4], [0,0,0], [0,0,20]], scale(10)); +n = 100; +path = bezier_path(p, n); + +rotate(45) sweep(path, circle_points(5, $fn = 64)); diff --git a/tests/teardrops.scad b/tests/teardrops.scad new file mode 100644 index 0000000..9d62cd6 --- /dev/null +++ b/tests/teardrops.scad @@ -0,0 +1,43 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../core.scad> + +module teardrops() { + color(pp1_colour) + rotate([90, 0, -45]) + linear_extrude(height = 3) + difference() { + square(40); + + translate([10, 10]) + teardrop(h = 0, r = 3); + + translate([10, 20]) + teardrop_plus(h = 0, r = 3); + + translate([20, 30]) + tearslot(h = 0, r = 3, w = 10); + + translate([30, 15]) + vertical_tearslot(h = 0, r =3, l = 10); + } +} + +teardrops(); diff --git a/tests/toggles.scad b/tests/toggles.scad new file mode 100644 index 0000000..f87199d --- /dev/null +++ b/tests/toggles.scad @@ -0,0 +1,30 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/toggles.scad> + +module toggles() + layout([for(t = toggles) toggle_width(t)], 16) + toggle(toggles[$i], 3); + +if($preview) + toggles(); diff --git a/tests/transformers.scad b/tests/transformers.scad new file mode 100644 index 0000000..19db222 --- /dev/null +++ b/tests/transformers.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/transformers.scad> + +module transformers() + layout([for(t = transformers) tx_depth(t)], 10) + rotate(90) + transformer(transformers[$i]); + +if($preview) + transformers(); diff --git a/tests/tube.scad b/tests/tube.scad new file mode 100644 index 0000000..6e7745d --- /dev/null +++ b/tests/tube.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include <../global_defs.scad> +use <../utils/tube.scad> + + +module tubes() { + linear_extrude(height = eps) + ring(10, 8); + + translate([50, 10]) + tube(10, 8, 30); +} + +tubes(); diff --git a/tests/tubings.scad b/tests/tubings.scad new file mode 100644 index 0000000..77e6513 --- /dev/null +++ b/tests/tubings.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/tubings.scad> + +module tubings() + layout([for(t = tubings) tubing_od(t)], 10) + tubing(tubings[$i]); + +if($preview) + tubings(); diff --git a/tests/variacs.scad b/tests/variacs.scad new file mode 100644 index 0000000..65c7cf6 --- /dev/null +++ b/tests/variacs.scad @@ -0,0 +1,31 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +include <../vitamins/variacs.scad> + +module variacs() + layout([for(v = variacs) 2 * (variac_bulge_dia(v) - variac_diameter(v) / 2)], 25) + rotate(-90) + variac(variacs[$i]); + +if($preview) + variacs(); diff --git a/tests/veroboard.scad b/tests/veroboard.scad new file mode 100644 index 0000000..9deee5b --- /dev/null +++ b/tests/veroboard.scad @@ -0,0 +1,48 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/screws.scad> +use <../vitamins/veroboard.scad> + +z_cable_ways = 20; + +z_vb = ["z_vb", "z_bed_terminal", 5, z_cable_ways / 2 + 12, inch(0.1), false, M3_dome_screw, + [[2,2],[2,-3]], [], [5, 7, 9], + [ + [3, z_cable_ways / 4 + 5.5, 0, "term254", z_cable_ways / 2, [1, 3]], + [0.5, z_cable_ways / 4 + 5.5, 90, "transition", z_cable_ways / 2, [1, 3]], + ], + [ + [[0,1,3], 6], [[0,1,3], 8], [[0,1,3],[10 : 6 + z_cable_ways / 2 - 1]], + ] + ]; + +module veroboard_test() translate([vero_length(z_vb) / 2, vero_width(z_vb) / 2]) { + vflip() + veroboard_assembly(z_vb, 12, 3); + + translate([30, 0]) + rotate(180) + veroboard_assembly(z_vb, 12, 3); +} + +if($preview) + veroboard_test(); diff --git a/tests/washers.scad b/tests/washers.scad new file mode 100644 index 0000000..a18ecb4 --- /dev/null +++ b/tests/washers.scad @@ -0,0 +1,47 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/washers.scad> + +function penny_diameter(w) = let(p = penny_washer(w)) washer_diameter(p ? p : w); + +module washers() + layout([for(w = washers) penny_diameter(w)], 2) let(w = washers[$i]) { + star_washer(w); + + if(spring_washer_thickness(w)) + translate([0, 20]) + let($explode = 1) + spring_washer(w); + + translate([0, 40]) + washer(w); + + if(penny_washer(w)) + translate([0, 65]) + penny_washer(w); + + translate([0, 90]) + printed_washer(w); + } + +if($preview) + washers(); diff --git a/tests/zipties.scad b/tests/zipties.scad new file mode 100644 index 0000000..cafb5cd --- /dev/null +++ b/tests/zipties.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +use <../utils/layout.scad> + +include <../vitamins/zipties.scad> + +module zipties() + layout([for(z = zipties) 9], 10) + ziptie(zipties[$i], 5); + +if($preview) + zipties(); diff --git a/times.txt b/times.txt new file mode 100644 index 0000000..313fdb2 --- /dev/null +++ b/times.txt @@ -0,0 +1,92 @@ +{ + "tests/annotation.scad": 2.104, + "tests/ball_bearings.scad": 2.148, + "tests/batteries.scad": 3.81, + "tests/belts.scad": 3.212, + "tests/bezier.scad": 5.746, + "tests/blowers.scad": 2.133, + "tests/box.scad": 14.312, + "tests/bulldogs.scad": 2.465, + "tests/buttons.scad": 2.261, + "tests/butt_box.scad": 8.325, + "tests/cable_grommets.scad": 2.128, + "tests/cable_strips.scad": 2.148, + "tests/carriers.scad": 2.259, + "tests/clip.scad": 5.751, + "tests/components.scad": 3.266, + "tests/corner_block.scad": 17.288, + "tests/displays.scad": 1.936, + "tests/dogbones.scad": 1.543, + "tests/door_hinge.scad": 1.622, + "tests/door_latch.scad": 3.21, + "tests/d_connectors.scad": 1.743, + "tests/fans.scad": 1.963, + "tests/fan_guard.scad": 1.861, + "tests/fillet.scad": 1.468, + "tests/fixing_block.scad": 4.495, + "tests/foot.scad": 1.54, + "tests/fuseholder.scad": 1.629, + "tests/global.scad": 1.445, + "tests/handle.scad": 2.211, + "tests/hanging_hole.scad": 2.239, + "tests/hot_ends.scad": 2.875, + "tests/iecs.scad": 1.608, + "tests/inserts.scad": 1.462, + "tests/jack.scad": 2.241, + "tests/layout.scad": 1.708, + "tests/leadnuts.scad": 1.534, + "tests/leds.scad": 1.514, + "tests/light_strips.scad": 1.678, + "tests/linear_bearings.scad": 1.578, + "tests/maths.scad": 2.017, + "tests/meter.scad": 2.135, + "tests/microswitches.scad": 1.77, + "tests/microview.scad": 2.88, + "tests/modules.scad": 2.25, + "tests/nuts.scad": 1.933, + "tests/offset.scad": 12.661, + "tests/opengrab.scad": 1.69, + "tests/o_ring.scad": 1.826, + "tests/pcbs.scad": 5.944, + "tests/pillars.scad": 1.908, + "tests/polyholes.scad": 3.002, + "tests/psus.scad": 6.242, + "tests/pulleys.scad": 5.971, + "tests/quadrant.scad": 2.113, + "tests/rails.scad": 2.865, + "tests/ribbon_clamp.scad": 5.174, + "tests/ring_terminals.scad": 2.097, + "tests/rockers.scad": 1.49, + "tests/rod.scad": 2.082, + "tests/round.scad": 352.238, + "tests/rounded_cylinder.scad": 2.155, + "tests/rounded_polygon.scad": 1.951, + "tests/rounded_rectangle.scad": 1.86, + "tests/screws.scad": 2.783, + "tests/screw_knob.scad": 4.461, + "tests/sealing_strip.scad": 1.709, + "tests/sector.scad": 2.029, + "tests/sheets.scad": 1.907, + "tests/socket_13A.scad": 9.275, + "tests/spades.scad": 2.28, + "tests/spheres.scad": 1.569, + "tests/spools.scad": 2.278, + "tests/springs.scad": 3.446, + "tests/ssrs.scad": 3.371, + "tests/stepper_motors.scad": 3.43, + "tests/strap_handle.scad": 3.182, + "tests/sweep.scad": 4.344, + "tests/teardrops.scad": 2.764, + "tests/toggles.scad": 3.929, + "tests/transformers.scad": 2.852, + "tests/tube.scad": 2.773, + "tests/tubings.scad": 3.01, + "tests/variacs.scad": 3.045, + "tests/veroboard.scad": 3.791, + "tests/washers.scad": 3.706, + "tests/zipties.scad": 2.501, + "tests/bom.scad": 2.328, + "tests/sphere.scad": 2.092, + "tests/socket_box.scad": 21.617, + "tests/mains_sockets.scad": 15.204 +} \ No newline at end of file diff --git a/utils/annotation.scad b/utils/annotation.scad new file mode 100644 index 0000000..1ca4a7c --- /dev/null +++ b/utils/annotation.scad @@ -0,0 +1,40 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include <../core.scad> +// +//! Annotation used in this documentation + +module label(str, scale = 0.25, valign = "baseline", halign = "left") //! Draw text that always faces the camera + color("black") + %rotate($vpr != [0, 0, 0] ? $vpr : [70, 0, 315]) + linear_extrude(height = eps) + scale(scale) + text(str, valign = valign, halign = halign); + +module arrow(length = 20) { //! Draw an arrow that faces downwards + d = length / 20; + head_r = 1.5 * d; + + color("grey") %union() { + translate_z(head_r) + cylinder(d = d, h = length - head_r, $fn = 32); + + cylinder(r1 = 0, r2 = head_r, h = head_r); + } +} diff --git a/utils/bezier.scad b/utils/bezier.scad new file mode 100644 index 0000000..711f23a --- /dev/null +++ b/utils/bezier.scad @@ -0,0 +1,57 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Bezier curves and function to get and adjust the length or minimum z point. +// +include <../global_defs.scad> + +function bezier(t, v) = //! Returns a point at distance ```t``` [0 - 1] along the curve with control points ```v``` + (len(v) > 2) ? bezier(t, [for (i = [0 : len(v) - 2]) v[i] * (1 - t) + v[i + 1] * (t)]) + : v[0] * (1 - t) + v[1] * (t); + +function bezier_path(v, steps = 100) = //! Returns a Bezier path from control points ```v``` with ```steps``` segments + [for(i = [0 : steps], t = i / steps) bezier(t, v)]; + +function bezier_length(v, delta = 0.01, t = 0, length = 0) = //! Calculate the length of a Bezier curve from control points ```v``` + t > 1 ? length + : bezier_length(v, delta, t + delta, length + norm(bezier(t, v) - bezier(t + delta, v))); + +function adjust_bezier(v, r) = + let(extension = (v[1] - v[0]) * (r - 1)) + [v[0], v[1] + extension, v[2] + extension, v[3]]; + +function adjust_bezier_length(v, l, eps = 0.001, r1 = 1.0, r2 = 1.5, l1, l2) = //! Adjust Bezier control points ```v``` to get the required curve length ```l``` + let(l1 = l1 != undef ? l1 : bezier_length(adjust_bezier(v, r1)), + l2 = l2 != undef ? l2 : bezier_length(adjust_bezier(v, r2)) + ) abs(l1 - l) < eps ? adjust_bezier(v, r1) + : let(r = r1 + (l - l1) * (r2 - r1) / (l2 - l1)) + abs(r - r1) < abs(r - r2) ? adjust_bezier_length(v, l, eps, r, r1, undef, l1) + : adjust_bezier_length(v, l, eps, r, r2, undef, l2); + +function bezier_min_z(v, steps = 100, z = inf, i = 0) = //! Calculate the minimum z coordinate of a Bezier curve from control points ```v``` + i <= steps ? bezier_min_z(v, steps, min(z, bezier(i / steps, v).z), i + 1) : z; + +function adjust_bezier_z(v, z, eps = 0.001, r1 = 1, r2 = 1.5, z1, z2) = //! Adjust Bezier control points ```v``` to get the required minimum ```z``` + let(z1 = z1 != undef ? z1 : bezier_min_z(adjust_bezier(v, r1)), + z2 = z2 != undef ? z2 : bezier_min_z(adjust_bezier(v, r2)) + ) abs(z1 - z) < eps ? adjust_bezier(v, r1) + : let(r = r1 + (z - z1) * (r2 - r1) / (z2 - z1)) + abs(r - r1) < abs(r - r2) ? adjust_bezier_z(v, z, eps, r, r1, undef, z1) + : adjust_bezier_z(v, z, eps, r, r2, undef, z2); diff --git a/utils/core/bom.scad b/utils/core/bom.scad new file mode 100644 index 0000000..bf080e1 --- /dev/null +++ b/utils/core/bom.scad @@ -0,0 +1,112 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Bill Of Materials generation via echo and the ```bom.py``` script. Also handles exploded assembly views and posing. Assembly instructions can precede the module +//! definition that makes the assembly. +//! +//! The example below shows how to define a vitamin and incorporate it into an assembly with sub-assemblies and make an exploded view. The resulting flat BOM is shown but +//! heirachical BOMs are also generated for real projects. +// +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]; + if($exploded && is_undef($exploded_parent)) { + translate(o) // Draw the line first in case the child is transparent + hull() { + sphere(0.2); + + translate(v * $exploded) + sphere(0.2); + } + + translate(v * $exploded) + let($exploded_parent = explode_children ? undef : true) + children(); + } + else + 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 assembly(name) { //! Name an assembly that will appear on the BOM, there needs to a module named ```_assembly``` to make it + if(bom_mode()) + echo(str("~", name, "_assembly{")); + + no_pose() + if(is_undef($child_assembly)) + let($child_assembly = true) + children(); + else + no_explode() + children(); + + if(bom_mode()) + echo(str("~}", name, "_assembly")); +} + +module stl(name) { //! Name an stl that will appear on the BOM, there needs to a module named ```_stl``` to make it + if(bom_mode()) + echo(str("~", name, ".stl")); +} + +module dxf(name) { //! Name a dxf that will appear on the BOM, there needs to a module named ```_dxf``` to make it + if(bom_mode()) + echo(str("~", name, ".dxf")); +} + +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 + +function arg(value, default, name = "") = //! Create string for arg if not default, helper for ```vitamin()``` + value == default ? "" + : name ? str(", ", name, " = ", value_string(value)) + : str(", ", value_string(value)); + +module vitamin(description) { //! Describe a vitamin for the BOM entry and precede it with a module call that creates it, eg. "wigit(42): Type 42 widget" + if(bom_mode(2)) + echo(str("~", description, !is_undef($hidden) ? " - not shown" : "")); +} + +module 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 + let($on_bom = on) + children(); + +module hidden() //! Make item invisible, except on the BOM + scale(1 / sqr(1024)) + let($hidden = true) + children(); diff --git a/utils/core/clip.scad b/utils/core/clip.scad new file mode 100644 index 0000000..1b526d2 --- /dev/null +++ b/utils/core/clip.scad @@ -0,0 +1,49 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Construct arbirarily large box to partition 3D space and clip objects, useful for creating cross sections to see the inside when debugging. +//! +//! Original version by Doug Moen on the OpenSCAD forum +// +// +module box(xmin, ymin, zmin, xmax, ymax, zmax) //! Construct a box given its bounds + polyhedron( + [[xmin, ymin, zmin], // 0 + [xmin, ymin, zmax], // 1 + [xmin, ymax, zmin], // 2 + [xmin, ymax, zmax], // 3 + [xmax, ymin, zmin], // 4 + [xmax, ymin, zmax], // 5 + [xmax, ymax, zmin], // 6 + [xmax, ymax, zmax]], // 7 + [[7,5,1,3], // top + [2,0,4,6], // bottom + [5,4,0,1], // front + [3,2,6,7], // back + [5,7,6,4], // right + [0,2,3,1]] // left + ); + +module clip(xmin = -inf, ymin = -inf, zmin = -inf, xmax = inf, ymax = inf, zmax = inf) //! Clip child to specified boundaries + render() intersection() { + children(); + + box(xmin, ymin, zmin, xmax, ymax, zmax); + } diff --git a/utils/core/global.scad b/utils/core/global.scad new file mode 100644 index 0000000..32b371c --- /dev/null +++ b/utils/core/global.scad @@ -0,0 +1,64 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Global constants, functions and modules. This file is used directly or indirectly in every scad file. +// +include <../../global_defs.scad> + +function sqr(x) = x * x; //! Returns the square of ```x``` +function inch(x) = x * 25.4; //! Inch to mm conversion +function echoit(x) = echo(x) x; //! Echo expression and return it, useful for debugging +function in(list, x) = !!len([for(v = list) if(v == x) true]); //! Returns true if ```x``` is an element in the ```list``` +function Len(x) = is_list(x) ? len(x) : 0; //! Returns the length of a list or 0 if ```x``` is not a list +function r2sides(r) = $fn ? $fn : ceil(max(min(360/ $fa, r * 2 * PI / $fs), 5)); //! Replicates the OpenSCAD logic to calculate the number of sides from the radius +function r2sides4n(r) = floor((r2sides(r) + 3) / 4) * 4; //! Round up the number of sides to a multiple of 4 to ensure points land on all axes + +module translate_z(z) translate([0, 0, z]) children(); //! Shortcut for Z only translations +module vflip() rotate([180, 0, 0]) children(); //! Invert children by doing a 180 flip around the X axis +module ellipse(xr, yr) scale([1, yr / xr]) circle4n(xr); //! Draw an ellipse + +module extrude_if(h, center = true) //! Extrudes 2D object to 3D when ```h``` is nonzero, otherwise leaves it 2D + if(h) + linear_extrude(height = h, center = center) // 3D + children(); + else + children(); // 2D + +module circle4n(r, d = undef) { //! Circle with multiple of 4 vertices + R = is_undef(d) ? r : d / 2; + circle(R, $fn = r2sides4n(R)); +} + +module semi_circle(r, d = undef) //! A semi circle in the positive Y domain + intersection() { + R = is_undef(d) ? r : d / 2; + circle4n(R); + + sq = R + 1; + translate([-sq, 0]) + square([2 * sq, sq]); + } + +include +include +include +include +include +include diff --git a/utils/core/polyholes.scad b/utils/core/polyholes.scad new file mode 100644 index 0000000..e57b087 --- /dev/null +++ b/utils/core/polyholes.scad @@ -0,0 +1,79 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! A method of making 3D printed holes come out the right size regardless of the printer, providing +//! it gets the linear dimensions right. See +//! +//! The module provides `poly_circle()`, `poly_cylinder()` and `poly_ring()` that is useful for making printed washers and pillars. +// +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 + +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_cylinder(r, h, center = false, sides = 0) //! Make a cylinder adjusted to print the correct size + extrude_if(h, center) + poly_circle(r, sides); + +module poly_ring(or, ir) { //! Make a 2D ring adjusted to have the correct internal radius + cir = corrected_radius(ir); + filaments = floor((or - cir) / extrusion_width); + if(filaments > 3) + difference() { + circle(or); + + poly_circle(ir); + } + else + if(filaments >= 2) + difference() { + offset(or - cir) + poly_circle(ir); + + poly_circle(ir); + } + else + difference() { + poly_circle(or); + + offset(-squeezed_wall) + poly_circle(or); + } +} + +module drill(r, h = 100) //! Make a cylinder for drilling holes suitable for CNC routing, set h = 0 for circle + extrude_if(h) + circle(r = corrected_radius(r, r2sides(r))); +// +// Horizontal slot +// +module slot(r, l, h = 100) //! Make a horizontal slot suitable for CNC routing, set h = 0 for 2D version + extrude_if(h) + hull() { + translate([l / 2,0]) + drill(r, 0); + + translate([-l / 2,0]) + drill(r, 0); + } diff --git a/utils/core/rounded_rectangle.scad b/utils/core/rounded_rectangle.scad new file mode 100644 index 0000000..123c5d1 --- /dev/null +++ b/utils/core/rounded_rectangle.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Rectangle with rounded corners. +// +module rounded_square(size, r, center = true) //! Like ```square()``` but with with rounded corners +{ + $fn = r2sides4n(r); + offset(r) offset(-r) square(size, center = center); +} + +module rounded_rectangle(size, r, center = true, xy_center = true) //! Like ```cube()``` but corners rounded in XY plane and separate centre options for xy and z. +{ + linear_extrude(height = size[2], center = center) + rounded_square([size[0], size[1]], r, xy_center); +} diff --git a/utils/core/sphere.scad b/utils/core/sphere.scad new file mode 100644 index 0000000..31459c4 --- /dev/null +++ b/utils/core/sphere.scad @@ -0,0 +1,28 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +//! Redefines `sphere()` to always have a vertex on all six half axes I.e. vertices at the poles and the equator and `$fn` a multiple of four. +//! This ensures `hull` and `minkowski` results have the correct dimensions when spheres are placed at the corners. + +module sphere(r = 1, d = undef) { //! Override ```sphere``` so that has vertices on all three axes. Has the advantage of giving correct dimensions when hulled + R = is_undef(d) ? r : d / 2; + rotate_extrude($fn = r2sides4n(R)) + rotate(-90) + semi_circle(R); +} diff --git a/utils/core/teardrops.scad b/utils/core/teardrops.scad new file mode 100644 index 0000000..74821a0 --- /dev/null +++ b/utils/core/teardrops.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! For making horizontal holes that don't need support material. +//! Small holes can get away without it, but they print better with truncated teardrops. +// +module teardrop(h, r, center = true, truncate = true) //! For making horizontal holes that don't need support material, set ```truncate = false``` to make traditional RepRap teardrops that don't even need bridging + render(convexity = 5) + extrude_if(h, center) + hull() { + circle4n(r); + + if(truncate) + translate([0, r / 2]) + square([2 * r * (sqrt(2) - 1), r], center = true); + else + polygon([[0, 0], [eps, 0], [0, r * sqrt(2)]]); + } + +module teardrop_plus(h, r, center = true, truncate = true) //! Slightly bigger teardrop to allow for the 3D printing staircase effect + teardrop(h, r + layer_height / 4, center, truncate); + +module tearslot(h, r, w, center = true) //! A horizontal slot that doesn't need support material + extrude_if(h, center) + hull() { + translate([-w/2,0,0]) teardrop(r = r, h = 0); + translate([ w/2,0,0]) teardrop(r = r, h = 0); + } + +module vertical_tearslot(h, r, l, center = true) //! A vertical slot that doesn't need support material + extrude_if(h, center) + hull() { + translate([0, l / 2]) teardrop(0, r, true); + translate([0, -l / 2]) + circle4n(r); + } diff --git a/utils/dogbones.scad b/utils/dogbones.scad new file mode 100644 index 0000000..3231fe4 --- /dev/null +++ b/utils/dogbones.scad @@ -0,0 +1,46 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! When square holes are cut with a CNC bit they get rounded corners. If it is important that +//! a square cornered part fits in the hole then circles are placed in the corners making a bone shape. +// +include <../core.scad> + +module dogbone_square(size, r = cnc_bit_r, center = true) //! Square with circles at the corners +{ + union() { + square(size, center = center); + + if(r > 0) { + origin = center ? [0, 0] : size / 2; + offset = r / sqrt(2); + + for(x = [-1, 1], y = [-1, 1]) + translate(origin + [x * (size.x / 2 - offset), y * (size.y / 2 - offset)]) + drill(r, 0); + } + } +} + +module dogbone_rectangle(size, r = cnc_bit_r, center = true, xy_center = true) //! Rectangle with cylinders at the corners +{ + extrude_if(h = size.z, center = center) + dogbone_square([size.x, size.y], r, xy_center); +} diff --git a/utils/fillet.scad b/utils/fillet.scad new file mode 100644 index 0000000..99359c2 --- /dev/null +++ b/utils/fillet.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Rounded fillet for adding to corners. +// +include <../core.scad> + +module fillet(r, h, center = false) //! Fillet with specified radius and height + extrude_if(h, center = center) + difference() { + translate([-eps, -eps, 0]) + square(r + eps); + + circle(r + eps); + } diff --git a/utils/hanging_hole.scad b/utils/hanging_hole.scad new file mode 100644 index 0000000..cb456be --- /dev/null +++ b/utils/hanging_hole.scad @@ -0,0 +1,64 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Method to print holes in mid air. See +// +include <../core.scad> + +module hanging_hole(z, ir, h = 100, h2 = 100) { //! Hole radius ```ir``` hanging at the specified ```z``` value above a void who's shape is given by a 2D child + module polyhole(r, h, n = 8) { + if(h > 0) + rotate(180 / n) { + poly_cylinder(r = r, h = layer_height, sides = n); + + translate_z(layer_height) + if(2 * n <= sides(r)) + polyhole(r - eps, h - layer_height, n * 2); + else + poly_cylinder(r - eps, h - layer_height); + } + } + assert(z % layer_height == 0, str(z)); + infill_angle = z % (2 * layer_height) ? -45 : 45; + below = min(z + eps, h2); + big = 1000; + + render(convexity = 3) translate_z(z) + union() { + translate_z(2 * layer_height) + polyhole(ir - eps, h - 2 * layer_height); + + difference() { + translate_z(-below) + linear_extrude(height = below + 2 * layer_height) + children(); + + rotate(infill_angle) + for(side = [-1, 1]) { + translate([side * (ir + big), 0, big + layer_height]) + cube(2 * big, center = true); + + translate([0, side * (ir + big), big]) + cube(2 * big, center = true); + + } + } + } +} diff --git a/utils/layout.scad b/utils/layout.scad new file mode 100644 index 0000000..8945427 --- /dev/null +++ b/utils/layout.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Layout objects in a line with equal gaps given a vector of their widths. +// +include <../global_defs.scad> + +function layout_offset(widths, i, gap = 2) = //! Calculate the offset for the ```i```th item + i == 0 ? widths[0] / 2 + : layout_offset(widths, i - 1, gap) + widths[i - 1] / 2 + gap + widths[i] / 2; + +module layout(widths, gap = 2, no_offset = false) //! Layout children passing ```$i``` + translate([no_offset ? -widths[0] / 2 : 0, 0]) + for($i = [0 : len(widths) - 1]) + translate([layout_offset(widths, $i, gap), 0]) + children(); diff --git a/utils/maths.scad b/utils/maths.scad new file mode 100644 index 0000000..97b6bb2 --- /dev/null +++ b/utils/maths.scad @@ -0,0 +1,76 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Maths utilities for minapulating vectors and matrices. +// +function sqr(x) = x * x; + +function translate(v) = let(u = is_list(v) ? len(v) == 2 ? [v.x, v.y, 0] //! Generate a 4x4 translation matrix, ```v``` can be ```[x, y]```, ```[x, y, z]``` or ```z``` + : v + : [0, 0, v]) + [ [1, 0, 0, u.x], + [0, 1, 0, u.y], + [0, 0, 1, u.z], + [0, 0, 0, 1] ]; + +function rotate(a, v) = //! Generate a 4x4 rotation matrix, ```a``` can be a vector of three angles or a single angle around ```z```, or around axis ```v``` + is_undef(v) ? let(av = is_list(a) ? a : [0, 0, a], + cx = cos(av[0]), + cy = cos(av[1]), + cz = cos(av[2]), + sx = sin(av[0]), + sy = sin(av[1]), + sz = sin(av[2])) + [ + [ cy * cz, cz * sx * sy - cx * sz, cx * cz * sy + sx * sz, 0], + [ cy * sz, cx * cz + sx * sy * sz,-cz * sx + cx * sy * sz, 0], + [-sy, cy * sx, cx * cy, 0], + [ 0, 0, 0, 1] + ] + : let(s = sin(a), + c = cos(a), + C = 1 - c, + m = sqr(v.x) + sqr(v.y) + sqr(v.z), // m used instead of norm to avoid irrational roots as much as possible + u = v / sqrt(m)) + [ + [ C * v.x * v.x / m + c, C * v.x * v.y / m - u.z * s, C * v.x * v.z / m + u.y * s, 0], + [ C * v.y * v.x / m + u.z * s, C * v.y * v.y / m + c, C * v.y * v.z / m - u.x * s, 0], + [ C * v.z * v.x / m - u.y * s, C * v.z * v.y / m + u.x * s, C * v.z * v.z / m + c, 0], + [ 0, 0, 0, 1] + ]; + +function scale(v) = let(s = is_list(v) ? v : [v, v, v]) //! Generate a 4x4 matrix that scales by ```v```, which can be a vector of xyz factors or a scalar to scale all axes equally + [ + [s.x, 0, 0, 0], + [0, s.y, 0, 0], + [0, 0, s.z, 0], + [0, 0, 0, 1] + ]; + +function vec3(v) = [v.x, v.y, v.z]; //! Return a 3 vector with the first three elements of ```v``` +function transform(v, m) = vec3(m * [v.x, v.y, v.z, 1]); //! Apply 4x4 transform to a 3 vector by extending it and cropping it again +function transform_points(path, m) = [for(p = path) transform(p, m)]; //! Apply transform to a path +function unit(v) = let(n = norm(v)) n ? v / n : v; //! Convert ```v``` to a unit vector + +function transpose(m) = [ for(j = [0 : len(m[0]) - 1]) [ for(i = [0 : len(m) - 1]) m[i][j] ] ]; //! Transpose an arbitrary size matrix + +function identity(n, x = 1) = [for(i = [0 : n - 1]) [for(j = [0 : n - 1]) i == j ? x : 0] ]; //! Construct an arbitrary size identity matrix + +function reverse(v) = let(n = len(v) - 1) n < 0 ? [] : [for(i = [0 : n]) v[n - i]]; //! Reverse a vector diff --git a/utils/offset.scad b/utils/offset.scad new file mode 100644 index 0000000..452cf7b --- /dev/null +++ b/utils/offset.scad @@ -0,0 +1,63 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +//! 3D offset using `minkowski` with a `sphere`, so very slow if `$fn` is not kept small. The offset can be positive or negative. +//! +//! Can be used to round corners. Positive offsets will round convex corners, negative offsets round concave corners. To round both use [`round_3D()`](#round). +//! +//! If `chamfer_base` is true then the bottom edge is made suitable for 3D printing by chamfering when the angle gets shallower than 45 degrees. +include <../core.scad> + +module offset_3D(r, chamfer_base = false) { //! Offset 3D shape by specified radius ```r```, positive or negative. + module ball(r) + if(chamfer_base) + rotate_extrude() + intersection() { + rotate(180) + teardrop(0, r); + + translate([0, -r]) + square([r, 2 * r]); + } + else + sphere(r); + + if(r > 0) + minkowski() { + children(); + + ball(r); + } + else + if(r < 0) + render() difference() { + cube(inf / 2, center = true); + + minkowski() { + difference() { + cube(inf, center = true); + + children(); + } + ball(-r); + } + } + else + children(); +} diff --git a/utils/quadrant.scad b/utils/quadrant.scad new file mode 100644 index 0000000..7fe2da7 --- /dev/null +++ b/utils/quadrant.scad @@ -0,0 +1,40 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Square with one rounded corner. +// +include <../core.scad> + +module quadrant(w, r, center = false) { //! Draw a square with one rounded corner, can be centered on the arc centre, when ```center``` is ```true```. + offset = center ? r - w : 0; + translate([offset, offset]) + hull() { + intersection() { + translate([w - r, w - r]) + circle4n(r); + + square(w); + } + + square([w, eps]); + + square([eps, w]); + } +} diff --git a/utils/round.scad b/utils/round.scad new file mode 100644 index 0000000..bab60aa --- /dev/null +++ b/utils/round.scad @@ -0,0 +1,44 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +//! Round 2D shapes uisng `offset()`, which is fast and 3D shapes with [`offset_3D()`](#offset), which is very slow. +//! +//! A single radius can be specified or separate internal and external radii. +//! If `chamfer_base` is `true` for `round_3D()` then the bottom edge is made suitable for 3D printing by chamfering once the +//! the angle gets shallower than 45 degrees. +include <../core.scad> +use + +module round(r, ir = undef, or = undef) { //! Round a 2D child, single radius or separate inside and outside radii + IR = is_undef(ir) ? r : ir; + OR = is_undef(or) ? r : or; + offset(OR) + offset(-OR -IR) + offset(IR) + children(); +} + +module round_3D(r, ir = undef, or = undef, chamfer_base = false) { //! Round a 3D child single radius or separate inside and outside radii + IR = is_undef(ir) ? r : ir; + OR = is_undef(or) ? r : or; + offset_3D(OR, chamfer_base) + offset_3D(-OR -IR, chamfer_base) + offset_3D(IR, chamfer_base) + children(); +} diff --git a/utils/rounded_cylinder.scad b/utils/rounded_cylinder.scad new file mode 100644 index 0000000..caccf55 --- /dev/null +++ b/utils/rounded_cylinder.scad @@ -0,0 +1,47 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Cylinder with a rounded end. +// +include <../core.scad> + +module rounded_corner(r, h, r2, ir = 0) { //! 2D version + assert(ir <= r - r2); + + translate([ir , 0]) + hull() { + square([eps, h]); + + square([r - ir, eps]); + + translate([r - r2 - ir, h - r2]) + intersection() { + circle4n(r2, $fs = 0.2); + + square(r2); + } + } +} + +module rounded_cylinder(r, h, r2, ir = 0, angle = 360) //! Rounded cylinder given radius ```r```, height ```h```, optional internal radius ```ir``` and optional ```angle``` +{ + rotate_extrude(angle = angle) + rounded_corner(r, h, r2, ir); +} diff --git a/utils/rounded_polygon.scad b/utils/rounded_polygon.scad new file mode 100644 index 0000000..11a7354 --- /dev/null +++ b/utils/rounded_polygon.scad @@ -0,0 +1,93 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Draw a polygon with rounded corners. Each element of the vector is the XY coordinate and a radius. Radius can be negative for a concave corner. +//! +//! Because the tangents need to be calculated to find the length these can be calculated separately and re-used when drawing to save calculating them twice. +// +include <../core.scad> + +function circle_tangent(p1, p2) = + let( + r1 = p1[2], + r2 = p2[2], + dx = p2.x - p1.x, + dy = p2.y - p1.y, + d = sqrt(dx * dx + dy * dy), + theta = atan2(dy, dx) + acos((r1 - r2) / d), + xa = p1.x +(cos(theta) * r1), + ya = p1.y +(sin(theta) * r1), + xb = p2.x +(cos(theta) * r2), + yb = p2.y +(sin(theta) * r2) + )[ [xa, ya], [xb, yb] ]; + +function rounded_polygon_tangents(points) = //! Compute the straight sections needed to draw and to compute the lengths + let(len = len(points)) + [for(i = [0 : len - 1]) + let(ends = circle_tangent(points[i], points[(i + 1) % len])) + for(end = [0, 1]) + ends[end]]; + +function sumv(v, i = 0, sum = 0) = i == len(v) ? sum : sumv(v, i + 1, sum + v[i]); + +// the cross product of 2D vectors is the area of the parallelogram between them. We use the sign of this to decide if the angle is bigger than 180. +function rounded_polygon_length(points, tangents) = //! Calculate the length given the point list and the list of tangents computed by ``` rounded_polygon_tangents``` + let( + len = len(points), + indices = [0 : len - 1], + straights = [for(i = indices) norm(tangents[2 * i] - tangents[2 * i + 1])], + arcs = [for(i = indices) let(p1 = tangents[2 * i + 1], + p2 = tangents[(2 * i + 2) % (2 * len)], + corner = points[(i + 1) % len], + c = [corner.x, corner.y], + v1 = p1 - c, + v2 = p2 - c, + r = abs(corner.z), + a = acos((v1 * v2) / sqr(r))) PI * (cross(v1,v2) <= 0 ? a : 360 - a) * r / 180] + ) + sumv(concat(straights, arcs)); + +module rounded_polygon(points, _tangents = undef) { //! Draw the rounded polygon from the point list, can pass the tangent list to save it being calculated + len = len(points); + indices = [0 : len - 1]; + tangents = _tangents ? _tangents : rounded_polygon_tangents(points); + + difference(convexity = points) { + union() { + for(i = indices) + if(points[i][2] > 0) + hull() { + translate([points[i].x, points[i].y]) + circle(points[i][2]); + polygon([tangents[(2 * i - 1 + 2 * len) % (2 * len)], tangents[2 * i], [points[i].x, points[i].y]]); + } + + polygon(tangents, convexity = points); + } + for(i = indices) + if(points[i][2] < 0) + hull() { + translate([points[i].x, points[i].y]) + circle(-points[i][2]); + + polygon([tangents[(2 * i - 1 + 2 * len) % (2 *len)], tangents[2 * i], [points[i].x, points[i].y]]); + } + } +} diff --git a/utils/sector.scad b/utils/sector.scad new file mode 100644 index 0000000..09fc237 --- /dev/null +++ b/utils/sector.scad @@ -0,0 +1,36 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! A sector of a circle between two angles. +// +include <../core.scad> + +module sector(r, start_angle, end_angle) { //! Create specified sector given radius ```r```, ```start_angle``` and ```end_angle``` + R = r * sqrt(2) + 1; + + if(end_angle > start_angle) + intersection() { + circle4n(r); + + // A 4 triangle fan + polygon([[0, 0], + for(i = [0 : 4], a = start_angle + i * (end_angle - start_angle) / 4) R * [cos(a), sin(a)] ]); + } +} diff --git a/utils/sweep.scad b/utils/sweep.scad new file mode 100644 index 0000000..4c2672e --- /dev/null +++ b/utils/sweep.scad @@ -0,0 +1,160 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Utility to generate a polhedron by sweeping a 2D profile along a 3D path and utilities for generating paths. +//! +//! The initial orientation is the Y axis of the profile points towards the initial center of curvature, Frenet-Serret style. +//! This means the first three points must not be colinear. Subsequent rotations use the minimum rotation method. +//! +//! The path can be open or closed. If closed sweep ensures that the start and end have the same rotation to line up. +//! An additional twist around the path can be specified. If the path is closed this should be a multiple of 360. +// +include <../core.scad> + +use + +function transpose3(m) = [ [m[0].x, m[1].x, m[2].x], + [m[0].y, m[1].y, m[2].y], + [m[0].z, m[1].z, m[2].z] ]; +// +// Frenet-Serret frame +// +function fs_frame(tangents) = + let(tangent = tangents[0], + normal = tangents[1] - tangents[0], + binormal = cross(tangent, normal), + z = unit(tangent), + x = assert(norm(binormal) > 0.00001, "first three points are colinear") unit(binormal), + y = unit(cross(z, x)) + ) [[x.x, y.x, z.x], + [x.y, y.y, z.y], + [x.z, y.z, z.z]]; +// +// Computes the rotation with minimum angle that brings UNIT vectors a to b. +// The code fails if a and b are opposed to each other. +// +function rotate_from_to(a, b) = + let(axis = unit(cross(a, b))) + axis * axis >= 0.99 ? transpose3([b, axis, cross(axis, b)]) * [a, axis, cross(axis, a)] + : a * b > 0 ? [[ 1, 0, 0], [0, 1, 0], [0, 0, 1]] + : [[-1, 0, 0], [0, 1, 0], [0, 0, -1]]; +// +// Given two rotations A and B, calculates the angle between B*[1,0,0] +// and A*[1,0,0] that is, the total torsion angle difference between A and B. +// +function calculate_twist(A, B) = let(D = transpose3(B) * A) atan2(D[1][0], D[0][0]); +// +// Compute a 4x4 matrix to orientate a frame of the sweep given the position and a 3x3 rotation matrix. +// +function orientate(p, r) = + let(x = r[0], y = r[1], z = r[2]) + [[x.x, y.x, z.x], + [x.y, y.y, z.y], + [x.z, y.z, z.z], + [p.x, p.y, p.z]]; + +// +// Rotate around z +// +function rot3_z(a) = + let(c = cos(a), + s = sin(a)) + [ [ c, -s, 0], + [ s, c, 0], + [ 0, 0, 1] ]; + +// +// Calculate the unit tangent at a vertex given the indices before and after. One of these can be the same as i in the case +// of the start and end of a non closed path. +// +function tangent(path, before, i, after) = unit(unit(path[after] - path[i]) - unit(path[before] - path[i])); +// +// Generate all the surface points of the swept volume. +// +function skin_points(profile, path, loop, twist = 0) = + let(len = len(path), + last = len - 1, + + profile4 = [for(p = profile) [p.x, p.y, p.z, 1]], + + tangents = [tangent(path, loop ? last : 0, 0, 1), + for(i = [1 : last - 1]) tangent(path, i - 1, i, i + 1), + tangent(path, last - 1, last, loop ? 0 : last)], + + rotations = [for(i = 0, rot = fs_frame(tangents); + i < len; + i = i + 1, + rot = i < len ? rotate_from_to(tangents[i - 1], tangents[i]) * rot : undef) rot], + + missmatch = loop ? calculate_twist(rotations[0], rotations[last]) : 0, + rotation = missmatch + twist + ) + [for(i = [0 : last]) + let(za = rotation * i / last) + each profile4 * orientate(path[i], rotations[i] * rot3_z(za)) + ]; + +function cap(facets, segment = 0) = [for(i = [0 : facets - 1]) segment ? facets * segment + i : facets - 1 - i]; + +function quad(p, a,b,c,d) = norm(p[a] - p[c]) > norm(p[b] - p[d]) ? [[b, c, d], [b, d, a]] : [[a, b, c], [a, c, d]]; + +function skin_faces(points, segs, facets, loop) = [for(i = [0 : facets - 1], s = [0 : segs - (loop ? 1 : 2)]) + each quad(points, + s * facets + i, + s * facets + (i + 1) % facets, + ((s + 1) % segs) * facets + (i + 1) % facets, + ((s + 1) % segs) * facets + i)]; + +function sweep(path, profile, loop = false, twist = 0) = //! Generate the point list and face list of the swept volume + let( + segments = len(path), + facets = len(profile), + points = skin_points(profile, path, loop, twist), + skin_faces = skin_faces(points, segments, facets, loop), + faces = loop ? skin_faces : concat([cap(facets)], skin_faces, [cap(facets, segments - 1)]) + ) [points, faces]; + +module sweep(path, profile, loop = false, twist = 0) { //! Draw a polyhedron that is the swept volume + mesh = sweep(path, profile, loop, twist); + + polyhedron(points = mesh[0], faces = mesh[1]); +} + +function path_length(path, i = 0, length = 0) = //! Calculated the length along a path + i >= len(path) - 1 ? length + : path_length(path, i + 1, length + norm(path[i + 1] - path[i])); + +function circle_points(r = 1, z = 0) = //! Generate the points of a circle, setting z makes a single turn spiral + let(sides = r2sides(r)) + [for(i = [0 : sides - 1]) let(a = i * 360 / sides) [r * sin(a), r * cos(a), z * a / 360]]; + +function rectangle_points(w, h) = [[-w/2, -h/2, 0], [-w/2, h/2, 0], [w/2, h/2, 0], [w/2, -h/2, 0]]; //! Generate the points of a rectangle + +function arc_points(r, a = [90, 0, 180], al = 90) = //! Generate the points of a circular arc + let(sides = ceil(r2sides(r) * al / 360), tf = rotate(a)) + [for(i = [0 : sides]) let(t = i * al / sides) transform([r * sin(t), r * cos(t), 0], tf)]; + +function before(path1, path2) = //! Translate ```path1``` so its end meets the start of ```path2``` and then concatenate + let(end = len(path1) - 1, offset = path2[0] - path1[end]) + concat([for(i = [0 : end - 1]) path1[i] + offset], path2); + +function after(path1, path2) = //! Translate ```path2``` so its start meets the end of ```path1``` and then concatenate + let(end1 = len(path1) - 1, end2 = len(path2) - 1, offset = path1[end1] - path2[0]) + concat(path1, [for(i = [1 : end2]) path2[i] + offset]); diff --git a/utils/tube.scad b/utils/tube.scad new file mode 100644 index 0000000..fcb497c --- /dev/null +++ b/utils/tube.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Simple tube or ring +// +include <../core.scad> + +module ring(or, ir) //! Create a ring with specified external and internal radii + difference() { + circle4n(or); + circle4n(ir); + } + +module tube(or, ir, h, center = true) //! Create a tube with specified external and internal radii and height ```h``` + linear_extrude(height = h, center = center, convexity = 5) + ring(or, ir); diff --git a/vitamins/ball_bearing.scad b/vitamins/ball_bearing.scad new file mode 100644 index 0000000..318a17b --- /dev/null +++ b/vitamins/ball_bearing.scad @@ -0,0 +1,68 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Simple model of ball bearings with seals, the colour of which can be specified. If silver they are assumed to be metal and the +//! part number gets a ZZ suffix. Any other colour is assumed to be rubber and the suffix is -2RS. +//! +//! If a ball bearing has a child it is placed on its top surface, the same as nuts and washers, etc. +//! +//! Also single bearing balls are modelled as just a silver sphere and a BOM entry. +// +include <../core.scad> + +function bb_name(type) = type[0]; //! Part code without shield type suffix +function bb_bore(type) = type[1]; //! Internal diameter +function bb_diameter(type) = type[2]; //! External diameter +function bb_width(type) = type[3]; //! Width +function bb_colour(type) = type[4]; //! Shield colour, "silver" for metal +function bb_rim(type) = bb_diameter(type) / 10; //! Inner and outer rim thickness + +module ball_bearing(type) { //! Draw a ball bearing + shield = bb_colour(type); + suffix = shield == "silver" ? "ZZ " : "-2RS "; + vitamin(str("ball_bearing(BB", bb_name(type), "): Ball bearing ", bb_name(type), suffix, bb_bore(type), "mm x ", bb_diameter(type), "mm x ", bb_width(type), "mm")); + rim = bb_rim(type); + h = bb_width(type); + od = bb_diameter(type); + id = bb_bore(type); + + module tube(od, id, h) + linear_extrude(height = h, center = true, convexity = 5) + difference() { + circle(d = od); + circle(d = id); + } + + color("silver") { + tube(od, od - rim, h); + tube(id + rim, id, h); + } + + color(shield) tube(od - rim, id + rim, h - 1); + + if($children) + translate_z(bb_width(type) / 2) + children(); +} + +module bearing_ball(dia) { //! Draw a steel bearing ball + vitamin(str(" bearing_ball(", dia, "): Steel ball ", dia, "mm")); + color("silver") sphere(d = dia); +} diff --git a/vitamins/ball_bearings.scad b/vitamins/ball_bearings.scad new file mode 100644 index 0000000..23fb42d --- /dev/null +++ b/vitamins/ball_bearings.scad @@ -0,0 +1,24 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +BB624 = ["624", 4, 13, 5, "blue"]; // 624 ball bearing for idlers +BB608 = ["608", 8, 22, 7, "OrangeRed"]; // 608 bearings for wades + +ball_bearings = [BB624, BB608]; + +use diff --git a/vitamins/batteries.scad b/vitamins/batteries.scad new file mode 100644 index 0000000..9429f42 --- /dev/null +++ b/vitamins/batteries.scad @@ -0,0 +1,50 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// w h t t t p n +// i e h a a o e +// d i i b b s g +// t g c +// h h k w h h d d h s +// t n 1 2 p +// e r +// s i +// s n +// g +bcontact = ["bcontact", 9.33, 9.75, 0.4, 2.86, 6, [1.6, 3, 5], [4.5, batt_spring]]; + +// l d n p p c L U c +// e i e o o o E S o +// n a g s s l D B n +// g m o t +// t e d d h u a +// t r c +// e t +// r +LUMINTOP = ["LUMINTOP", "Cell LUMINTOP 18650 LION with charger", 70.7, 18.4, 13, 5, 1, "white", [[3.32, 5], [3.32, -5]], 4, bcontact]; +S25R18650 = ["S25R18650", "Cell Samsung 25R 18650 LION", 65, 18.3, 13, 10, 0, "MediumSeaGreen", [], 0, bcontact]; +AACELL = ["AACELL", "Cell AA", 50.5, 14.5, 11, 5.5, 1, "grey", [], 0, bcontact]; +AAACELL = ["AAACELL", "Cell AAA", 44.5, 10.5, 8, 3.8, 0.8, "grey", [], 0, bcontact]; +CCELL = ["CCELL", "Cell C", 50, 26.2, 20, 7.5, 1.5, "brown", [], 0, bcontact]; +DCELL = ["DCELL", "Cell D", 61.5, 34.2, 22, 8.2, 2.4, "brown", [], 0, bcontact]; + +batteries = [AAACELL, AACELL, CCELL, DCELL, LUMINTOP, S25R18650]; + +use diff --git a/vitamins/battery.scad b/vitamins/battery.scad new file mode 100644 index 0000000..7fe609e --- /dev/null +++ b/vitamins/battery.scad @@ -0,0 +1,161 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Actually just single cells at the moment, shown here with mating contacts in place. +//! +//! Note that the [Lumintop LM34](http://www.lumintop.com/lm34c-usb-rechargeable-18650-li-ion-battery.html) has a built in charger with a USB socket and two LEDs. +//! +//! The battery length includes its contacts and the origin is the centre of that length. As well as drawing the battery and contacts there are functions +//! exposing enough information to make a battery box. +// +include <../core.scad> +use +use <../utils/rounded_cylinder.scad> + +function battery_length(type) = type[2]; //! Total length including terminals +function battery_diameter(type) = type[3]; //! Casing diameter +function battery_neg_dia(type) = type[4]; //! Negative terminal diameter +function battery_pos_dia(type) = type[5]; //! Positive terminal diameter +function battery_pos_height(type) = type[6]; //! Positive terminal height above the casing +function battery_colour(type) = type[7]; //! Casing colour +function battery_led_positions(type) = type[8]; //! LED positions for Lumintop +function battery_usb_offset(type) = type[9]; //! USB connector offset from the top +function battery_contact(type) = type[10]; //! Contact type + +module battery_led_positions(type) { //! Position of the LEDs on a Lumintop + posns = battery_led_positions(type); + for($i = [0 : 1 : len(posns) - 1]) + translate([posns[$i].x, posns[$i].y, battery_length(type) / 2 - battery_pos_height(type)]) + children(); +} + +module battery(type) { //! Draw a battery + vitamin(str("battery(", type[0], "): ", type[1])); + len = battery_length(type); + + l = 6; + iw1 = 7; + iw2 = 5.7; + ih1 = 1; + ih2 = 1.85; + h = 2.65; + t = 0.4; + + module D() { + hull() { + translate([-iw1 / 2, h - t - ih1]) + square([iw1, ih1]); + + translate([-iw2 / 2, h - t - ih2]) + square([iw2, ih2]); + } + } + + color(battery_colour(type)) render() difference() { + translate_z(-battery_pos_height(type) / 2) + cylinder(d = battery_diameter(type), h = len - battery_pos_height(type), center = true); + + if(battery_usb_offset(type)) + translate([battery_diameter(type) / 2, 0, len / 2 - battery_usb_offset(type) + h / 2]) + rotate([-90, 0, 90]) + linear_extrude(height = l + 1) + offset(delta = t) + D(); + } + color("gold") + translate_z(len / 2 - battery_pos_height(type)) + rounded_cylinder(r = battery_pos_dia(type) / 2, h = battery_pos_height(type) + eps, r2 = 0.5); + + color("silver") { + if(battery_usb_offset(type)) + translate([battery_diameter(type) / 2 - 1, 0, len / 2 - battery_usb_offset(type) + h / 2]) + rotate([-90, 0, 90]) { + linear_extrude(height = l) + difference() { + offset(t) D(); + D(); + } + + translate_z(l - 1) + linear_extrude(height = 1) + D(); + } + + translate_z(-len / 2) + vflip() + cylinder(d = battery_neg_dia(type), h = eps); + + } + + battery_led_positions(type) + color(["red","green","blue"][$i]) + cylinder(d = 1.5, h = eps); +} + +function contact_width(type) = type[1]; //! Width of the flat part +function contact_height(type) = type[2]; //! Height of the flat part +function contact_thickness(type) = type[3]; //! Thickness of the metal +function contact_tab_width(type) = type[4]; //! Width of the tab +function contact_tab_length(type) = type[5]; //! Length of the tab +function contact_pos(type) = type[6]; //! Positive contact dimple height and top and bottom internal diameter +function contact_neg(type) = type[7]; //! Negative spring height above the plate when compressed and the spring type + +module battery_contact(type, pos = true) { //! Draw a positive or negative battery contact for specified battery + vitamin(str("battery_contact(", type[0], ", ", pos, "): Battery ", pos ? "positive" : "negative", " contact")); + + neg = 9; + + tw = contact_tab_width(type); + h = contact_height(type); + hole_y = -contact_tab_length(type) + tw / 2; + t = contact_thickness(type); + + color("silver") { + rounded_rectangle([contact_width(type), h, t], r = 1, center = false); + + translate([0, -h / 2, t]) + rotate([90, 0, 0]) + linear_extrude(height = t) + difference() { + hull() { + translate([-tw / 2, -1]) + square([tw, 1]); + + translate([0, hole_y]) + circle(d = tw); + } + translate([0, hole_y]) + circle(tw / 4); + } + + if(pos) { + p = contact_pos(type); + + cylinder(d1 = p.z + 2 * t, d2 = p.y + 2 * t, h = p.x); + } + else { + p = contact_neg(type); + + not_on_bom() + translate_z(t) + comp_spring(p.y, p.x - t); + } + } +} diff --git a/vitamins/belt.scad b/vitamins/belt.scad new file mode 100644 index 0000000..37ad1d5 --- /dev/null +++ b/vitamins/belt.scad @@ -0,0 +1,81 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Models timing belt running over toothed or smooth pulleys and calculates an accurate length. +//! Only models 2D paths, so not core XY! +//! +//! By default the path is a closed loop but a gap length and position can be specified to make open loops. +//! +//! Individual teeth are not drawn, instead they are represented by a lighter colour. +// +include <../core.scad> +use <../utils/rounded_polygon.scad> + +function belt_pitch(type) = type[1]; //! Pitch in mm +function belt_width(type) = type[2]; //! Width in mm +function belt_thickness(type) = type[3]; //! Total thickness including teeth +function belt_tooth_height(type) = type[4]; //! Tooth height +function belt_pitch_height(type) = belt_tooth_height(type) + type[4]; //! Offset of the pitch radius from the tips of the teeth + +function no_point(str) = chr([for(c = str) if(c == ".") ord("p") else ord(c)]); +// +// We model the belt path at the pitch radius of the pulleys and the pitch line of the belt to get an accurate length. +// The belt is then drawn by offseting each side from the pitch line. +// +module belt(type, points, gap = 0, gap_pt = undef) { //! Draw a belt path given a set of points and pitch radii where the pulleys are. Closed loop unless a gap is specified + belt_colour = grey20; + tooth_colour = grey50; + width = belt_width(type); + pitch = belt_pitch(type); + thickness = belt_thickness(type); + part = str(type[0],pitch); + vitamin(str("belt(", no_point(part), "x", width, ", ", points, arg(gap, 0), arg(gap_pt, undef), "): Belt ", part," x ", width, "mm x ", length, "mm")); + + len = len(points); + + tangents = rounded_polygon_tangents(points); + + length = ceil((rounded_polygon_length(points, tangents) - gap) / pitch) * pitch; + + module shape() rounded_polygon(points, tangents); + + module gap() + if(gap) + translate(gap_pt) + square([gap, thickness + eps], center = true); + + color(belt_colour) + linear_extrude(height = width, center = true) + difference() { + offset(thickness - belt_pitch_height(type)) shape(); + offset(-belt_pitch_height(type) + belt_tooth_height(type)) shape(); + gap(); + + } + color(tooth_colour) + linear_extrude(height = width, center = true) + difference() { + offset(-belt_pitch_height(type) + belt_tooth_height(type)) shape(); + offset(-belt_pitch_height(type)) shape(); + gap(); + } +} + +function belt_length(points, gap = 0) = rounded_polygon_length(points, rounded_polygon_tangents(points)) - gap; //! Compute belt length given path and optional gap diff --git a/vitamins/belts.scad b/vitamins/belts.scad new file mode 100644 index 0000000..904621f --- /dev/null +++ b/vitamins/belts.scad @@ -0,0 +1,35 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Belt model +// +// p w t t p +// i i h o i +// t d i o t +// c t c t c +// h h k h h line from tooth base +// +T5x6 = ["T", 5, 6, 2.2, 1.2, 0.5]; +T5x10 = ["T", 5, 10, 2.2, 1.2, 0.5]; +T2p5x6 =["T", 2.5, 6, 1.7, 0.7, 0.3]; +GT2x6 = ["GT", 2.0, 6, 1.38, 0.75, 0.254]; + +belts = [T5x6, T5x10, T2p5x6, GT2x6]; +use diff --git a/vitamins/blower.scad b/vitamins/blower.scad new file mode 100644 index 0000000..f2ea839 --- /dev/null +++ b/vitamins/blower.scad @@ -0,0 +1,127 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Models of radial blowers. +// +include <../core.scad> +use <../utils/rounded_cylinder.scad> + +function blower_length(type) = type[2]; //! Length of enclosing rectangle +function blower_width(type) = type[3]; //! Width of enclosing rectangle +function blower_depth(type) = type[4]; //! Height +function blower_bore(type) = type[5]; //! The air intake hole diameter +function blower_screw(type) = type[6]; //! The type of screws needed +function blower_hub(type) = type[7]; //! Rotor hub diameter +function blower_axis(type) = type[8]; //! XY coordinates of the axle +function blower_screw_hole(type) = type[9]; //! Screw hole diameter +function blower_screw_holes(type) = type[10]; //! List of XY coordinates of the screw holes +function blower_exit(type) = type[11]; //! The width of the exit port +function blower_hub_height(type) = type[12]; //! Height of the rotor +function blower_base(type) = type[13]; //! Thickness of the base +function blower_top(type) = type[14]; //! Thickness of the top +function blower_wall(type) = type[15]; //! Side wall thickness +function blower_lug(type) = type[16]; //! Height of the lugs + +fan_colour = grey20; + +module blower(type) { //! Draw specified blower + length = blower_length(type); + width = blower_width(type); + depth = blower_depth(type); + screw = blower_screw(type); + + r1 = blower_axis(type)[0]; + r2 = width - blower_axis(type)[1]; + r3 = length - blower_axis(type)[0]; + function radius(a) = a < 90 ? r1 * exp(a * ln(r2 / r1) / 90) + : r2 * exp((a - 90) * ln(r3 / r2) / 90); + function spiral(a) = let(r = radius(a)) [-r * cos(a), r * sin(a)]; + + module shape(inside = false) + union() { + hull() { + translate(blower_axis(type)) + polygon([for(a = [0 : 1 : 360]) spiral(a)]); + + if(blower_exit(type) > length / 2) + square([blower_exit(type), 1]); + } + offset = inside ? 5 : 0; + translate([0, -offset]) + square([blower_exit(type), blower_axis(type)[1] + offset]); + } + + vitamin(str("blower(", type[0], "): ", type[1])); + + color(fan_colour) { + // screw lugs + linear_extrude(height = blower_lug(type), center = false) + for(hole = blower_screw_holes(type)) + difference() { + hull() { + translate(hole) + circle(d = blower_screw_hole(type) + 2 * blower_wall(type)); + + translate(blower_axis(type)) + circle(d = blower_screw_hole(type) + 2 * blower_wall(type) + 7); + } + circle(d = blower_screw_hole(type)); + + shape(true); + } + // rotor + translate(concat(blower_axis(type), [blower_base(type) + 1])) + rounded_cylinder(r = blower_hub(type) / 2, h = blower_hub_height(type) - blower_base(type) - 1, r2 = 1); + + *%square([length, width]); + + // base + linear_extrude(height = blower_base(type)) + difference() { + shape(); + + translate(concat(blower_axis(type), [blower_base(type)])) + circle(d = 2); + } + // sides + linear_extrude(height = depth) + difference() { + shape(); + + offset(-blower_wall(type)) + shape(true); + } + + // top + translate_z(depth -blower_top(type)) + linear_extrude(height = blower_top(type)) + difference() { + shape(); + + translate(concat(blower_axis(type), [blower_base(type)])) + circle(d = blower_bore(type)); + } + } +} + +module blower_hole_positions(type) //! Translate children to screw hole positions + for(hole = blower_screw_holes(type)) + translate(hole) + children(); diff --git a/vitamins/blowers.scad b/vitamins/blowers.scad new file mode 100644 index 0000000..3e1b2f4 --- /dev/null +++ b/vitamins/blowers.scad @@ -0,0 +1,25 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +RB5015 = ["RM5015", "Blower Runda RB5015", 51.3, 51, 15, 31.5, M4_cap_screw, 26, [27.3, 25.4], 4.5, [[4.3, 45.4], [47.3,7.4]], 20, 14, 1.5, 1.3, 1.2, 15]; +PE4020 = ["PE4020", "Blower Pengda Technology 4020", 40, 40, 20, 27.5, M3_cap_screw, 22, [21.5, 20 ], 3.2, [[37,3],[3,37],[37,37]], 29.3, 17, 1.7, 1.2, 1.3, 13]; + +blowers = [PE4020, RB5015]; + +use diff --git a/vitamins/bulldog.scad b/vitamins/bulldog.scad new file mode 100644 index 0000000..874e0b2 --- /dev/null +++ b/vitamins/bulldog.scad @@ -0,0 +1,83 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Crude representation of a bulldog clip. The handle is not currently drawn but its length can be +//! accessed to allow clearance. Used for holding glass on 3D printer beds but Swiss picture clips can be +//! better. +// +include <../core.scad> +use <../utils/rounded_polygon.scad> +use <../utils/sector.scad> + +function bulldog_length(type) = type[1]; //! Length along the profile +function bulldog_depth(type) = type[2]; //! Depth from the back to the front of the tubes +function bulldog_height(type) = type[3]; //! Height at the back +function bulldog_thickness(type) = type[4]; //! Thickness of the metal +function bulldog_tube(type) = type[5] / 2; //! Outside diameter of the tubes +function bulldog_radius(type) = type[6]; //! Outside radius of the back corners +function bulldog_handle_length(type) = type[7]; //! Length that the handle protrudes from the back + +module bulldog_shape(depth, height, radius, tube, open) { + rounded_polygon([ + [-depth / 2 + radius, -height / 2 + radius, radius], + [-depth / 2 + radius, height / 2 - radius, radius], + [ depth / 2 - tube, open / 2 + tube, -tube], + [ depth / 2 + tube, height / 2, tube], + [ depth / 2 + tube, -height / 2, tube], + [ depth / 2 - tube, -open / 2 - tube, -tube], + + ]); +} + +module bulldog(type, open = 4) { //! Draw bulldog clip open by specified amount + tube = bulldog_tube(type); + thickness = bulldog_thickness(type); + depth = bulldog_depth(type); + length = bulldog_length(type); + height = bulldog_height(type); + rad = bulldog_radius(type); + gap = open + thickness * 2; + + vitamin(str("bulldog(", type[0], "): Bulldog clip ",length, "mm")); + + color("yellow") + translate([depth / 2 - thickness - eps, 0]) + rotate([90, 0, 0]) + linear_extrude(length, center = true) + union() { + difference() { + bulldog_shape(depth, height, rad, tube, gap); + + offset(-thickness) + bulldog_shape(depth, height, rad, tube, gap); + + translate([depth / 2 - tube, -height]) + square([depth, 2 * height]); + } + + for(side = [-1, 1]) + translate([depth / 2 - tube, side * (open / 2 + tube)]) + difference() { + mirror([0, side < 0 ? 1 : 0]) + sector(tube, -90, 210); + circle(tube - thickness); + } + } +} diff --git a/vitamins/bulldogs.scad b/vitamins/bulldogs.scad new file mode 100644 index 0000000..23bfb5a --- /dev/null +++ b/vitamins/bulldogs.scad @@ -0,0 +1,35 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Crude representation of a bulldog clip +// +// l d h t t r h +// e e e h u a a +// n p i i b d n +// g t g c e i d +// t h h k u l +// h s e +// +small_bulldog = ["small_bulldog", 19, 12, 8, 0.25, 2.67, 1, 16]; +large_bulldog = ["large_bulldog", 25, 15,12, 0.28, 3.00, 2.4, 20]; + +bulldogs = [small_bulldog, large_bulldog]; + +use diff --git a/vitamins/button.scad b/vitamins/button.scad new file mode 100644 index 0000000..aaa3f94 --- /dev/null +++ b/vitamins/button.scad @@ -0,0 +1,74 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! PCB mounted buttons. Can optionally have a coloured cap +// +include <../core.scad> +use <../utils/rounded_cylinder.scad> + +function square_button_width(type) = type[1]; //! Width and depth of the base +function square_button_height(type) = type[2]; //! Height of the base +function square_button_wall(type) = type[3]; //! Offset of the metal part +function square_button_rivit(type) = type[4]; //! Size of the corner rivets +function square_button_d(type) = type[5]; //! Button diameter +function square_button_h(type) = type[6]; //! Height of the button above the PCB +function square_button_cap_flange_d(type) = type[7]; //! Diameter of the flange of the cap +function square_button_cap_d(type) = type[8]; //! Diameter of the body of the cap +function square_button_cap_h(type) = type[9]; //! Height of the cap including the stem +function square_button_cap_stem(type) = type[10]; //! Length of the cap stem +function square_button_cap_flange_h(type) = type[11]; //! Height of the cap flange + +module square_button(type, colour = "yellow") { //! Draw square button with specified cap colour if it has a cap + w = square_button_width(type); + flange_d = square_button_cap_flange_d(type); + vitamin(str("square_button(", type[0], flange_d ? str(", \"", colour, "\"") : "", "): Square button ", w, "mm", + flange_d ? str(" with ", colour, " cap") : "")); + h = square_button_height(type); + wall = square_button_wall(type); + rivit = square_button_rivit(type); + pitch = (w/ 2 - wall - rivit * 0.75); + stem = square_button_cap_stem(type); + + color(grey20) { + rounded_rectangle([w, w, h - 0.5], r = wall, center = false); + + for(x = [-1, 1], y = [-1, 1]) + translate([x * pitch, y * pitch]) + cylinder(d = rivit, h = h); + + cylinder(d = square_button_d(type), h = square_button_h(type)); + } + + color("silver") + translate_z(h - 0.5) + rounded_rectangle([w - 2 * wall, w - 2 * wall, 0.2], r = wall, center = true); + + if(flange_d) + translate_z(square_button_h(type)) + color(colour) rotate_extrude() { + square([square_button_d(type) / 2, stem]); + + translate([0, stem]) { + square([flange_d / 2, square_button_cap_flange_h(type)]); + + rounded_corner(r = square_button_cap_d(type) / 2, h = square_button_cap_h(type) - stem, r2 = 0.5); + } + } +} diff --git a/vitamins/buttons.scad b/vitamins/buttons.scad new file mode 100644 index 0000000..e4f64ac --- /dev/null +++ b/vitamins/buttons.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// w h w r b b c c c c c +// i e a i u u a a a a a +// d i l v t t p p p p p +// t g l i +// h h t d h f d h s f +// d t h +button_12mm = ["button_12mm", 12, 4.0, 0.8, 1.5, 6.8, 4.3, 12.86, 11.44, 8.15, 2.7, 1.4]; +button_6mm = ["button_6mm", 6, 4.0, 0.2, 1.0, 3.5, 5.0, 0]; +button_4p5mm= ["button_4p5mm", 4.5, 3.1, 0.1, 0.9, 2.4, 4.5, 0]; + +buttons = [button_4p5mm, button_6mm, button_12mm]; + +use diff --git a/vitamins/cable_strip.scad b/vitamins/cable_strip.scad new file mode 100644 index 0000000..92bbd13 --- /dev/null +++ b/vitamins/cable_strip.scad @@ -0,0 +1,159 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! A strip of polypropylene used with ribbon cable to make a cable flexible in one direction only. +//! +//! Modelled with a Bezier spline, which is not quite the same as a miniumum energy curve but very close, epecially +//! near the extreme positions, where the model needs to be accurate. +//! +//! When the sides are constrained then a circular model is more accurate. +// +include <../core.scad> +cable_strip_thickness = 0.8; +function ribbon_clamp_slot(ways) = ways * inch(0.05) + 1; +function ribbon_clamp_slot_depth() = cable_strip_thickness + inch(0.05); +function cable_strip_thickness() = cable_strip_thickness; + +use <../utils/bezier.scad> +use <../utils/sweep.scad> + +cable_strip_color = "green"; + +function cable_strip_control_points(depth, min_z, pos) = let(z = min(min_z, min_z + pos)) +[ + [0, 0, 0], [0, 0, z], [0, depth, z], [0, depth, pos] +]; + +function bezier_cable_length(depth, min_z, pos) = //! Calculate a length that will achieve the desired minimum z + bezier_length(adjust_bezier_z(cable_strip_control_points(depth, min_z, pos), min_z)); + +module bezier_cable_strip(ways, depth, length, travel, pos, below, extra) { //! Draw a cable strip using a Bezier curve + width = ceil(ribbon_clamp_slot(ways) - 1); + + thickness = cable_strip_thickness; + + total = 2 * extra + length; + + vitamin(str("bezier_cable_strip(", ways, ", ", depth, ", ", length, ", ", travel, ", ", pos, ", ", below, ", ", extra, + "): Polypropylene strip ", total, "mm x ", width, "mm x ", thickness, "mm")); + + c = cable_strip_control_points(depth, -below + extra, pos); + v = adjust_bezier_length(c, length); + + steps = 100; + extra_v = [0, 0, extra]; + + path = [v[0] + extra_v, each bezier_path(v, steps), v[3] + extra_v]; + + color(cable_strip_color) + translate_z(-extra) + sweep(path, rectangle_points(width, thickness)); + *echo(cable_strip_lengh = length); + *translate_z(-extra) sweep(v, circle_points(1)); +} + +function cable_strip_length(depth, travel, extra = 15) = ceil(travel / 2 + 2 * extra + PI * depth); //! Calculate circular cable strip length + +module cable_strip(ways, depth, travel, x, extra = 15) { //! Draw a cable stripe with a semi circular fold + + width = ribbon_clamp_slot(ways); + + thickness = cable_strip_thickness; + + radius = depth / 2; + + top = travel / 4 + extra + x / 2; + bottom = travel / 4 + extra - x /2; + + length = max(top, bottom); + + total = ceil(top + bottom + PI * depth); + w = floor(width - 2); + + vitamin(str("cable_strip(", ways, ", ", depth, ", ", travel, ", ", x, arg(extra, 15), "): Polypropylene strip ", total, "mm x ", w, "mm x ", thickness, "mm")); + + color(cable_strip_color) linear_extrude(height = w, center = true, convexity = 4) + difference() { + union() { + translate([-bottom, radius]) + circle(radius); + + translate([-bottom, 0]) + square([length, depth]); + } + union() { + translate([-bottom, radius]) + circle(radius - thickness); + + translate([-bottom, thickness]) + square([length + 1, depth - thickness * 2]); + } + translate([0, -thickness / 2]) + square([travel, thickness * 2]); + + translate([x, depth - thickness - thickness / 2]) + square([travel, thickness * 2]); + } +} + +function elliptical_cable_strip_length(p1, pmax, extra = 15) = ceil(PI * pow((pow(abs((pmax - p1)[0] / 2),1.5) + pow(75,1.5))/2, 1/1.5)) + 2 * extra; + +module elliptical_cable_strip(ways, p1, p2, pmax, extra = 15) { + width = ribbon_clamp_slot(ways); + + thickness = cable_strip_thickness; + w = floor(width - 1); + + max_delta = pmax - p1; + delta = p2 - p1; + + A = abs(max_delta[0] / 2); + B = 75; + + length = ceil(PI * pow((pow(A,1.5) + pow(B,1.5))/2, 1/1.5)); + total = length + 2 * extra; + + vitamin(str("elliptical_cable_strip(", ways, ", ", p1, ", ", p2, ", ", pmax, arg(extra, 15), + "): Polypropylene strip ", total, "mm x ", w, "mm x ", thickness, "mm")); + + a = abs(delta[0] / 2); + b = pow(2 * pow(length / PI, 1.5) - pow(a, 1.5), 1/1.5); + + translate(p1 - [a, 0, 0]) + multmatrix(m = [ [1, 0, 0, 0], + [delta[1] / delta[0], 1, 0, delta[1] / 2], + [delta[2] / delta[0], 0, 1, delta[2] / 2], + [0, 0, 0, 1] ]) + + color(cable_strip_color) linear_extrude(height = w, center = true, convexity = 4) + difference() { + union() { + square([(a + thickness) * 2, extra * 2], center = true); + translate([0, -extra]) + ellipse((a + thickness), b + thickness); + } + translate([0, (b + 1) / 2]) + square([a * 2 + 1, b + 1], center = true); + + square([a * 2, extra * 2], center = true); + translate([0, -extra]) + ellipse(a, b); + } +} diff --git a/vitamins/component.scad b/vitamins/component.scad new file mode 100644 index 0000000..346b1a9 --- /dev/null +++ b/vitamins/component.scad @@ -0,0 +1,450 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Various electronic components used in hot ends and heated beds. +// + +// +// Resistor model for hot end +// +include <../core.scad> +include +include +include +use <../utils/rounded_cylinder.scad> + +function resistor_length(type) = type[2]; //! Body length +function resistor_diameter(type) = type[3]; //! Body diameter +function resistor_wire_diameter(type) = type[4]; //! Wire diameter +function resistor_wire_length(type) = type[5]; //! Wire length from body +function resistor_hole(type) = type[6]; //! Hole big enough to glue it into +function resistor_colour(type) = type[7]; //! Body colour +function resistor_radial(type) = type[8]; //! Radial gives bead thermistor style body +function resistor_sleeved(type) = type[9]; //! Are the leads sleeved + +splay_angle = 2; // radial lead splay angle + +module resistor(type) { //! Draw specified type of resitor + length = resistor_length(type); + dia = resistor_diameter(type); + + vitamin(str("resistor(", type[0], "): ", type[1])); + // + // wires + // + color([0.7, 0.7, 0.7]) + if(resistor_radial(type)) + for(side= [-1,1]) + translate([side * dia / 6, 0, length / 2]) + rotate([0, splay_angle * side, 0]) + cylinder(r = resistor_wire_diameter(type) / 2, h = resistor_wire_length(type), center = false); + else + cylinder(r = resistor_wire_diameter(type) / 2, h = length + 2 * resistor_wire_length(type), center = true); + // + // Sleeving + // + if(resistor_sleeved(type)) + color([0.5, 0.5, 1]) + if(resistor_radial(type)) + for(side= [-1, 1]) + translate([side * resistor_diameter(type) / 6, 0, length / 2]) { + rotate([0, splay_angle * side, 0]) + cylinder(r = resistor_wire_diameter(type) / 2 + 0.1, h = resistor_wire_length(type) - 5, center = false); } + // + // Body + // + color(resistor_colour(type)) + if(resistor_radial(type)) + hull() { + translate_z(-length / 2 + dia / 2) + sphere(d = dia); + + cylinder(d = dia / 2, h = length / 2); + } + else + rotate_extrude() + for(y = [0, 1]) + mirror([0, y]) + rounded_corner(r = dia / 2, h = length / 2, r2 = dia / 10); +} + +module sleeved_resistor(type, sleeving, bare = 5, heatshrink = false) { //! Draw a resistor with sleeved leads and option heatshrink + resistor(type); + sleeving_length = resistor_wire_length(type) - bare; + + for(side= [-1,1]) + if(resistor_radial(type)) { + translate([side * resistor_diameter(type) / 6, 0, 0]) + rotate([0, splay_angle * side, 0]) { + if(!resistor_sleeved(type)) + translate_z(sleeving_length / 2 + resistor_length(type) / 2 + 20 * exploded()) + tubing(sleeving, sleeving_length); + + if(heatshrink) + translate_z(sleeving_length + resistor_length(type) / 2 + bare / 2 + 30 * exploded()) + tubing(heatshrink); + } + } + else { + translate_z(side * (resistor_length(type) + sleeving_length + 40 * exploded()) / 2) + tubing(sleeving, sleeving_length); + + if(heatshrink) + translate_z(side * (resistor_length(type) /2 + sleeving_length + 30 * exploded())) + tubing(heatshrink); + } +} + +function al_clad_length(type) = type[1]; //! Body length +function al_clad_width(type) = type[2]; //! Width including tabs +function al_clad_tab(type) = type[3]; //! Tab width +function al_clad_hpitch(type) = type[4]; //! Lengthways pitch between screw holes +function al_clad_vpitch(type) = type[5]; //! Widthways pitch between screw holes +function al_clad_thickness(type) = type[6]; //! Tab thickness +function al_clad_hole(type) = type[7]; //! Hole diameter +function al_clad_clearance(type) = type[8]; //! Clearance from screw hole centre to the body +function al_clad_height(type) = type[9]; //! Body height +function al_clad_wire_length(type) = type[10]; //! Total length including wires + +module al_clad_resistor_hole_positions(type) //! Position children at the screw holes of an aluminium clad resistor + for(end = [-1, 1]) + translate([end * al_clad_hpitch(type) / 2, end * al_clad_vpitch(type) / 2, al_clad_thickness(type)]) + children(); + +module al_clad_resistor_holes(type, h = 100) //! Drill screw holes for an aluminium clad resistor + al_clad_resistor_hole_positions(type) + drill(screw_clearance_radius(al_clad_hole(type) > 3 ? M3_pan_screw : M2p5_pan_screw), h); + +module al_clad_resistor(type, value, leads = true) { //! Draw an aluminium clad resistor + vitamin(str("al_clad_resistor(", type[0], ", ", value, arg(leads, true, "leads"), + "): Resistor aluminium clad ", type[0], " ", value)); + length = al_clad_length(type); + width = al_clad_width(type); + height = al_clad_height(type); + tab = al_clad_tab(type); + thickness = al_clad_thickness(type); + terminal_h = 4; + terminal_t = 1; + terminal_l = 5; + + body = al_clad_vpitch(type) - 2 * al_clad_clearance(type); + + color("silver") { + rotate([90, 0, 90]) + linear_extrude(height = length, center = true) + hull() { + translate([0, al_clad_height(type) / 2]) + intersection() { + square([body, al_clad_height(type)], center = true); + + circle(body / 2 - eps); + } + translate([0, thickness / 2]) + square([body, thickness], center = true); + } + linear_extrude(height = thickness) + difference() { + for(end = [-1, 1]) + translate([end * (length - tab) / 2, end * (width - width / 2) / 2]) + square([tab, width / 2], center = true); + + al_clad_resistor_hole_positions(type) + circle(d = al_clad_hole(type)); + + } + if(leads) { + translate_z(height / 2) + rotate([0, 90, 0]) + cylinder(r = 1, h = al_clad_wire_length(type) - 2 * terminal_l + eps, center = true); + + for(end = [-1, 1]) + translate([end * (al_clad_wire_length(type) - terminal_l) / 2, 0, height / 2]) + rotate([90, 0, 0]) + linear_extrude(height = terminal_t, center = true) difference() { + square([terminal_l, terminal_h], center = true); + + circle(r = 1); + } + } + } + color("black") + translate_z(height / 2) + rotate([0, 90, 0]) + cylinder(r = leads ? 3 : height / 2 - 2, h = length + eps, center = true); +} + +module al_clad_resistor_assembly(type, value, sleeved = true) { //* Draw aluminium clad resistor with optional sleaving, positions children at the screw positions + sleeving_length = 15; + sleeving = HSHRNK32; + + al_clad_resistor(type, value); + + if(sleeved) + for(end = [-1, 1]) + translate([end * (al_clad_length(type) + sleeving_length + 0) / 2, 0, al_clad_height(type) / 2]) + rotate([0, 90, 0]) + scale([1.5, 0.66, 1]) + tubing(sleeving, sleeving_length); + + al_clad_resistor_hole_positions(type) + children(); +} + +function TO220_thickness() = 1.5; //! Thickness of the tab of a TO220 + +module TO220(description, leads = 3, lead_length = 16) { //! Draw a TO220 package, use ```description``` to describe what it is + width = 10.2; + inset = 1.5; + hole = 3.3; + length = 15; + height = 4.4; + lead_height = 1.9; + lead_t = 0.4; + lead_w = 0.7; + lead_w2 = 1.4; + lead_l = 4.2; + body = 8; + hole_y = 2.9; + + vitamin(str("TO220(\"", description, "\"", arg(leads, 3, "leads"), arg(lead_length, 16, "lead_length"), "): ", description)); + + translate([0, -length + hole_y]) { + color("silver") { + linear_extrude(height = TO220_thickness()) + difference() { + translate([-width / 2, inset]) + square([width, length - inset]); + + translate([0, length - hole_y]) + circle(d = hole); + + for(side = [-1, 1]) + translate([side * width / 2, 0]) + square([inset * 2, body * 2], center = true); + } + + for(i = [-1 : 1]) + if(i || leads == 3) { + translate([inch(0.1) * i, -lead_length / 2, lead_height]) + cube([lead_w, lead_length, lead_t], center = true); + + translate([inch(0.1) * i, -lead_l / 2, lead_height]) + cube([lead_w2, lead_l, lead_t], center = true); + } + } + color("dimgrey") + translate([-width / 2, 0, eps]) + cube([width, body, height]); + } + translate_z(TO220_thickness()) + children(); +} + +panel_USBA_pitch = 30; + +module panel_USBA_hole_positions() //! Place children at hole positions + for(side = [-1, 1]) + translate([side * panel_USBA_pitch / 2, 0]) + children(); + +module panel_USBA_holes(h = 100) { //! Make holes for USBA connector + corner_clearance = 2 * cnc_bit_r * (1 - 1 / sqrt(2)); + width = 5.5 + corner_clearance; + length = 13 + corner_clearance; + + extrude_if(h) union() { + rounded_square([length, width], r = cnc_bit_r); + + panel_USBA_hole_positions() + drill(M3_clearance_radius, 0); + } +} + +module panel_USBA() { //! Draw a panel mount USBA connector + vitamin("panel_USBA(): Socket USB A panel mount"); + + width = 12; + length = 40; + length2 = 22; + thickness = 5.5; + height = 33; + height2 = 27; + lead_dia = 10; + r1 = 1.5; + r2 = 5; + height3 = 9.5; + length3 = 17.5; + + l = 17; + w = 13.3; + h = 5.7; + flange_t = 0.4; + h_flange_h = 0.8; + h_flange_l = 11.2; + + v_flange_h = 0.8; + v_flange_l = 3.8; + tongue_w = 10; + tongue_t = 1.3; + + vflip() { + color("dimgrey") { + linear_extrude(height = thickness) + difference() { + hull() + for(side = [-1, 1]) + translate([side * (length / 2 - width / 2), 0]) + circle(d = width); + + square([length3, width + 1], center = true); + + panel_USBA_hole_positions() + circle(M3_clearance_radius); + } + + translate_z(height2) + cylinder(d = lead_dia, h = height - height2); + + hull() { + dx = (length2 / 2 - r2); + dy = (width / 2 - r1); + translate_z(l) + rounded_rectangle([length2, width, 1], r = r1, center = false); + + translate([-dx, -dy, height2 - r2]) + rotate([90, 0, 0]) + rounded_cylinder(r = r2, r2 = r1, h = r1); + + translate([dx, -dy, height2 - r2]) + rotate([90, 0, 0]) + rounded_cylinder(r = r2, r2 = r1, h = r1); + + translate([-dx, dy, height2 - r2]) + rotate([-90, 0, 0]) + rounded_cylinder(r = r2, r2 = r1, h = r1); + + translate([dx, dy, height2 - r2]) + rotate([-90, 0, 0]) + rounded_cylinder(r = r2, r2 = r1, h = r1); + } + + translate_z(height3) + linear_extrude(height = l - height3) + difference() { + rounded_square([length2, width], r = r1); + + square([w - flange_t, h - flange_t], center = true); + } + + linear_extrude(height = height3) + difference() { + rounded_square([length2, width], r = r1); + + square([length3, width + 1], center = true); + } + } + + *cube([12, 4.5, 32], center = true); + + color("silver") { + linear_extrude(height = l) + difference() { + square([w, h], center = true); + + square([w - 2 * flange_t, h - 2 * flange_t], center = true); + } + + translate_z(l - flange_t / 2) + cube([w, h, flange_t], center = true); + + linear_extrude(height = flange_t) + difference() { + union() { + square([h_flange_l, h + 2 * h_flange_h], center = true); + + square([w + 2 * v_flange_h, v_flange_l], center = true); + } + square([w - 2 * flange_t, h - 2 * flange_t], center = true); + } + } + + color("white") + translate([0, h / 2 - 1 - tongue_t / 2, l / 2]) + cube([tongue_w, tongue_t, l], center = true); + } +} + +function tc_length(type) = type[1]; //! Across the lugs +function tc_width(type) = type[2]; //! Width of lugs +function tc_thickness(type) = type[3]; //! Metal thickness +function tc_hole_dia(type) = type[4]; //! Screw hole diameter +function tc_hole_pitch(type) = type[5]; //! Screw hole pitch +function tc_body_length(type) = type[6]; //! Plastic body length +function tc_body_width(type) = type[7]; //! Plastic body width +function tc_body_height(type) = type[8]; //! Plastic body height +function tc_body_inset(type) = type[9]; //! How far metal is inset into the plastic body +function tc_spade_height(type) = type[10]; //! Terminal spade height measured from base +function tc_spade_pitch(type) = type[11]; //! Terminal spade pitch + +module thermal_cutout_hole_positions(type) //! Place children at hole positions + for(side = [-1, 1]) + translate([side * tc_hole_pitch(type) / 2, 0]) + children(); + +module thermal_cutout(type) { //! Draw specified thermal cutout + vitamin(str("thermal_cutout(", type[0], "): Thermal cutout ", type[0])); + + w = tc_width(type); + t = tc_thickness(type); + h = tc_body_height(type); + bw = tc_body_width(type); + bl = tc_body_length(type); + spade = spade6p4; + + color("silver") { + linear_extrude(height = tc_thickness(type)) + difference() { + hull() + for(side = [-1, 1]) + translate([side *(tc_length(type) - w) / 2, 0]) + circle(d = w); + + thermal_cutout_hole_positions(type) + circle(d = tc_hole_dia(type)); + } + + body_inset = tc_body_inset(type); + translate_z((h - body_inset) / 2) + cube([bl - 2 * body_inset, bw + 2 * eps, h - body_inset], center = true); + } + + color("black") + translate_z(h / 2 + eps) + cube([bl, bw, h], center = true); + + for(side = [-1, 1]) + translate([side * tc_spade_pitch(type) / 2, 0, h]) + rotate(90) + spade(spade, tc_spade_height(type) - h); + + translate_z(t) + thermal_cutout_hole_positions(type) + children(); +} diff --git a/vitamins/components.scad b/vitamins/components.scad new file mode 100644 index 0000000..27b4f8a --- /dev/null +++ b/vitamins/components.scad @@ -0,0 +1,50 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Resistor model for hot end +// +RWM04106R80J = [ "RWM04106R80J", "Resistor RWM04106R80J 6R8 3W vitreous enamel", 12, 5, 0.8, 30, 5.5, "green", false, false]; +RIE1212UB5C5R6 = [ "RIE1212UB5C5R6", "Resistor UB5C 5R6F 5R6 3W vitreous enamel", 13, 5.9, 0.96, 35, 6.0, "gray", false, false]; +// +// Thermistors +// +Honewell = [ "Honewell", "Thermistor Honeywell 135-104LAC-J01 100K 1%", 4.75, 1.8, 0.5, 28.6, 2, "red", false]; +Epcos = [ "Epcos", "Thermistor Epcos B57560G104F 100K 1%", 4.6, 2.5, 0.3, 67, 2.5, [0.8, 0.8, 0.8, 0.25], true, false]; +EpcosBlue = [ "EpcosBlue", "Thermistor Epcos B57861S104F40 100K 1%", 6.5, 2.41,0.25, 43.5,2.5, "black", true, true]; + +resistors = [Honewell, Epcos, EpcosBlue, RWM04106R80J, RIE1212UB5C5R6]; +// +// Aluminium clad resistors used for heated beds and dummy loads. +// +// l w tab hp vp t hd clr h wire +THS10 = [ "THS10", 17, 17, 4.8, 11.3, 12.4, 2.5, 2.4, 1.9, 9, 30.0]; +THS15 = [ "THS15", 21, 21, 6.0, 14.3, 15.9, 3.2, 2.4, 1.9, 11, 36.5]; +THS25 = [ "THS25", 29, 28, 9.4, 18.3, 19.8, 3.2, 3.3, 2.8, 15, 47]; +THS50 = [ "THS50", 51, 30, 10.7, 39.7, 21.4, 3.2, 3.3, 2.8, 17, 72.5]; + +al_clad_resistors = [ THS10, THS15, THS25, THS50]; +// +// Thermal cutout used on heated beds. +// +TC = ["TC", 31.4, 9.13, 0.8, 3.9, 24, 14.56, 11.3, 14.13, 2, 25, 10]; + +thermal_cutouts = [ TC ]; + +use diff --git a/vitamins/d_connector.scad b/vitamins/d_connector.scad new file mode 100644 index 0000000..655c736 --- /dev/null +++ b/vitamins/d_connector.scad @@ -0,0 +1,191 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! D-connectors. Can be any number of ways, male or female, solder buckets, PCB mount or IDC, with or without pillars. +// +include <../core.scad> + +d_pillar_color = grey90; +d_plug_shell_color = grey80; +d_plug_insulator_color = grey20; + +function d_flange_length(type) = type[1]; //! Length of the flange +function d_lengths(type) = type[2]; //! Lengths of the D for plug and socket +function d_hole_pitch(type) = type[3]; //! Mounting hole pitch +function d_widths(type) = type[4]; //! Widths of the D for plug and socket +function d_flange_width(type) = type[5]; //! Width of the flange +function d_height(type) = type[6]; //! From the front to the back of the metal part +function d_front_height(type) = type[7]; //! From the back of the flange to the front +function d_flange_thickness(type) = type[8]; //! Thickness of the flange +function d_ways(type) = type[9]; //! Number of ways +function d_mate_distance(type) = 8.5; //! Spacing when mated +function d_pcb_offset(type) = d_height(type) - d_front_height(type) + 2; //! Height of the back of the flange above the PCB + +function d_slot_length(type) = d_lengths(type)[0] + 3; //! Slot to clear the back + +module d_connector_holes(type) //! Place children at the screw hole positions + for(end = [-1, 1]) + translate([end * d_hole_pitch(type) / 2, 0]) + children(); + +module d_pillar() { //! Draw a pillar for a D-connector + vitamin("d_pillar(): D-type connector pillar"); + rad = 5.37 / 2; + height = 4.5; + screw = 2.5; + screw_length = 8; + color(d_pillar_color) { + translate_z(-screw_length) + cylinder(d = screw, h = screw_length + 1); + + linear_extrude(height = height) + difference() { + circle(r = rad, $fn = 6); + circle(d = screw); + } + } +} + +module d_plug(type, socket = false, pcb = false, idc = false) { //! Draw specified D plug, which can be IDC, PCB or plain solder bucket + hole_r = 3.05 / 2; + dwall = 0.5; + + flange_length = d_flange_length(type); + d_length = d_lengths(type)[socket ? 1 : 0]; + hole_pitch = d_hole_pitch(type); + d_width = d_widths(type)[socket ? 1 : 0]; + flange_width = d_flange_width(type); + front_height = d_front_height(type); + back_height = d_height(type) - front_height; + pins = d_ways(type); + + desc = idc ? "IDC" : pcb ? "PCB mount" : ""; + vitamin(str(socket ? "d_socket(" : "d_plug(", type[0], arg(pcb, false, "pcb"), arg(idc, false, "idc"), + "): D-type ", pins, " way ", desc, socket ? " socket" : " plug")); + + module D(length, width, rad) { + d = width / 2 - rad; + offset = d * sin(10); + + hull() + for(x = [-1, 1], y = [-1, 1]) + translate([x * (length / 2 - rad) + y * x * offset, y * (width / 2 - rad)]) + circle(rad); + } + + module pin_positions() + for($i = [1 : pins]) + translate([($i - (pins + 1) / 2) * 2.77 / 2, ($i % 2 - 0.5) * 2.84]) + children(); + // + // Shell + // + color(d_plug_shell_color) { + linear_extrude(height = d_flange_thickness(type)) + difference() { + rounded_square([flange_length, flange_width], 2); + + d_connector_holes(type) + circle(hole_r); + } + + linear_extrude(height = front_height, convexity = 5) + difference() { + D(d_length, d_width, 2.5); + D(d_length - 2 * dwall, d_width - 2 * dwall, 2.5 - dwall); + } + + if(!idc) + rotate([0,180,0]) + linear_extrude(height = back_height, convexity = 5) + D(d_lengths(type)[0] + 2 * dwall, d_widths(type)[0] + 2 * dwall, 2.5 + dwall); + + } + // + // Insulator + // + color(d_plug_insulator_color) { + translate_z(d_flange_thickness(type) + eps) + rotate([0, 180, 0]) + linear_extrude(height = back_height + 1 + d_flange_thickness(type), convexity = 5) + D(d_length - dwall, d_width - dwall, 2.5 - dwall/2); + + if(socket) + linear_extrude(height = front_height - eps, convexity = 5) + difference() { + D(d_length - dwall, d_width - dwall, 2.5 - dwall/2); + + pin_positions() + circle(r = 0.7); + } + if(idc) { + translate_z(-2.4 / 2) + cube([((pins + 1) / 2) * 2.77 + 6, flange_width, 2.4], center = true); + + translate_z(-14.4 / 2) + cube([pins * 1.27 + 7.29, flange_width, 14.4], center = true); + } + } + // + // Pins + // + color("gold") { + if(!socket) + translate_z(-0.5) + pin_positions() + hull() { + pin_r = 0.5; + cylinder(r = pin_r, h = eps); + + translate_z(front_height - pin_r) + sphere(pin_r); + } + + if(pcb) + rotate([0, 180, 0]) { + linear_extrude(height = back_height + 1 + 4.5) + pin_positions() + circle(r = 0.75 / 2, $fn = 12); + + linear_extrude(height = back_height + 1 + 1) + pin_positions() + circle(r = 0.75, $fn = 12); + } + + if(!pcb && !idc) + rotate([0, 180, 0]) + pin_positions() + rotate(180 + ($i % 2) * 180) + render() difference() { + linear_extrude(height = 8) + difference() { + circle(1); + + circle(0.45); + } + translate([0, 2.1, 8]) + rotate([45, 0, 0]) + cube([3, 3, 3], center = true); + } + } +} + +module d_socket(connector, pcb = false, idc = false) //! Draw specified D socket, which can be IDC, PCB or plain solder bucket + d_plug(connector, true, pcb = pcb, idc = idc); diff --git a/vitamins/d_connectors.scad b/vitamins/d_connectors.scad new file mode 100644 index 0000000..3b49dfe --- /dev/null +++ b/vitamins/d_connectors.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// D-connectors +// +DCONN9 = ["DCONN9", 30.81, [18, 16.92], 24.99, [9.26, 8.38], 12.55, 10.72, 6.693, 1.12, 9]; +DCONN15 = ["DCONN15", 39.14, [26.25, 25.25], 33.32, [9.26, 8.38], 12.55, 10.72, 6.693, 1.12, 15]; +DCONN25 = ["DCONN25", 53.04, [40, 38.96], 47.04, [9.26, 8.38], 12.55, 10.72, 6.693, 1.12, 25]; + +d_connectors = [DCONN9, DCONN15, DCONN25]; + +use diff --git a/vitamins/display.scad b/vitamins/display.scad new file mode 100644 index 0000000..e4315a4 --- /dev/null +++ b/vitamins/display.scad @@ -0,0 +1,109 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! LCD dispays. +// +include <../core.scad> + +use +use + +function display_width(type) = type[2]; //! Width of the metal part +function display_height(type) = type[3]; //! Depth of the metal part +function display_thickness(type) = type[4]; //! Height of the metal part +function display_pcb(type) = type[5]; //! PCB mounted on the back +function display_pcb_offset(type) = type[6]; //! 3D offset of the PCB centre +function display_aperture(type) = type[7]; //! Size of the aperture including its depth +function display_touch_screen(type) = type[8]; //! Touch screen position and size +function display_threads(type) = type[9]; //! Length that studs protrude from the PCB holes +function display_ribbon(type) = type[10]; //! Keep out region for ribbon cable + +function display_ts_thickness(type) = let(ts = display_touch_screen(type)) ts ? ts[1].z : 0; //! Touch screen thickness or 0 + +function display_depth(type) = display_ts_thickness(type) + display_thickness(type) + display_pcb_offset(type).z + pcb_thickness(display_pcb(type)); //! Total thickness including touch screen and PCB + +module display_aperture(type, clearance, clear_pcb = false) { //! Make aperture cutout + aperture = display_aperture(type); + ts = display_touch_screen(type); + pcb = display_pcb(type); + rb = display_ribbon(type); + + translate([aperture[0].x, aperture[0].y, -10]) + cube([aperture[1].x - aperture[0].x, aperture[1].y - aperture[0].y, 20]); + + if(ts) + translate([ts[0].x - clearance, ts[0].y - clearance, -clearance]) + cube([ts[1].x - ts[0].x + 2 * clearance, ts[1].y - ts[0].y + 2 * clearance, ts[1].z + clearance + eps]); + + if(rb) + translate([rb[0].x, rb[0].y,0]) + cube([rb[1].x - rb[0].x, rb[1].y - rb[0].y, ts[1].z + display_depth(type) + 2]); + + if(clear_pcb) + translate([display_pcb_offset(type).x, display_pcb_offset(type).y, display_depth(type) / 2 + 0.5 + display_ts_thickness(type)]) + cube([pcb_length(pcb) + 2 * clearance, pcb_width(pcb) + 2 * clearance, display_depth(type) + 1], center = true); + else + translate_z(display_depth(type) / 2 + 0.5) + cube([display_width(type) + 2 * clearance, display_height(type) + 2 * clearance, display_depth(type) + 1], center = true); +} + +module display(type) { //! Draw specified display + vitamin(str("display(", type[0], "): ", type[1])); + + w = display_width(type); + h = display_height(type); + t = display_thickness(type); + pcb = display_pcb(type); + + gap = display_pcb_offset(type).z; + aperture = display_aperture(type); + ts = display_touch_screen(type); + + not_on_bom() { + translate_z(display_ts_thickness(type)) { + difference() { + color("silver") + rounded_rectangle([w, h, t], 0.5, center = false); + + color("black") + translate([aperture[0].x, aperture[0].y, - eps]) + cube([aperture[1].x - aperture[0].x, aperture[1].y - aperture[0].y, aperture[1].z]); + } + + if(gap) + color("black") + translate_z(t + gap / 2) + cube([w - 1, h - 1, gap], center = true); + + translate([0, 0, display_thickness(type)] + display_pcb_offset(type)) { + pcb(pcb); + + if(display_threads(type)) + pcb_screw_positions(pcb) + vflip() + screw(pcb_screw(pcb), pcb_thickness(pcb) + display_threads(type)); + } + } + if(ts) + color("white", 0.15) + translate([ts[0].x, ts[0].y, 0]) + cube([ts[1].x - ts[0].x, ts[1].y - ts[0].y, ts[1].z - eps]); + } +} diff --git a/vitamins/displays.scad b/vitamins/displays.scad new file mode 100644 index 0000000..52b0606 --- /dev/null +++ b/vitamins/displays.scad @@ -0,0 +1,52 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +HDMI5PCB = ["", "", 121.11, 77.93, 1.65, 0, 2.2, 0, "mediumblue", false, [[4.6, 4.9], [4.6, -3.73], [97.69, -3.73], [97.69, 4.9]], + [[ 47.245,-2.5, 90, "usb_uA"], + [-53.14, -4.4, 90, "hdmi"], + [ 53.7, 40.6, 0, "chip", 14, 14, 1], + [ 59.8, 25.2, 0, "2p54socket", 13, 2, false, 13.71], + [ 59.8, 10.12, 0, "2p54header", 13, 2, true], + ], + []]; + +HDMI5 = ["HDMI5", "HDMI display 5\"", 121, 76, 2.85, HDMI5PCB, + [0, 0, 1.9], // pcb offst + [[-54, -30.225], [54, 34.575, 0.5]], // aperture + [[-58.7, -34], [58.7, 36.25, 1]], // touch screen + 2, // thread length + [[-2.5, -39], [10.5, -33]], // clearance need for the ts ribbon + ]; + +LCD1602APCB = ["", "", 80, 36, 1.65, 0, 2.9, 5, "green", false, [[-2.5, -2.5], [-2.5, 2.5], [2.5, 2.5], [2.5, -2.5]], + [ [-27.05, - 2.5, 0, "2p54header", 16, 1] + ], + []]; + +LCD1602A = ["LCD1602A", "LCD display 1602A", 71.3, 24.3, 7.0, LCD1602APCB, + [0, 0, 0], // pcb offst + [[-64.5 / 2, -14.5 / 2], [64.5 / 2, 14.5 / 2, 0.6]], // aperture + [], // touch screen + 0, // thread length + [], // clearance need for the ts ribbon + ]; + +displays = [LCD1602A, HDMI5]; + +use diff --git a/vitamins/e3d.scad b/vitamins/e3d.scad new file mode 100644 index 0000000..f463c99 --- /dev/null +++ b/vitamins/e3d.scad @@ -0,0 +1,180 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// E3D hot ends +// +include <../core.scad> +include +include +include +include + +use <../utils/tube.scad> + +rad_dia = 22; // Diam of the part with ailettes +rad_nb_ailettes = 11; +rad_len = 26; + +nozzle_h = 5; + +module e3d_nozzle(type) { + color("gold") { + rotate_extrude() + polygon([ + [0.2, 0], + [0.2, 2], + [1.5, 2], + [0.65, 0] + ]); + + translate_z(2) + cylinder(d = 8, h = nozzle_h - 2, $fn=6); + } +} + +resistor_len = 22; +resistor_dia = 6; + +heater_width = 16; +heater_length = 20; +heater_height = 11.5; + +heater_x = 4.5; +heater_y = heater_width / 2; + +fan_x_offset = rad_dia / 2 + 4; + +module e3d_resistor(type) { + translate([11 - heater_x, -3 - heater_y, heater_height / 2 + nozzle_h]) { + color("grey") + rotate([-90, 0, 0]) + cylinder(r = resistor_dia / 2, h = resistor_len); + + color("red") + translate([-3.5/2, resistor_len + 3.5/2 + 1, 0]) { + cylinder(d = 3.5, h = 36); + + translate([3.5, 0, 0]) + cylinder(r = 3.5 / 2, h = 36); + } + } +} + +module heater_block(type) { + translate_z(-hot_end_length(type)) { + translate_z(nozzle_h) + color("lightgrey") + translate([-heater_x, -heater_y, 0]) + cube([heater_length, heater_width, heater_height]); + + e3d_resistor(type); + e3d_nozzle(type); + } +} + + + +module e3d_fan_duct(type) { + color("DeepSkyBlue") + render() difference() { + hull() { + translate([-8, -23 / 2, 0]) + cube([eps, 23, 26]); + + translate([fan_x_offset, -30 / 2, 0]) + cube([eps, 30, 30]); + } + cylinder(h = 70, d = rad_dia + 0.1, center = true); // For rad + + translate_z(15) + rotate([0, 90, 0]) + cylinder(d = rad_dia, h = 50); + } +} + +module e3d_fan(type) { + e3d_fan_duct(type); + + translate([fan_x_offset + 5, 0, 15]) + rotate([0, 90, 0]) + not_on_bom() + fan(fan30x10); +} + +module e3d_hot_end(type, filament, naked = false) { + insulator_length = hot_end_insulator_length(type); + inset = hot_end_inset(type); + h_ailettes = rad_len / (2 * rad_nb_ailettes - 1); + + vitamin(str("e3d_hot_end(", type[0], ", ", filament, "): Hot end ", hot_end_part(type), " ", filament, "mm")); + + translate_z(inset - insulator_length) + color(hot_end_insulator_colour(type)) + rotate_extrude() + difference() { + union() { + for (i = [0 : rad_nb_ailettes - 1]) + translate([0, (2 * i) * h_ailettes]) + square([rad_dia / 2, h_ailettes]); + + square([hot_end_insulator_diameter(type) / 2, insulator_length]); + + translate([0, -10]) + square([2, 10]); + } + square([3.2 / 2, insulator_length]); // Filament hole + + translate([hot_end_groove_dia(type) / 2, insulator_length - inset - hot_end_groove(type)]) + square([100, hot_end_groove(type)]); + } + + rotate(90) + heater_block(type); + + if(!naked) + translate_z(inset - insulator_length) + e3d_fan(); +} + +module e3d_hot_end_assembly(type, filament, naked = false) { + bundle = 3.2; + + e3d_hot_end(type, filament, naked); + + // Wire and ziptie + if(!naked) + rotate(10) { + dia = hot_end_insulator_diameter(type); + scale([1, (bundle + dia) / dia]) + translate([0, -bundle / 2, -7]) + rotate(-110) + ziptie(small_ziptie, dia / 2); + + translate([0, -dia / 2 - bundle / 2, 20]) + scale([0.7, bundle / 6.4]) + difference() { + tubing(HSHRNK64, 60); + + translate_z(20) + cube([10, 10, 60], center = true); + } + } + +} diff --git a/vitamins/fan.scad b/vitamins/fan.scad new file mode 100644 index 0000000..abb940f --- /dev/null +++ b/vitamins/fan.scad @@ -0,0 +1,170 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Axial fans. +//! +//! Can draw three styles: solid, open frame and open frame with screw bosses. +// +include <../core.scad> +use +use +use +use <../utils/tube.scad> + +fan_colour = grey20; + +function fan_width(type) = type[0]; //! Width of square +function fan_depth(type) = type[1]; //! Depth of fan +function fan_bore(type) = type[2]; //! Diameter of the hole for the blades +function fan_hole_pitch(type) = type[3]; //! Screw hole pitch +function fan_screw(type) = type[4]; //! Screw type +function fan_hub(type) = type[5]; //! Diameter of the hub +function fan_thickness(type) = type[6]; //! Thickness of the frame +function fan_outer_diameter(type) = type[7]; //! Outside diameter of the frame +function fan_blades(type) = type[8]; //! The number of blades +function fan_boss_d(type) = type[9]; //! Diameter of the screw bosses +function fan_aperture(type) = type[10] ? type[10] : fan_bore(type); //! Optional diameter for the aperture, which can be bigger than the bore it has flared corners. + +module fan(type) { //! Draw specified fan, origin in the centre + width = fan_width(type); + depth = fan_depth(type); + thickness = fan_thickness(type); + hole_pitch = fan_hole_pitch(type); + corner_radius = width / 2 - hole_pitch; + screw = fan_screw(type); + + vitamin(str("fan(fan", width, "x", depth, "): Fan ", width, "mm x ", depth, "mm")); + + module squarish(s, n) { + polygon([ + for(i = [0 : n]) [i * s.x / n, s.y + (i % 2) * eps], + for(i = [0 : n]) [s.x - i * s.x / n, (i % 2) * eps], + ]); + } + + module shape() + difference() { + //overall outside + rounded_square([width, width], corner_radius); + + //main inside bore, less hub + difference() { + circle(fan_bore(type) / 2); + circle(fan_hub(type) / 2); + } + + //Mounting holes + fan_hole_positions(type) + circle(screw_clearance_radius(screw)); + } + + color(fan_colour) { + middle = depth - 2 * thickness; + if(middle > 0) { + for(z = [-1, 1]) + translate_z(z * (depth - thickness) / 2) + linear_extrude(height = thickness, center = true) + shape(); + + linear_extrude(height = middle, center = true) + difference() { + shape(); + difference() { + circle(sqrt(2) * width / 2); + circle(d = fan_outer_diameter(type)); + + if(fan_boss_d(type)) + for(i = [-1, 1]) + hull() + for(side = [-1, 1]) + translate([hole_pitch * side * i, hole_pitch * side]) + circle(d = fan_boss_d(type)); + } + } + } + else + linear_extrude(height = depth, center = true) + shape(); + + // Blades + blade_ir = fan_hub(type) / 2 - 1; + blade_len = fan_bore(type) / 2 - 0.75 - blade_ir; + linear_extrude(height = depth - 1, center = true, convexity = 4, twist = -30, slices = round(depth / 2)) + for(i = [0 : fan_blades(type) - 1]) + rotate((360 * i) / fan_blades(type)) + translate([blade_ir, -1.5 / 2]) + squarish([blade_len, 1.5], round(blade_len / 2)); + } +} + +module fan_hole_positions(type, z = undef) { //! Position children at the screw hole positions + hole_pitch = fan_hole_pitch(type); + for(x = [-hole_pitch, hole_pitch]) + for(y = [-hole_pitch, hole_pitch]) + translate([x, y, is_undef(z) ? fan_depth(type) / 2 : z]) + children(); +} + +module fan_holes(type, poly = false, screws = true, h = 100) { //! Make all the holes for the fan, or just the aperture if ```screws``` is false. Set ```poly``` true for poly_holes. + hole_pitch = fan_hole_pitch(type); + screw = fan_screw(type); + + extrude_if(h) { + if(screws) + fan_hole_positions(type, z = 0) + if(poly) + poly_circle(r = screw_clearance_radius(screw)); + else + drill(screw_clearance_radius(screw), 0); + + difference() { + intersection() { + square(fan_bore(type), center = true); + + circle(d = fan_aperture(type)); + } + if(screws) + fan_hole_positions(type, z = 0) + circle(d = washer_diameter(screw_washer(screw)) + 1); + } + } +} + +function nut_and_washer_thickness(screw, nyloc) = washer_thickness(screw_washer(screw)) + nut_thickness(screw_nut(screw), nyloc); +function fan_screw_depth(type) = fan_boss_d(type) ? fan_depth(type) : fan_thickness(type); +function fan_screw_length(type, thickness) = screw_longer_than(thickness + fan_screw_depth(type) + nut_and_washer_thickness(fan_screw(type), true)); //! Screw length required + +module fan_assembly(type, thickness, include_fan = true) { //! Fan with its fasteners + translate_z(-fan_depth(type) / 2) { + if(include_fan) + fan(type); + + screw = fan_screw(type); + nut = screw_nut(screw); + fan_hole_positions(type) { + translate_z(thickness) + screw_and_washer(screw, fan_screw_length(type, thickness)); + + translate_z(include_fan ? -fan_screw_depth(type) : 0) + vflip() + nut(nut, true); + } + } +} diff --git a/vitamins/fans.scad b/vitamins/fans.scad new file mode 100644 index 0000000..55b78ce --- /dev/null +++ b/vitamins/fans.scad @@ -0,0 +1,46 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include + +// w d b h s h t o b b a +// i e o o c u h u l o p +// d p r l r b i t a s p +// t t e e e c e d s e +// h h w d k r e r +// p i n s d t +// i a e d u +// t s i r +// c s a e +// h +// +fan120x25= [120,25, 116,52.5, M4_dome_screw, 41, 4, 140, 9, 0, 137]; +fan80x38 = [80, 38, 75, 35.75, M4_dome_screw, 40, 4.3, 84, 7, 0, 85]; +fan80x25 = [80, 25, 75, 35.75, M4_dome_screw, 40, 4.3, 84, 7, 0, 85]; +fan70x15 = [70, 15, 66, 30.75, M4_dome_screw, 29, 3.8, 70 ,7, 0, undef]; +fan60x25 = [60, 25, 57, 25, M4_dome_screw, 31.5, 3.6, 64, 7, 0, 63]; +fan60x15 = [60, 15, 57, 25, M4_dome_screw, 29, 2.4, 60, 7, 7.7, 63]; +fan50x15 = [50, 15, 48, 20, M4_dome_screw, 25, 12.5,100,7, 0, undef]; +fan40x11 = [40, 11, 37, 16, M3_dome_screw, 25, 7.5,100, 9, 0, undef]; +fan30x10 = [30, 10, 27, 12, M3_dome_screw, 17, 10, 100, 5, 0, undef]; +fan25x10 = [25, 10, 24, 10, M2p5_pan_screw, 16, 10, 100, 5, 0, undef]; + +fans = [fan25x10, fan30x10, fan40x11, fan50x15, fan60x15, fan60x25, fan70x15, fan80x25, fan80x38, fan120x25]; + +use diff --git a/vitamins/fuseholder.scad b/vitamins/fuseholder.scad new file mode 100644 index 0000000..56e77f0 --- /dev/null +++ b/vitamins/fuseholder.scad @@ -0,0 +1,133 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! 20mm panel mount fuse holder. +// +include <../core.scad> +include +use <../utils/tube.scad> + +module fuseholder_hole(h = 100) //! Hole with flats for fuseholder + extrude_if(h) + intersection() { + circle(d = 12); + + square([100, 11], center = true); + } + +function fuseholder_diameter() = 18.8; //! Outside diameter of flange + +module fuseholder(thickness) { //! Fuseholder with nut in place for specified panel thickness + vitamin(str("fuseholder(6): Fuse holder 20mm")); + flange_d = fuseholder_diameter(); + flange_t = 2; + height = 33.2; + thread_d = 11.7; + thread = 15; + bot_d = 10.4; + top_d = 8.7; + flat = 10.8; + nut_d = 18.8; + nut_t = 6; + nut_flange_t = 1.5; + spade = 5.4; + contact_slot_z = 20; + contact_slot_w = 3.2; + contact_slot_d = 7; + + contact_w = 2.8; + contact_t = 0.3; + contact_l1 = 11; + contact_l2 = 6; + // + // Nut + // + vflip() + translate_z(thickness) + explode(height) + color("dimgrey") { + tube(or = nut_d / 2, ir = 5, h = nut_flange_t, center = false); + + linear_extrude(height = nut_t) + difference() { + circle(d = nut_d, $fn = 6); + + circle(5); + } + } + // + // Body + // + explode(height + 5, offset = -height - 4) { + color("dimgrey") { + tube(or = flange_d / 2, ir = 5.2, h = flange_t, center = false); + + cylinder(r = 5, h = flange_t - 1); + + linear_extrude(height = flange_t) + difference() { + circle(r = 5); + + square([8, 1.5], center = true); + } + + vflip() { + linear_extrude(height = thread) + intersection() { + circle(d = thread_d); + + square([100, 10.8], center = true); + } + render() difference() { + translate_z(thread) + cylinder(d1 = bot_d, d2 = top_d, h = height - flange_t - thread); + + for(side = [-1, 1]) + translate([side * (contact_slot_d / 2 + 1) - 1, -contact_slot_w / 2, contact_slot_z]) + cube([2, contact_slot_w, 100]); + } + } + } + // + // Side contacts + // + color("silver") vflip() + for(side = [-1, 1]) + translate([side * contact_slot_d / 2, 0, contact_slot_z]) + rotate([0, -70, 90 - side * 90]) + linear_extrude(height = contact_t, center = true) difference() { + hull() { + square([eps, contact_w], center = true); + + translate([(side > 0 ? contact_l1 : contact_l2) - contact_w / 2, 0]) + circle(d = contact_w); + } + translate([(side > 0 ? contact_l1 : contact_l2) - contact_w / 2, 0]) + circle(d = 1); + } + // + // Bottom contact + // + translate_z(-height + flange_t) + vflip() + rotate(45) + spade(spade3, spade); + } +} diff --git a/vitamins/hot_end.scad b/vitamins/hot_end.scad new file mode 100644 index 0000000..0132d76 --- /dev/null +++ b/vitamins/hot_end.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Hot end models. The E3D models were originally contributed to Mendel90 by Philippe LUC @philfifi +//! +//! Needs updating as mostly obsolete versions. +// +include <../core.scad> + +function hot_end_style(type) = type[1]; //! Basic type, jhead or e3d +function hot_end_part(type) = type[2]; //! Description +function hot_end_total_length(type) = type[3]; //! Length from nozzle tip to the top +function hot_end_inset(type) = type[4]; //! The length that goes into the mounting +function hot_end_insulator_diameter(type) = type[5]; //! Outside diameter +function hot_end_insulator_length(type) = type[6]; //! Length of the insulator +function hot_end_insulator_colour(type) = type[7]; //! Colour of the insulator +function hot_end_groove_dia(type) = type[8]; //! Groove internal diameter +function hot_end_groove(type) = type[9]; //! Groove length +function hot_end_duct_radius(type) = type[10]; //! Require radius to clear the heater block +function hot_end_duct_offset(type) = type[11]; //! Offset of circular duct centre from the nozzle +function hot_end_need_cooling(type) = hot_end_style(type) != e3d; //! Has own fan so don't need cooling hole in the duct +function hot_end_duct_height_nozzle(type) = type[12]; //! Duct height at nozzle end +function hot_end_duct_height_fan(type) = type[13]; //! Duct height at fan end + +function hot_end_length(type) = hot_end_total_length(type) - hot_end_inset(type); //! The amount the hot end extends below its mounting + +use +use + +module hot_end(type, filament, naked = false) { //! Draw specified hot end + if(hot_end_style(type) == jhead) + jhead_hot_end_assembly(type, filament, naked); + + if(hot_end_style(type) == e3d) + e3d_hot_end_assembly(type, filament, naked); +} diff --git a/vitamins/hot_ends.scad b/vitamins/hot_ends.scad new file mode 100644 index 0000000..78fd6fc --- /dev/null +++ b/vitamins/hot_ends.scad @@ -0,0 +1,44 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Hot end descriptions +// +// s p l i d i l c s g g d d d a d a +// t a e n i n e o c r r u u u t u t +// y r n s a s n l r o o c c c c +// l t g e u g o e o o t t t n t f +// e t t l t u w v v o a +// h a h r e e r o h z h n +// t p a f e z e +// o i d w d f i l i +// r t i i i s g e g +// c a d u e h h +// h t s t t t +// h +// +JHeadMk4 = ["JHeadMk4", jhead, "JHead MK4", 64, 5.1, 16, 50, grey20, 12, 4.64, 14, [0, 2.94, -5], 20, 20]; +JHeadMk5 = ["JHeadMk5", jhead, "JHead MK5", 51.2, 5.1, 16, 40, grey20, 12, 4.64, 13, [0, 2.38, -5], 20, 20]; +E3Dv5 = ["E3Dv5", e3d, "E3D V5 direct", 70, 3.7, 16, 50.1, "silver", 12, 6, 15, [1, 5, -4.5], 14.5, 28]; +E3Dv6 = ["E3Dv6", e3d, "E3D V6 direct", 62, 3.7, 16, 42.7, "silver", 12, 6, 15, [1, 5, -4.5], 14, 21]; +E3D_clone = ["E3D_clone", e3d, "E3D clone aliexpress",66, 6.8, 16, 46, "silver", 12, 5.6, 15, [1, 5, -4.5], 14.5, 21]; + +hot_ends = [JHeadMk5, E3Dv5, E3Dv6, E3D_clone]; + +use diff --git a/vitamins/iec.scad b/vitamins/iec.scad new file mode 100644 index 0000000..0c7152e --- /dev/null +++ b/vitamins/iec.scad @@ -0,0 +1,228 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! IEC mains inlets and outlet. +// +include <../core.scad> +use +use +use +use +include + +function iec_part(type) = type[1]; //! Description +function iec_screw(type) = type[2]; //! Screw type +function iec_pitch(type) = type[3]; //! Screw hole pitch +function iec_slot_w(type) = type[4]; //! Body width +function iec_slot_h(type) = type[5]; //! Body height +function iec_slot_r(type) = type[6]; //! Body corner radius +function iec_bezel_w(type) = type[7]; //! Bezel width +function iec_bezel_h(type) = type[8]; //! Bezel height +function iec_bezel_r(type) = type[9]; //! Bezel corner radius +function iec_bezel_t(type) = type[10]; //! Bezel thickness +function iec_flange_w(type) = type[11]; //! Flange width not including the lugs +function iec_flange_h(type) = type[12]; //! Flange height +function iec_flange_r(type) = type[13]; //! Flange corner radius +function iec_flange_t(type) = type[14]; //! Flange thickness +function iec_width(type) = type[15]; //! Widest part including the lugs +function iec_depth(type) = type[16]; //! Depth of the body below the flange +function iec_spades(type) = type[17]; //! Spade type +function iec_male(type) = type[18]; //! True for an outlet + +insert_overlap = 1.1; // chosen to make cap screws 10mm long. + +module iec(type) { //! Draw specified IEC connector + vitamin(str("iec(", type[0], "): ", iec_part(type))); + + pin_w = 2; + pin_d = 4; + pin_h1 = 12; + pin_h2 = 15; + pin_chamfer = 1; + + module pin(h) + color("silver") + hull() { + translate_z(h / 2) + cube([pin_w - pin_chamfer, pin_d - pin_chamfer, h], center = true); + + translate_z((h - pin_chamfer) / 2) + cube([pin_w, pin_d, h - pin_chamfer], center = true); + } + + socket_w = 24; + socket_w2 = 14; + socket_h = 16.5; + socket_h2 = 12; + socket_d = 17; + socket_r = 3; + socket_r2 = 0.5; + socket_offset = iec_bezel_h(type) / 2 - socket_h / 2 - (iec_bezel_w(type) - socket_w) / 2; + + module socket_shape() + hull() + for(side = [-1, 1]) { + translate([side * (socket_w / 2 - socket_r), -socket_h / 2 + socket_r]) + circle(socket_r); + + translate([side * (socket_w / 2 - socket_r2), -socket_h / 2 + socket_h2 + socket_r2]) + circle(socket_r2); + + translate([side * (socket_w2 / 2 - socket_r2), socket_h / 2 - socket_r2]) + circle(socket_r2); + } + + module oriffice_shape() + translate([0, socket_offset]) + if(iec_male(type)) + difference() { + offset(3) + socket_shape(); + + difference() { + offset(-1) socket_shape(); + + translate([0, 2]) + square([2.4, 5], center = true); + + for(side = [-1, 1]) + translate([side * 7, -2]) + square([2.4, 5], center = true); + } + } + else + socket_shape(); + + color(grey20) { + // Flange + flange_t = iec_flange_t(type); + linear_extrude(height = flange_t) + difference() { + hull() { + rounded_square([iec_flange_w(type), iec_flange_h(type)], iec_flange_r(type)); + + iec_screw_positions(type) + circle(d = iec_width(type) - iec_pitch(type)); + } + oriffice_shape(); + + iec_screw_positions(type) + circle(socket_r); + } + head_r = screw_head_radius(iec_screw(type)); + screw_r = screw_clearance_radius(iec_screw(type)); + iec_screw_positions(type) + rotate_extrude() + difference() { + translate([screw_r, 0]) + square([socket_r - screw_r, flange_t]); + + translate([0, flange_t - head_r]) + rotate(45) + square(10); + } + // Bezel + translate_z(iec_flange_t(type)) + linear_extrude(height = iec_bezel_t(type)) + difference() { + rounded_square([iec_bezel_w(type), iec_bezel_h(type)], iec_bezel_r(type)); + + oriffice_shape(); + } + + // Body + h = socket_d - iec_flange_t(type) - iec_bezel_t(type); + translate_z(-h) + linear_extrude(height = h) + difference() { + rounded_square([iec_slot_w(type), iec_slot_h(type)], iec_slot_r(type)); + oriffice_shape(); + } + // Back + translate_z(-iec_depth(type)) + rounded_rectangle([iec_slot_w(type), iec_slot_h(type), iec_depth(type) - h], iec_slot_r(type), center = false); + } + if(!iec_male(type)) + translate([0, socket_offset, iec_flange_t(type) + iec_bezel_t(type) - socket_d]) { + translate([0, 2]) + pin(pin_h2); + + for(side = [-1, 1]) + translate([side * 7, -2]) + pin(pin_h1); + } + for(spade = iec_spades(type)) + translate([spade[2], spade[3], -iec_depth(type)]) + rotate([180, 0, spade[4]]) + spade(spade[0], spade[1]); +} + +module iec_screw_positions(type) //! Position children at the screw holes + for(side = [-1, 1]) + translate([side * iec_pitch(type) / 2, 0]) + children(); + +module iec_holes(type, h = 100, poly = false, horizontal = false, insert = false) { //! Drill the required panel holes + clearance = 0.2; + + iec_screw_positions(type) + if(insert) + insert_hole(screw_insert(iec_screw(type)), insert_overlap, horizontal = horizontal); + else + if(horizontal) + teardrop_plus(r = screw_clearance_radius(iec_screw(type)), h = h); + else + if(poly) + poly_cylinder(r = screw_clearance_radius(iec_screw(type)), h = h, center = true); + else + drill(screw_clearance_radius(iec_screw(type)), h); + + extrude_if(h) + hull() + for(x = [-1, 1], y = [-1, 1], sag = horizontal && y > 1 ? layer_height : 0) + translate([x * (iec_slot_w(type) / 2 - iec_slot_r(type)), y * (iec_slot_h(type) / 2 - iec_slot_r(type) + sag )]) + if(horizontal) + teardrop(0, iec_slot_r(type) + clearance / 2 + layer_height / 4); + else + drill(iec_slot_r(type) + clearance / 2, 0); +} + +module iec_assembly(type, thickness) { //! Assembly with fasteners given panel thickness + screw = iec_screw(type); + washer = screw_washer(screw); + nut = screw_nut(screw); + insert = screw_insert(screw); + screw_length = thickness ? screw_longer_than(iec_flange_t(type) + thickness + washer_thickness(washer) + nut_thickness(nut, true)) + : screw_shorter_than(iec_flange_t(type) + insert_hole_length(insert) + insert_overlap); + + iec(type); + + iec_screw_positions(type) { + translate_z(iec_flange_t(type)) + screw(screw, screw_length); + + if(thickness) + translate_z(-thickness) + vflip() + nut_and_washer(nut, true); + else + insert(insert); + } +} diff --git a/vitamins/iecs.scad b/vitamins/iecs.scad new file mode 100644 index 0000000..d3e0ea2 --- /dev/null +++ b/vitamins/iecs.scad @@ -0,0 +1,47 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include +include + +fused_spades = [[spade6p4, 14, -7, 0, 0], + [spade6p4, 14, 7, 0, 0], + [spade6p4, 14, 0, 11, 0], + [spade4p8, 8.5, -7, -9, 90], + [spade4p8, 8.5, 7, -9, 90]]; + +inlet_spades = [[spade6p4, 9, -7, -5.5, 0], + [spade6p4, 9, 7, -5.5, 0], + [spade6p4, 9, 0, 5.5, 0]]; +// +// p s p s s s b b b b f f f f w d s m +// a c i l l l e e e e l l l l i e p a +// r r t o o o z z z z a a a a d p a l +// t e c t t t e e e e n n n n t t d e +// w h l l l l g g g g h h e +// w h r e e e e s +// w h r t +// w h r t +// +IEC_fused_inlet = ["IEC_fused_inlet", "IEC fused inlet", M3_cs_cap_screw, 36, 27.3, 31.2, 3, 28, 31, 2, 2.5, 30, 33, 4, 2.5, 44, 21, fused_spades, false ]; +IEC_inlet = ["IEC_inlet", "IEC inlet", M3_cs_cap_screw, 40, 28.2, 20.2, 3, 28, 20.5, 4, 2.5, 37, 23, 1, 2.5, 48, 14, inlet_spades, false ]; +IEC_inlet_atx = ["IEC_inlet_atx", "IEC inlet for ATX", M3_cs_cap_screw, 40, 27.0, 19.0, 3, 30, 22, 2, 2.0, 30, 22, 2, 4.0, 50, 13, inlet_spades, false ]; +IEC_outlet = ["IEC_outlet", "IEC outlet", M3_cs_cap_screw, 40, 32, 24, 3, 28, 20.5, 2, 0.0, 29, 29, 2, 2.9, 50, 23, inlet_spades, true ]; + +iecs = [IEC_inlet, IEC_inlet_atx, IEC_fused_inlet, IEC_outlet]; +use diff --git a/vitamins/insert.scad b/vitamins/insert.scad new file mode 100644 index 0000000..f85a195 --- /dev/null +++ b/vitamins/insert.scad @@ -0,0 +1,104 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Heatfit threaded inserts. Can be pushed into thermoplastics using a soldering iron with a conical bit set to 200°C. +// +include <../core.scad> + +function insert_length(type) = type[1]; //! Length +function insert_outer_d(type) = type[2]; //! Outer diameter at the top +function insert_hole_radius(type) = type[3] / 2; //! Radius of the required hole in the plastic +function insert_screw_diameter(type) = type[4]; //! Screw size +function insert_barrel_d(type) = type[5]; //! Diameter of the main barrel +function insert_ring1_h(type) = type[6]; //! Height of the top and middle rings +function insert_ring2_d(type) = type[7]; //! Diameter of the middle ring +function insert_ring3_d(type) = type[8]; //! Diameter of the bottom ring + +function insert_hole_length(type) = round_to_layer(insert_length(type)); + +module insert(type) { //! Draw specified insert + length = insert_length(type); + ring1_h = insert_ring1_h(type); + + chamfer1 = (insert_ring2_d(type) - insert_barrel_d(type)) / 2; + chamfer2 = (insert_ring3_d(type) - insert_barrel_d(type)) / 2; + ring2_h = ring1_h + chamfer1; + gap = (length - ring1_h - ring2_h- chamfer2) / 3; + + vitamin(str("insert(", type[0], "): Heatfit insert M", insert_screw_diameter(type))); + $fn = 64; + explode(20, offset =[0, 0, -5]) color(brass) translate_z(eps) { + vflip(){ + r1 = insert_screw_diameter(type) / 2; + r2 = insert_barrel_d(type) / 2; + r3 = insert_ring3_d(type) / 2; + r4 = insert_ring2_d(type) / 2; + r5 = insert_outer_d(type) / 2; + h1 = ring1_h; + h2 = ring1_h + gap; + h3 = ring1_h + gap + ring2_h; + h4 = ring1_h + gap + ring2_h + gap; + rotate_extrude() + polygon([ + [r1, 0], + [r1, length], + [r2, length], + [r3, length - chamfer2], + [r3, h4], + [r2, h4], + [r2, h3], + [r4, h3 - chamfer1], + [r4, h2], + [r2, h2], + [r2, h1], + [r5, h1], + [r5, 0], + ]); + } + } +} + +module insert_hole(type, counterbore = 0, horizontal = false) { //! Make a hole to take an insert, ```counterbore``` is the extra length for the screw + h = insert_hole_length(type); + + render() if(horizontal) { + teardrop_plus(r = insert_hole_radius(type), h = 2 * h); + + if(counterbore) + teardrop_plus(r = insert_screw_diameter(type) / 2 + 0.1, h = 2 * (h + counterbore)); + } + else { + poly_cylinder(r = insert_hole_radius(type), h = 2 * h, center = true); + + if(counterbore) + poly_cylinder(r = insert_screw_diameter(type) / 2 + 0.1, h = 2 * (h + counterbore), center = true); + } +} + +module insert_boss(type, z, wall = 2 * extrusion_width) { //! Make a boss to take an insert + difference() { + ir = insert_hole_radius(type); + linear_extrude(height = z) + poly_ring(corrected_radius(ir) + wall, insert_screw_diameter(type) / 2 + 0.1); + + translate_z(z) + insert_hole(type, max(0, z - insert_hole_length(type) - 2 * layer_height)); + } +} diff --git a/vitamins/inserts.scad b/vitamins/inserts.scad new file mode 100644 index 0000000..4d3362c --- /dev/null +++ b/vitamins/inserts.scad @@ -0,0 +1,43 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Threaded inserts +// +// l o h s b r r r +// e u o c a i i i +// n t l r r n n n +// g e e e r g g g +// t r w e 1 2 3 +// h d l +// d d h d d +// d +// +F1BM2 = [ "F1BM", 4.0, 3.6, 3.2, 2, 3.0, 1.0, 3.4, 3.1 ]; +F1BM2p5 = [ "F1BM2p5", 5.8, 4.6, 4.0, 2.5, 3.65, 1.6, 4.4, 3.9 ]; +F1BM3 = [ "F1BM3", 5.8, 4.6, 4.0, 3, 3.65, 1.6, 4.4, 3.9 ]; +F1BM4 = [ "F1BM4", 8.2, 6.3, 5.6, 4, 5.15, 2.3, 6.0, 5.55 ]; + +inserts = [ F1BM2, F1BM2p5, F1BM3, F1BM4 ]; + +function screw_insert(screw, i = 0) = let(d = screw_radius(screw) * 2) + i >= len(inserts) ? undef + : insert_screw_diameter(inserts[i]) == d ? inserts[i] + : screw_insert(screw, i + 1); +use diff --git a/vitamins/jack.scad b/vitamins/jack.scad new file mode 100644 index 0000000..7e5b784 --- /dev/null +++ b/vitamins/jack.scad @@ -0,0 +1,284 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! 4mm jack sockets and binding posts. Each has a colour for the BOM entry and an optional alternative colour for display. +//! E.g. a "brown" socket for mains live needs to be displayed as "sienna" to look realistic. +// +include <../core.scad> +include +include +use <../utils/tube.scad> +use <../utils/rounded_cylinder.scad> +use + +function jack_4mm_hole_radius() = 8/2; //! Panel hole radius for 4mm jack + +module jack_4mm(colour, thickness, display_colour = false) { //! Draw a 4mm jack socket with nut positioned for specified panel thickness + vitamin(str("jack_4mm(\"", colour, "\", 3", arg(display_colour, false), "): 4mm jack socket ", colour)); + flange_d = 10.6; + flange_t = 3; + flange_id = 4.6; + length = 28.5; + sleaved = 15; + sleaved_d = 6.4; + thread = 10.4; + thread_d = 8; + nut_d = 9.8; + nut_t = 3; + barrel_d = 5.4; + barrel = 18.5; + spade = 7; + + explode(length, offset = -length + flange_t) { + color(display_colour ? display_colour : colour) rotate_extrude() difference() { + union() { + square([flange_d / 2, flange_t]); + + translate([0, -sleaved]) + square([sleaved_d / 2, sleaved]); + + } + square([flange_id, 100], center = true); + } + color("silver") rotate_extrude() difference() { + union() { + translate([0, -thread]) + square([thread_d / 2, thread]); + + *translate([0, -nut_t - thickness]) + square([nut_d / 2, nut_t]); + + translate([0, -barrel]) + square([barrel_d / 2, barrel]); + } + square([4, 2 * (barrel - 1)], center = true); + } + translate_z(-length + flange_t + spade) + vflip() + spade(spade4p8l, spade); + } + translate_z(-thickness) + explode(-length) + color("silver") + vflip() + tube(ir = thread_d / 2, or = nut_d / 2, h = nut_t, center = false); +} + +function jack_4mm_shielded_hole_radius() = 12/2; //! Panel hole radius for 4mm shielded jack + +module jack_4mm_shielded(colour, thickness, display_colour = false) { //! Draw a 4mm shielded jack + vitamin(str("jack_4mm_shielded(\"", colour, "\", 3", arg(display_colour, false), "): 4mm shielded jack socket ", colour)); + flange_d = 14.5; + flange_t = 2.5; + flange_id = 4.6; + sleaved = 21; + sleaved_d = 10.7; + thread = 14; + thread_d = 11.7; + nut_d = 14.4; + nut_t = 5; + length = 32; + spade = 8.5; + + explode(length, offset = -length + flange_t) { + color(display_colour ? display_colour : colour) { + rounded_cylinder(r = flange_d / 2, h = flange_t, r2 = 1, ir = 4.5); + + rotate_extrude() difference() { + union() { + translate([0, -sleaved]) + square([sleaved_d / 2, sleaved + flange_t]); + + translate([0, -thread]) + square([thread_d / 2, thread]); + } + square([flange_id, 100], center = true); + + difference() { + square([9, 2 * sleaved - 1], center = true); + square([6, 2 * sleaved], center = true); + } + } + } + color("silver") + translate_z(-length + flange_t + spade - 0.5) + cylinder(d = 4.8, h = 0.5); + + translate_z(-length + flange_t + spade) + vflip() + spade(spade4p8ll, spade); + } + + translate_z(-thickness) + explode(-length) + color("silver") + vflip() + tube(ir = thread_d / 2, or = nut_d / 2, h = nut_t, center = false); + +} + +function post_4mm_diameter() = 13; //! Outer diameter of 4mm binding post + +post_4mm_hole_radius = 7 / 2; +post_4mm_spigot_w = 1.1; +post_4mm_spigot_l = 1.2; +post_4mm_spigot_h = 2.5; + +module post_4mm_hole(h = 100, poly = false) { //! Drill hole for 4mm binding post + extrude_if(h) union() { + r = cnc_bit_r + eps; + if(poly) + poly_circle(post_4mm_hole_radius); + else + drill(post_4mm_hole_radius, 0); + + hull() { + translate([0, post_4mm_hole_radius + post_4mm_spigot_l - r]) + drill(r, 0); + + drill(r, 0); + } + } +} + +module post_4mm(colour, thickness, display_colour = false) { //! Draw a 4mm binding post + vitamin(str("post_4mm(\"", colour, "\", 3", arg(display_colour, false), "): 4mm jack binding post ", colour)); + + actual_colour = display_colour ? display_colour : colour; + d = post_4mm_diameter(); + base_h = 5; + collar_t = post_4mm_spigot_h; + + post_od = 9.5; + post_metal = 0.2; + thread_d = 3.5; + thread_l = 15; + post_d = 7; + post_h = 14; + + ringterm = ["", 6.3, 3.8, 16.7, 3, 1.6, 0.3, M4_dome_screw]; + + module washer() { + washer_t = 0.65; + + tube(or = 7.6 / 2, ir = thread_d / 2, h = washer_t, center = false); + + translate_z(washer_t) + children(); + } + + module nut() { + nut_t = 2.3; + + linear_extrude(height = nut_t) difference() { + circle(d = 6.3 / cos(30), $fn = 6); + + circle(d = thread_d); + } + + translate_z(nut_t) + children(); + } + + module spigot() + hull() + for(end = [-1, 1]) + translate([0, post_4mm_hole_radius + end * (post_4mm_spigot_l - post_4mm_spigot_w / 2)]) + circle(d = post_4mm_spigot_w); + + explode(20, offset = -thread_l) { + color(actual_colour) { + cylinder(d = d, h = base_h); + + translate_z(-collar_t) + linear_extrude(height = base_h) { + circle(post_4mm_hole_radius - 0.1); + + spigot(); + } + + translate_z(base_h + 1) + for(i = [0 : 7]) + rotate(i * 360 / 8) + render() difference() { + rotate_extrude(angle = 360 / 8) + polygon([ + [0, 0], + [0, 2], + [post_d / 2, 2], + [post_d / 2, 17.5], + [11 / 2, 17.5], + [12.5 / 2, 0] + ]); + + rotate(180 /8) + hull() { + translate([6.5, 0, 3]) + sphere(d = 2.5); + + translate([6, 0, 17.5]) + cylinder(d = 2.5, h = eps); + } + } + } + + color("silver") { + translate_z(post_metal) + cylinder(d = post_od, h = base_h); + + translate_z(base_h + post_metal + 0.1) + cylinder(d = post_od, h = 2); + + rotate_extrude() difference() { + square([post_d / 2, base_h + post_metal + post_h]); + + translate([0, base_h + post_metal + 1]) + square([2, post_h]); + + } + vflip() + cylinder(d = thread_d, h = thread_l); + } + } + explode(-15) + color(actual_colour) { + translate_z(-thickness - base_h) { + linear_extrude(height = base_h) + difference() { + circle(d = d); + + circle(post_4mm_hole_radius); + + offset(0.1) spigot(); + } + tube(or = d / 2, ir = thread_d / 2, h = collar_t, center = false); + } + } + translate_z(-thickness - base_h) + explode(-20, true) + color("silver") + vflip() + not_on_bom() + washer() + explode(5, true) nut() + explode(5, true) ring_terminal(ringterm) + explode(5, true) washer() + explode(5, true) nut(); +} diff --git a/vitamins/jhead.scad b/vitamins/jhead.scad new file mode 100644 index 0000000..a693419 --- /dev/null +++ b/vitamins/jhead.scad @@ -0,0 +1,202 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! J-Head hot ends from Brian Reifsnider, hotends.com. +// +include <../core.scad> +include +include +include +include + +use +use <../utils/tube.scad> + +MK4_heater = [ 12.76, 15.88, 8.22, (15.88 / 2 - 4.5), (12.76 / 2 - 0.5 - 2.5 / 2), (-15.88 / 2 + 5), 9.5, 3]; +MK5_heater = [ 12.76, 12.76, 8.22, (12.76 / 2 - 3.75), (12.76 / 2 - 0.5 - 2.5 / 2), (-12.76 / 2 + 4), 8, 2]; + +function heater_width(type) = type[0]; +function heater_length(type) = type[1]; +function heater_height(type) = type[2]; +function resistor_x(type) = type[3]; +function thermistor_y(type) = type[4]; +function nozzle_x(type) = type[5]; +function nozzle_cone(type) = type[6]; +function nozzle_cone_length(type) = type[7]; + +barrel_tap_dia = 5; + +barrel_dia = 6; +insulator_dia = 12; + +module heater_block(type, resistor, thermistor) { + h = heater_height(type); + + color("gold") { + render() difference() { + rotate([90, 0, 0]) + linear_extrude(height = heater_width(type), center = true) difference() { + square([heater_length(type), h], center = true); + + translate([resistor_x(type), 0]) + circle(resistor_hole(resistor) / 2); + } + + translate([-heater_length(type) / 2, thermistor_y(type), 0]) // hole for thermistor + rotate([0, 90, 0]) + cylinder(r = resistor_hole(thermistor) / 2, h = 2 * resistor_length(thermistor), center = true); + } + // + // nozzle + // + cone_length = nozzle_cone_length(type); + cone_end_r = 1 / 2; + cone_start_r = nozzle_cone(type) / 2; + straight = 1; + nozzle_r = 0.4 / 2; + translate_z(-h / 2) vflip() + rotate_extrude() + polygon([ + [nozzle_r, 0], + [cone_start_r, 0], + [cone_start_r, straight], + [cone_end_r, straight + cone_length], + [nozzle_r, straight + cone_length], + ]); + } +} + +module jhead_hot_end(type, filament) { + resistor = RIE1212UB5C5R6; + thermistor = Epcos; + heater = type == JHeadMk4 ? MK4_heater : MK5_heater; + + insulator_length = hot_end_insulator_length(type); + inset = hot_end_inset(type); + length = hot_end_total_length(type); + barrel_length = length - insulator_length; + vitamin(str("jhead_hot_end(", type[0], ", ", filament, "): Hot end ", hot_end_part(type), " ", filament, "mm")); + // + // insulator + // + translate_z(inset - insulator_length) + color(hot_end_insulator_colour(type)) rotate_extrude() + difference() { + chamfer = 2; + hull() { + translate([0, chamfer]) + square([hot_end_insulator_diameter(type) / 2, insulator_length - chamfer]); + + square([hot_end_insulator_diameter(type) / 2 - chamfer, insulator_length]); + } + square([3.2 / 2, insulator_length]); + + translate([hot_end_groove_dia(type) / 2, insulator_length - hot_end_inset(type) - hot_end_groove(type)]) + square([100, hot_end_groove(type)]); + } + // + // heater block + // + rotate(90) + translate([-nozzle_x(heater), 0, inset - insulator_length - heater_height(heater) / 2]) + heater_block(heater, resistor, thermistor); +} + +module jhead_hot_end_assembly(type, filament, naked = false) { //! Assembly with resistor, thermistor, tape, sleaving and ziptie + resistor = RIE1212UB5C5R6; + thermistor = Epcos; + heater = type == JHeadMk4 ? MK4_heater : MK5_heater; + + insulator_length = hot_end_insulator_length(type); + inset = hot_end_inset(type); + length = hot_end_total_length(type); + barrel_length = length - insulator_length; + bundle = 3.2; + tape_width = 25; + tape_overlap = 10; + tape_thickness = 0.8; + + jhead_hot_end(type, filament); + + vitamin(": Tape self amalgamating silicone 110mm x 25mm"); + // + // silcone tape + // + if(!naked) + color("red") + if(exploded()) + translate([0, max(hot_end_insulator_diameter(type) / 2, heater_length(heater) / 2 - nozzle_x(heater)), + -tape_width + tape_overlap + inset - insulator_length]) + cube([110, tape_thickness, tape_width]); + else + hull() { + translate_z(+ inset - insulator_length) + cylinder(r = hot_end_insulator_diameter(type) / 2 + 2 * tape_thickness, h = tape_overlap); + + translate([0, -nozzle_x(heater), inset - insulator_length - heater_height(heater) / 2 + eps]) + cube([heater_width(heater) + 4 * tape_thickness, + heater_length(heater) + 4 * tape_thickness, heater_height(heater)], center = true); + } + // + // Zip tie and heatshrink + // + if(!naked) + rotate(10) { + dia = hot_end_insulator_diameter(type); + scale([1, (bundle + dia) / dia]) + translate([0, -bundle / 2, -7]) + rotate(-110) + ziptie(small_ziptie, dia / 2); + + translate([0, -dia / 2 - bundle / 2, 20]) + scale([0.7, bundle / 6.4]) + difference() { + tubing(HSHRNK64, 60); + if(!exploded()) + translate_z(20) + cube([10, 10, 60], center = true); + } + + } + wire("Red PTFE", 16, 170); + wire("Red PTFE", 16, 170); + // + // heater block + // + rotate(90) + translate([-nozzle_x(heater), 0, inset - insulator_length - heater_height(heater) / 2]) { + intersection() { + group() { + translate([resistor_x(heater), -exploded() * 15, 0]) + rotate([90, 0, 0]) + sleeved_resistor(resistor, PTFE20, bare = -10); + + translate([-heater_length(heater) / 2 + resistor_length(thermistor) / 2 - exploded() * 10, thermistor_y(heater), 0]) + rotate([90, 0, -90]) + sleeved_resistor(thermistor, PTFE07, heatshrink = HSHRNK16); + } + if(!exploded()) + if(naked) + color("grey") cylinder(r = 12, h = 100, center = true); + else + cube(1, true); // hide the wires when not exploded + } + } +} diff --git a/vitamins/leadnut.scad b/vitamins/leadnut.scad new file mode 100644 index 0000000..56b570b --- /dev/null +++ b/vitamins/leadnut.scad @@ -0,0 +1,67 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Nuts for leadscrews. +// +include <../core.scad> +include <../utils/tube.scad> + +function leadnut_bore(type) = type[2]; //! Thread size +function leadnut_od(type) = type[3]; //! Outer diameter of the shank +function leadnut_height(type) = type[4]; //! Total height +function leadnut_flange_dia(type) = type[5]; //! Flange diameter +function leadnut_flange_t(type) = type[6]; //! Flange thickness +function leadnut_flange_offset(type) = type[7]; //! Offset of the flange from the top +function leadnut_holes(type) = type[8]; //! The number of screw holes +function leadnut_hole_dia(type) = type[9]; //! The diameter of the screw holes +function leadnut_hole_pitch(type) = type[10]; //! The radia pitch of the screw holes +function leadnut_screw(type) = type[11]; //! The type of the fixing screws + +function leadnut_shank(type) = leadnut_height(type) - leadnut_flange_t(type) - leadnut_flange_offset(type); //! The length of the shank below the flange + +module leadnut_screw_positions(type) { //! Position children at the screw holes + holes = leadnut_holes(type); + for(i = [0 : holes - 1], a = i * 360 / holes + 180) + rotate(a) + translate([leadnut_hole_pitch(type), 0, leadnut_flange_t(type)]) + rotate(45) + children(); +} + +module leadnut(type) { //! Draw specified leadnut + vitamin(str("leadnut(", type[0], "): ", type[1])); + bore_r = (leadnut_bore(type) + 0.5) / 2; + + color("dimgrey") vflip() + translate_z(-leadnut_flange_offset(type) - leadnut_flange_t(type)) { + tube(or = leadnut_od(type) / 2, ir = bore_r, h = leadnut_height(type), center = false); + + translate_z(leadnut_flange_offset(type)) + linear_extrude(height = leadnut_flange_t(type)) + difference() { + circle(d = leadnut_flange_dia(type)); + + circle(bore_r); + + leadnut_screw_positions(type) + circle(d = leadnut_hole_dia(type)); + } + } +} diff --git a/vitamins/leadnuts.scad b/vitamins/leadnuts.scad new file mode 100644 index 0000000..e119cc2 --- /dev/null +++ b/vitamins/leadnuts.scad @@ -0,0 +1,25 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +LSN8x2 = ["LSN8x2", "Leadscrew nut 8 x 2", 8, 10.2, 15, 22, 3.5, 1.5, 4, 3.5, 8, M3_cap_screw]; +LSN8x8 = ["LSN8x8", "Leadscrew nut 8 x 8 RobotDigg",8, 12.75,19, 25.4, 4.1, 0, 3, 3.5, 19.05/2, M3_cap_screw]; + +leadnuts = [LSN8x2, LSN8x8]; + +use diff --git a/vitamins/led.scad b/vitamins/led.scad new file mode 100644 index 0000000..59ed0c2 --- /dev/null +++ b/vitamins/led.scad @@ -0,0 +1,56 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Standard domed through hole LEDs. Can specify colour and lead length. +// +include <../core.scad> +use <../utils/rounded_cylinder.scad> + +function led_diameter(type) = type[1]; //! Body diameter +function led_rim_dia(type) = type[2]; //! Rim diameter +function led_rim_t(type) = type[3]; //! Rim height +function led_height(type) = type[4]; //! Body height +function led_pitch(type) = type[5]; //! Lead pitch +function led_lead_t(type) = type[6]; //! Lead thickness + +function led_hole_radius(type) = led_diameter(type) / 2; //! Radius of panel hole to accept LED + +module led(type, colour = "red", lead = 5) { //! Draw specified LED with desired colour and led length + d = led_diameter(type); + vitamin(str("led(", type[0], arg(colour, "red"), "): LED ", d, " mm ", colour)); + + color(colour) { + rotate_extrude() + rounded_corner(r = d / 2, h = led_height(type), r2 = d / 2); + + linear_extrude(height = led_rim_t(type)) + difference() { + circle(d = led_rim_dia(type)); + + translate([d / 2 + eps, -5]) + square(10); + } + } + color("silver") + for(side = [-1, 1], len = lead - side) + translate([side * led_pitch(type) / 2, 0, -len / 2]) + vflip() + cube([led_lead_t(type), led_lead_t(type), len], center = true); +} diff --git a/vitamins/leds.scad b/vitamins/leds.scad new file mode 100644 index 0000000..4da2f26 --- /dev/null +++ b/vitamins/leds.scad @@ -0,0 +1,34 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// d r r h p l +// i i i e i e +// a m m i t a +// g c d +// d t h h +// t t +// +LED3mm = ["LED3mm", 3, 3.1, 1.0, 4.5, 2.54, 0.4]; +LED5mm = ["LED5mm", 5, 5.6, 0.9, 8.5, 2.54, 0.4]; +LED10mm = ["LED10mm", 10, 11.0, 2.0, 13.5, 2.54, 0.4]; + +LEDs = [LED3mm, LED5mm, LED10mm]; + +use diff --git a/vitamins/light_strip.scad b/vitamins/light_strip.scad new file mode 100644 index 0000000..9770bac --- /dev/null +++ b/vitamins/light_strip.scad @@ -0,0 +1,166 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! LED strip lights that can be cut to size. +//! +//! The definitions are for the full length but they can be cut to size by specifying how many segments, +//! which can by calcuated using ```light_strip_segments(type, max_length)```. +//! +//! The `light_strip_clip()` module makes a clip to go around the light that can be incorporated into a printed bracket to hold it. +// +include <../core.scad> + +function light_strip_length(type) = type[2]; //! Un-cut length +function light_strip_leds(type) = type[3]; //! Total number of LEDs +function light_strip_grouped(type) = type[4]; //! Number of LEDs in each group +function light_strip_width(type) = type[5]; //! Outside width +function light_strip_depth(type) = type[6]; //! Outside depth +function light_strip_aperture(type) = type[7]; //! Inside width +function light_strip_thickness(type) = type[8]; //! Metal thickness +function light_strip_pcb_thickness(type) = type[9]; //! PCB thickness + +function light_strip_segments(type, max_length) = let( //! Calculate the maximum number of segments that fit in max_length + length = light_strip_length(type), + seg_length = length * light_strip_grouped(type) / light_strip_leds(type), + segs = floor(min(max_length, length) / seg_length) + ) + assert(segs, str("max_length must be at least ", ceil(seg_length), "mm")) + segs; + +function light_strip_cut_length(type, segs) = ceil(light_strip_length(type) * segs * light_strip_grouped(type) / light_strip_leds(type)); //! Calculate cut length given segments + +module light_strip(type, segs = undef) { //! Draw specified light strip, segs can be used to limit the length + segment_length = light_strip_length(type) / (light_strip_leds(type) / light_strip_grouped(type)); + segments = is_undef(segs) ? leds / light_strip_grouped(type) : segs; + l = light_strip_cut_length(type, segs); + vitamin(str("light_strip(", type[0], arg(segs, undef), "): Light strip ", type[1], " x ", l, "mm (", segments, " segments)")); + leds = light_strip_grouped(type) * segments; + w = light_strip_width(type); + d = light_strip_depth(type); + t = light_strip_thickness(type); + a = light_strip_aperture(type); + s = (w - a) / 2 - t; + p = light_strip_pcb_thickness(type); + x1 = w / 2; + x2 = x1 - t; + x3 = a / 2; + y1 = t; + y5 = d - s; + y4 = y5 - t; + y3 = y4 - p; + y2 = y3 - t; + + module led_positions() + for(i = [0 : leds - 1]) + translate([l * (i + 0.5) / leds - l / 2, 0]) + children(); + + module segment_positions(n = segments) + for(i = [0 : 1 : n - 1]) + translate([l * i / segments - l / 2, 0]) + children(); + + module resistor_positions() + segment_positions() + for(end = [-1, 1], side = end > 0 ? [-1, 1] : [0]) + translate([end * l / leds / 2 + segment_length / 2, side * 2]) + children(); + + color("silver") + rotate([90, 0, 90]) + linear_extrude(height = l, center = true) + polygon([ + [ x1, 0], [ x1, d], [ x2, d], [ x3, y5], [ x3, y4], [ x2, y4], + [ x2, y3], [ x3, y3], [ x3, y2], [ x2, y2], [ x2, y1], + [-x2, y1], [-x2, y2], [-x3, y2], [-x3, y3], [-x2, y3], [-x2, y4], + [-x3, y4], [-x3, y5], [-x2, d], [-x1, d], [-x1, 0], + ]); + + color("ghostwhite") + translate_z(y3 + p / 2) + cube([l, w - 2 * t, p], center = true); + + translate_z(y4) { + color("white") + linear_extrude(height = 1.6) + led_positions() + square([5, 5], center = true); + + color("yellow") + linear_extrude(height = 1.6 + eps) + led_positions() + circle(d = 3.5); + + color("silver") + linear_extrude(height = 0.8) + led_positions() + for(side = [-1,1], end = [-1:1]) + translate([side * 2.2, end * 1.6]) + square([1, 0.9], center = true); + + color("black") + linear_extrude(height = 0.1) + segment_positions(segments - 1) + translate([segment_length, 0]) + square([0.2, a], center = true); + + color("silver") + linear_extrude(height = 0.15) + segment_positions() + for(end = [-1, 1], side = [-1, 1]) + translate([end * (segment_length / 2 - 1.25) + segment_length / 2, side * 2.5]) + square(2.5, center = true); + + color("silver") + linear_extrude(height = 0.55) + resistor_positions() + square([3.2, 1.5], center = true); + + color("black") + linear_extrude(height = 0.55 + eps) + resistor_positions() + square([2.1, 1.5 + 2 * eps], center = true); + + } + + if(show_rays) + %cylinder(r = 1, h = 150); +} + +wall = 1.8; +clearance = 0.2; +function light_strip_clip_slot(light) = light_strip_width(light) + clearance; //! Clip slot size +function light_strip_clip_depth(light) = 10; //! Depth of the clip +function light_strip_clip_length(light) = light_strip_clip_slot(light) + 2 * wall; //! Outside length +function light_strip_clip_width(light) = light_strip_depth(light) + 2 * wall; //! Outside width + +module light_strip_clip(light) { //! Make a clip to go over the strip to be incorporated into a bracket + linear_extrude(height = light_strip_clip_depth(light), convexity = 2) + difference() { + translate([-light_strip_clip_length(light) / 2, -wall]) + square([light_strip_clip_length(light), light_strip_clip_width(light)]); + + translate([-light_strip_clip_slot(light) / 2, 0]) + square([light_strip_clip_slot(light), light_strip_clip_width(light) - 2 * wall]); + + translate([-light_strip_aperture(light) / 2, 0]) + square([light_strip_aperture(light), 100]); + } +} diff --git a/vitamins/light_strips.scad b/vitamins/light_strips.scad new file mode 100644 index 0000000..696349f --- /dev/null +++ b/vitamins/light_strips.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +// l l g w d a t p +// e e r i e p h c +// n d o d p e i b +// g s u t t r c +// t p h h t k t +// h u n h +// r e i +// e s c +// s k +Rigid5050 = ["Rigid5050", "rigid SMD5050 low profile", 500, 36, 3, 14.4, 7, 10.4, 0.9, 1.2]; +RIGID5050 = ["RIGID5050", "rigid SMD5050" , 500, 36, 3, 14.4, 8.6, 10.4, 0.9, 1.6]; + +light_strips = [Rigid5050, RIGID5050,]; + +use diff --git a/vitamins/linear_bearing.scad b/vitamins/linear_bearing.scad new file mode 100644 index 0000000..1cc699b --- /dev/null +++ b/vitamins/linear_bearing.scad @@ -0,0 +1,44 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! LMnUU linear bearings. +// +include <../core.scad> + +use <../utils/tube.scad> + +bearing_colour = grey70; +seal_colour = grey20; + +function bearing_length(type) = type[1]; //! Total length +function bearing_dia(type) = type[2]; //! Outside diameter +function bearing_rod_dia(type) = type[3]; //! Internal diameter + +function bearing_radius(type) = bearing_dia(type) / 2; //! Outside radius + +module linear_bearing(type) { //! Draw specified linear bearing + vitamin(str("linear_bearing(", type[0], "): Linear bearing LM", bearing_rod_dia(type),"UU")); + + casing_t = bearing_radius(type) / 10; + casing_ir = bearing_radius(type) - casing_t; + + color(bearing_colour) tube(or = bearing_radius(type), ir = casing_ir, h = bearing_length(type)); + color(seal_colour) tube(or = casing_ir, ir = bearing_rod_dia(type) / 2, h = bearing_length(type) - 0.5); +} diff --git a/vitamins/linear_bearings.scad b/vitamins/linear_bearings.scad new file mode 100644 index 0000000..0600601 --- /dev/null +++ b/vitamins/linear_bearings.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Linear bearings +// +LM12UU = ["LM12UU", 30, 21, 12]; +LM10UU = ["LM10UU", 29, 19, 10]; +LM8UU = ["LM8UU", 24, 15, 8]; +LM6UU = ["LM6UU", 19, 12, 6]; +LM5UU = ["LM5UU", 15, 10, 5]; +LM4UU = ["LM4UU", 12, 8, 4]; +LM3UU = ["LM3UU", 10, 7, 3]; + +linear_bearings = [LM3UU, LM4UU, LM5UU, LM6UU, LM8UU, LM10UU, LM12UU]; + +use diff --git a/vitamins/mains_socket.scad b/vitamins/mains_socket.scad new file mode 100644 index 0000000..e7dc2ac --- /dev/null +++ b/vitamins/mains_socket.scad @@ -0,0 +1,123 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! UK 13A sockets at the moment. +// +include <../core.scad> +include +include +include + +function mains_socket_width(type) = type[1]; //! Width at the base +function mains_socket_depth(type) = type[2]; //! Depth at the base +function mains_socket_top_w(type) = type[3]; //! Width at the top, might be tapered +function mains_socket_top_d(type) = type[4]; //! Depth at the top, might be tapered +function mains_socket_corner(type) = type[5]; //! Corner radius +function mains_socket_height(type) = type[6]; //! Height +function mains_socket_t(type) = type[7]; //! Plastic thickness +function mains_socket_offset(type) = type[8]; //! Offset of the socket from the centre +function mains_socket_pitch(type) = type[9]; //! Screw hole pitch +function mains_socket_screw(type) = M3_cs_cap_screw; //! Screw type + +earth = M3_ringterm; +earth_screw = ringterm_screw(earth); +insert_wall = 3; + +function mains_socket_insert_boss(type) = 2 * (insert_hole_radius(screw_insert(mains_socket_screw(type))) + insert_wall); + +module face_plate(type) + rounded_square([mains_socket_width(type), mains_socket_depth(type)], mains_socket_corner(type)); + +module mains_socket_hole_positions(type) //! Position children at the screw holes + for(side = [-1, 1]) + translate([side * mains_socket_pitch(type) / 2, 0]) + children(); + +module mains_socket_earth_position(type) { //! Position of earth terminal for DiBond panel + inset = mains_socket_t(type) + washer_diameter(screw_washer(earth_screw)) / 2 + 1; + + translate([-mains_socket_width(type) / 2 + inset, -mains_socket_depth(type) / 2 + inset]) + children(); +} + +module mains_socket_holes(type, h = 0) { //! Panel cutout + mains_socket_hole_positions(type) + drill(screw_clearance_radius(mains_socket_screw(type)), h); + + extrude_if(h) + offset(cnc_bit_r) offset(-cnc_bit_r) difference() { + offset(-7) face_plate(type); + + for(side = [-1, 1]) + hull() + for(x = [1, 2]) + translate([side * mains_socket_pitch(type) / x, 0]) + circle(4.5); + + mains_socket_earth_position(type) + circle(d = washer_diameter(screw_washer(earth_screw)) + 2); + } + + mains_socket_earth_position(type) + drill(screw_clearance_radius(earth_screw), h); +} + +module mains_socket(type) { //! Draw specified 13A socket + offset = mains_socket_offset(type); + screw = mains_socket_screw(type); + height = mains_socket_height(type); + + vitamin(str("mains_socket(", type[0], "): Mains socket 13A", offset.x || offset.y ? ", switched" : "")); + + color("white") render() difference() { + hull() { + linear_extrude(height = eps) + face_plate(type); + + linear_extrude(height = height) + offset(-(mains_socket_width(type) - mains_socket_top_w(type)) / 2) + face_plate(type); + } + // Holes for pins + translate([offset.x, offset.y, mains_socket_height(type)]) { + for(side = [-1, 1]) + translate([side * 11.1, -11.1]) + cube([7, 4.5, 8], center = true); + + translate([0, 11.1]) + cube([4.5, 8.5, 8], center = true); + } + // Hollow out the back + difference() { + linear_extrude(height = height - mains_socket_t(type)) + offset(-mains_socket_t(type)) + face_plate(type); + + cube(50, center = true); + } + // Screw holes + mains_socket_hole_positions(type) { + cylinder(r = screw_clearance_radius(screw), h = 100, center = true); + + translate_z(height) + screw_countersink(screw); + } + } +} diff --git a/vitamins/mains_sockets.scad b/vitamins/mains_sockets.scad new file mode 100644 index 0000000..07250a5 --- /dev/null +++ b/vitamins/mains_sockets.scad @@ -0,0 +1,29 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! UK 13A sockets at the moment. +// + +MKLOGIC = ["MKLOGIC", 86, 86, 86, 86, 3.6, 9, 3.6, [-9, -9], 60.3]; // Screwfix, switched +Contactum = ["Contactum", 84, 84, 80, 80, 4.0, 10.5, 3.6, [ 0, 0], 60.3]; // Old and unswitched + +mains_sockets = [MKLOGIC, Contactum]; + +use diff --git a/vitamins/meter.scad b/vitamins/meter.scad new file mode 100644 index 0000000..ba6f4fe --- /dev/null +++ b/vitamins/meter.scad @@ -0,0 +1,130 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! LED volt meter modules available from China and a printed bezel that allows them to be mounted into a +//! CNC cut panel. The meter is held in the bezel by melting the stakes with a soldering iron set to 200°C. The +//! bezel is fixed in the panel with hot glue. +//! +//! Needs 7 segment font from to look realistic. +// +include <../core.scad> + +led_meter = ["led_meter", 22.72, 10.14, 6.3, 22.72, 11.04, 0.96, 30, 4.2, 26, 2.2 / 2]; + +function meter() = led_meter; //! Default meter type + +function meter_length(type = led_meter) = type[1]; //! Length of body +function meter_width(type = led_meter) = type[2]; //! Width of body +function meter_height(type = led_meter) = type[3]; //! Height of body excluding PCB +function meter_pcb_length(type = led_meter) = type[4]; //! PCB length excluding lugs +function meter_pcb_width(type = led_meter) = type[5]; //! PCB width +function meter_pcb_thickness(type = led_meter) = type[6]; //! PCB thickness +function meter_lug_length(type = led_meter) = type[7]; //! PCB length including lugs +function meter_lug_width(type = led_meter) = type[8]; //! Lug width +function meter_hole_pitch(type = led_meter) = type[9]; //! Lug hole pitch +function meter_hole_radius(type = led_meter) = type[10]; //! Lug hole radius + +module meter_hole_positions(type = led_meter) //! Position children over the holes + for(side = [-1, 1]) + translate([side * meter_hole_pitch(type) / 2, 0]) + children(); + +module meter(type = led_meter, colour = "red", value = "888", display_colour = false) //! Draw a meter with optional colour and display value +{ + vitamin(str("meter(", type[0], arg(colour, "red", "colour"), "): LED meter ", colour)); + + color("grey") + translate_z(meter_height(type) / 2) + cube([meter_length(type), meter_width(type), meter_height(type)], center = true); + + color("green") + translate_z(meter_height(type)) + linear_extrude(height = meter_pcb_thickness(type)) + difference() { + union() { + square([meter_pcb_length(type), meter_pcb_width(type)], center = true); + + square([meter_lug_length(type), meter_lug_width(type)], center = true); + } + meter_hole_positions(type) + circle(meter_hole_radius(type)); + } + + color(display_colour ? display_colour : colour) + linear_extrude(height = 0.2, center = true) + mirror([1,0,0]) + text(value, font = "7 segment", valign = "center", halign = "center", size = meter_width(type) - 2, spacing = 1.2); +} + +clearance = 0.1; +overlap = 1; +flange_t = 1; + +function meter_bezel_wall(type = led_meter) = (meter_lug_length(type) - meter_length(type)) / 2; //! Printed bezel wall thickness +function meter_bezel_rad(type = led_meter) = meter_bezel_wall(type); //! Printed bezel corner radius +function meter_bezel_length(type = led_meter) = meter_length(type) + 2 * (meter_bezel_wall(type) + overlap); //! Printed bezel length +function meter_bezel_width(type = led_meter) = meter_width(type) + 2 * (meter_bezel_wall(type) + overlap); //! Printed bezel width + +module meter_bezel_hole(type = led_meter, h = 100) { //! Make a hole to fit the meter Bezel + wall = meter_bezel_wall(type) + clearance; + rad = meter_bezel_rad(type) + clearance; + l = meter_length(type); + w = meter_width(type); + + extrude_if(h) + rounded_square([l + 2 * wall, w + 2 * wall], rad); +} + +module meter_bezel(type = led_meter) { //! Generate the STL for the meter bezel + stl("meter_bezel"); + + wall = meter_bezel_wall(type); + rad = meter_bezel_rad(type); + l = meter_length(type); + w = meter_width(type); + h = meter_height(type); + + union() { + linear_extrude(height = h) + difference() { + rounded_square([l + 2 * wall, w + 2 * wall], rad); + + square([l + 2 * clearance, w + 2 * clearance], center = true); + } + + linear_extrude(height = flange_t) + difference() { + rounded_square([l + 2 * wall + 2 * overlap, w + 2 * wall + 2 * overlap], rad + overlap); + + square([l + 2 * clearance, w + 2 * clearance], center = true); + } + meter_hole_positions(type) + cylinder(r = meter_hole_radius(type), h = h + meter_pcb_thickness(type) * 2); + } +} + +module meter_assembly(type = led_meter, colour = "red", value = "888", display_colour = false) { //! Meter assembled into the bezel + vflip() + translate_z(-flange_t) { + color("dimgrey") meter_bezel(type); + + meter(type, colour, value, display_colour); + } +} diff --git a/vitamins/microswitch.scad b/vitamins/microswitch.scad new file mode 100644 index 0000000..53bbb6e --- /dev/null +++ b/vitamins/microswitch.scad @@ -0,0 +1,117 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Used for limit switches. +// +include <../core.scad> + +microswitch_contact_color = "gold"; + +function microswitch_thickness(type) = type[2]; //! Body thickness +function microswitch_width(type) = type[3]; //! Body width +function microswitch_length(type) = type[4]; //! Body length +function microswitch_radius(type) = type[5]; //! Body corner radius +function microswitch_hole_d(type) = type[6]; //! Screw hole diameter +function microswitch_holes(type) = type[7]; //! Hole positions +function microswitch_button_w(type) = type[8]; //! Button width +function microswitch_button_t(type) = type[9]; //! Button thickness +function microswitch_button_pos(type)= type[10]; //! Button position +function microswitch_legs(type) = type[11]; //! Leg positions +function microswitch_leg(type) = type[12]; //! Leg types +function microswitch_body_clr(type) = type[13]; //! Body colour +function microswitch_button_clr(type)= type[14]; //! Button colour + +function microswitch_lower_extent(type) = let(leg = microswitch_leg(type)) min([for(pos = microswitch_legs(type)) pos.y - leg.y / 2]); //! How far legs extend downwards +function microswitch_right_extent(type) = let(leg = microswitch_leg(type)) max([microswitch_length(type) / 2, for(pos = microswitch_legs(type)) pos.x + leg.x / 2]); //! How far legs extend right + +module microswitch_hole_positions(type) //! Place children at the hole positions +{ + for(hole = microswitch_holes(type)) + translate(hole) + children(); +} + +module microswitch_wire_positions(type, skip = undef) { //! Place children at the leg hole positions + leg = microswitch_leg(type); + legs = microswitch_legs(type); + for(i = [0 : len(legs) - 1]) + if(i != skip) + let(pos = legs[i]) + translate(leg[3] ? pos + leg[4] : pos - [leg.x, leg.y] / 6) + children(); +} + +module microswitch(type) { //! Draw specified microswitch + vitamin(str("microswitch(", type[0], "): Microswitch ", type[1])); + d = microswitch_button_t(type); + + color(microswitch_body_clr(type)) + linear_extrude(height = microswitch_thickness(type), center = true) + difference() { // main body + rounded_square([microswitch_length(type), microswitch_width(type)], microswitch_radius(type)); + + microswitch_hole_positions(type) + circle(d = microswitch_hole_d(type)); + } + + color(microswitch_button_clr(type)) // orange button + translate(microswitch_button_pos(type) - [0, d / 2]) + linear_extrude(height = microswitch_button_w(type), center = true) + hull() { + circle(d = d); + + translate([0, -3]) + circle(d = d); + } + + color(microswitch_contact_color) // yellow contacts + for(pos = microswitch_legs(type)) + translate(pos) { + leg = microswitch_leg(type); + vertical = leg.y > leg.x; + + if(vertical) + rotate([0, 90, 0]) + linear_extrude(height = leg.x, center = true) + difference() { + square([leg.z, leg.y], center = true); + + if(leg[3]) + translate(leg[4]) + circle(d = leg[3]); + } + else + rotate([90, 0, 0]) + linear_extrude(height = leg.y, center = true) + difference() { + square([leg.x, leg.z], center = true); + + if(leg[3]) + translate(leg[4]) + circle(d = leg[3]); + } + + if(!vertical && pos.y < -microswitch_width(type) / 2) { + gap = -microswitch_width(type) / 2 - pos.y; + translate([-leg.x / 2 + leg.y / 2, gap / 2]) + cube([leg.y, gap, leg.z], center = true); + } + } +} diff --git a/vitamins/microswitches.scad b/vitamins/microswitches.scad new file mode 100644 index 0000000..58f2351 --- /dev/null +++ b/vitamins/microswitches.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Microswitches +// +small_leg = [0.9, 3.3, 0.4, 0]; +medium_leg = [0.5, 3.9, 3.2, 1.6, [0, -0.5]]; +large_leg = [11.4, 0.8, 6.3, 1.8, [1.7, 0]]; + +small_microswitch = ["small_microswitch", "DM1-00P-110-3", 5.8, 6.5, 12.8, 0, 2, [[-3.25, -1.65], [3.25, -1.65]], 2.9, 1.2, [-1.95, 3.75], [[-5.08, -4.95], [0, -4.9], [5.08, -4.9] ], small_leg, grey20, "white" ]; +medium_microswitch = ["medium_microswitch","SS-01 or SS-5GL", 6.4, 10.2, 19.8, 1, 2.35, [[-4.8, -2.6 ], [4.7, -2.6 ]], 3.2, 2, [-2.8, 5.8 ], [[-8.05, -7.05], [0.75, -7.05], [8.05, -7.05] ], medium_leg, grey20, "burlywood" ]; +large_microswitch = ["large_microswitch", "Saia G3 low force", 10.4, 15.9, 28.0, 2, 3.1, [[-11.1, -5.15], [11.2, 5.15]], 4, 2.75,[-9.1, 9.55], [[19.7, 2.19], [19.7, -3.45], [8.3, -10.45] ], large_leg, "ivory", "white" ]; + +microswitches = [small_microswitch, medium_microswitch, large_microswitch]; + +use diff --git a/vitamins/microview.scad b/vitamins/microview.scad new file mode 100644 index 0000000..f33c736 --- /dev/null +++ b/vitamins/microview.scad @@ -0,0 +1,59 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Microview OLED display with on board AVR by geekammo / Sparkfun. +//! +//! ```microview()``` generates the model. ```microview(true)``` makes an object to cut out a panel aperture for it. +//! +//! Uses STL files copyright geekammo and licenced with MIT license, see [microview/LICENSE.txt](vitamins/microview/LICENSE.txt). +// +include <../core.scad> +use // for pin + +panel_clearance = 0.2; + +module microview(cutout = false) { //! Draw microview or generate a panel cutout for it + rotate([0, 0, -90]) + if(cutout) + linear_extrude(height = 100) + offset(panel_clearance) + projection() + hull() + import("microview/GKM-003_R05_CHIP_LOWER_HOUSING.stl"); + else { + vitamin("microview(): Microview OLED display"); + + translate_z(8.35) { + + color("black") + import("microview/GKM-002_R05_CHIP_UPPER_HOUSING-1.stl", convexity = 2); + + translate([-2, 0, 0]) + color("dimgray") + cube([12.5, 15.5, 4.41], center = true); + } + color("dimgray") + import("microview/GKM-003_R05_CHIP_LOWER_HOUSING.stl", convexity = 2); + + for(side = [-1, 1], i = [0 : 7]) + translate([side * inch(0.35), (i - 3.5) * inch(0.1)]) + pin(); + } +} diff --git a/vitamins/microview/GKM-002_R05_CHIP_UPPER_HOUSING-1.STL b/vitamins/microview/GKM-002_R05_CHIP_UPPER_HOUSING-1.STL new file mode 100644 index 0000000000000000000000000000000000000000..ed64b93f7bd73a06cb57ffc57fcbf7f64f4cfaf8 GIT binary patch literal 63084 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%0DSOJVTWA5msH}^{= ziJ(&W&7&>DyIjxKy*Z$l$SFtnN!+C}IHN^A0{K&stop0<)g`N(7anRw~@%%+ALim5zXMef%Z+6(y*CjauKcEZwN9K``%W zm_$%1YW-4_snwEn`z{B;;4R_8pE`H;p#LHE&E5&EEuVe30TnA=q+`JPiLeClZ9FnPoqsvb40 zaqx!&Wuqkjc1qy;Cw2z4%O->E*&xXGD_kO|6pkL^+23r2H9waY1U;R?BtLyh;5a7s zk}qAJ8%l*lz}6Z85<#VCQ~_qpdSlO>>>?n)qxOB|#v4$A+Gj8m^tdA2I=vQLNhvKw zC{QUn2cu`d_g6#pfcntkLos1J&z-|5LG4T!F;e2Y;alUT5Lu{zL{KStZr>k!-thfN zJe-XBA$gur0`2>`K81>i=QNn&Iq7dvDYQUqUN1TEoTp!`HvGDk2#zoBi8Gem7D`a> zB=mG@Rnu^}P-i$D`amM66!k(=l$vF08Y=JU3fww_(-ull?=94?&cvmRNb3sTj@KoE zO37YmQ?A9Pyy(#v+>h!V?m8b{+e} z3${+G=S3~_w3ZlZuQBCxXY2Kb6s$l@TPT4ZTewP?{NZ-IaMl%T)enBnk?UYlDO%wQ zb3izZj~a$Pckg;@K6j}mpF3J{iqeYGHN1c>fBT`Zef=Qe=ekkNSG*Wnl z<+e~l`d~#u3RMfGq@cyMX5_2^cs>#%T6oY3I zInltF`KwWrsI2tP@VCP~%u{4;p#+XMF#B1~pB5X{?QB0dQfiAtP$~3o#C%IRQO20# zdSGT`@1A|(!}Z;qwon2`C75H8%dNf}Ik5L2*gb2WM95OqZz-l_o}}>yM?lQ}2=t)4 zjHfd7(x&S#3va4a;SVM9QOVJn-FxOuy76rU)OlA4vv`=}HYp);Nd3Dx2BViD8lwmH zjfrragn0)HqUMX4pi*=!;9jl4@<@I52&j}8CXEtG$kijJ+b)l^cNqz5mjz1%m7*gC z^C3S;NMW6aK-Vr4ByU6NQ%1c_F~;QcsiZcYhCrQN6U5WajR&U$^)JI%(7Jb%w+P zlzApI6Y_{zUTAs5OZ501XY}|r5waBZ5w@t75#faMZiA=0B_Cl*&~c1jldbPXY``ef z<^PE?m8Hz_qZr2$z9{ndh0YK@_MX;>wY#OOIkE$FvHVH9*tiV)`5({2w;frmAwKjMLFNR z7#EoDzT_y#ccv7lEtJ4-{y)Ucm{IUyT2YChQke4&^Z3`tD7wedn9-p^{-kmd!@)hO zg~%v}+ER`WHN8UFpL${DuH<7;BjDxa>X^&I>}gF2dbJn@(an&Q9x(#GrOE`A!dWTW z&TPJ07=i%_G3d=SR$L+~H>O$;rcE z_xQ$Qrq1=>r38KIVjSX`?Zn~Wc19+s6ppc6@829Equ?kgm8Uem4|5A8gp}#2qleJK zHA!cGjD(PkG7=$6Q6J&Jg{~19NK{5bNSVEd&A*HNk-P?>SN=xTL+^|>mY5R1l7CH zza*$z)N0gZlI^!hS_YM(IuUw6Y`YaTq2)k$SaPYP`%r>voLG11_uZ(fsOg_i&X5Qy zCEJ!Fd}`=;wchYz%~7#8$kpK}LG^1z`PQqZZg`t^@b2w5iJ(%{5{CI1=JwZJP&&hf z)cYbHC)eUd3F^s%Uf!+y>tft6=Rh7UOF=F{rRc1UQ9>&g>mt6kg-(WVm=)M;xuXR2 z{lf43$wFPqju?opU0PTNb8UT8N?r~6_t>vnf1oKW^tP4kyOhA)YpU4-d85N#-H6(? zq4rZ}iJ(%lH4696jO?9ry{RaHyJF&>_^qTPb4*uZ@H{Wc8bzh3?MzW-9Eo5tHC2dP z?IGF7C_()pwQ~bT&JA+ya8!y~^suUxWo`EDP!(wN$p>f)C1hW<+PB-YQJt$n;hCi+ zf=W?arlPoIbYWX7R)Y)WN=r6NO32>0d1DgUuCLs1(eOtc&)NVi?63lIAz1xcWM_B<6ew!TNW_ z)z$AxV1#K9Myy-lRap=1!PCmv!RN;Hp?n+BdocbhruNgh;9e(4vTier~~uaA24^7c*}zsL`tQ8{(~8z0;e`DlNyepe1NEtJD73`MbznU$BZ>|I~E z!_3BTeW(@s!UW5jf}qyN;2|V@8XUICBdnXe0^dfVi`YP07;^zU@EGV+y`ZjSdY<%i4HX zji2)&b^ogHG>^Nt)wNFVQVR!!iTe|MWN+79*)R;=+=}Ixc_gh}QWeYr z5qQVf)WaX`&_gc*^IGgfV$(cJUE{eJk5jWDTD;6HlnBXZk2Tx`aTSTV=jTP8`#Kl` zMo;ED$~fYCjl`#TRqfK*5yw$w>~Dd3t$rc&a;ls*N?nA+*3nMv;oW}l_xnQrC&LoQ zXBEseb5I?($OeBi0^^-CRC=}OH5pkcKa6TWkJA=P$nSj7uco@h)FIG0 za->91DLNLgGSHB1EcH$-3|#h>|K9o@V;Vwn7D$rD>+B_8FhgP(j^m?v=Me|jvu5|3 z!H80iur|55g^uvC-9Mr2BpmBbo<(A}A(5@G-wE=py(ke>>S4p*s2Nnj>`6C~2oBoJ zYL;#XR*jN5{ayM+Q5tu`=U1@LZ#uxkIfprIp~UN0+yUX}8MNRQ_L1*IRTecT9xP(s zNCcIVuPW?W8XNbqDlFJ!4GT+VrQS)en0E2P1$Auqtkm7LLh(z=RNE!JK;QaM9F6C( zkGT%3S?hr2aOdq4PFpC!3S_6YOA1XJ+dUJBfa6P9_L&xt)bEi*P$@dcU@f}aY3$GA zs!-&(HPAlNFTBQCtrE`A$tt~CMY*)*4C{Uh&(%)bOZP#EJL%bIPppXdl!8|k?|F$G zYY+x2A390|mAdbFKXqM=5S;VVkvKj$gW1ISL8BVRmQp-|$AC$oAc&QCKR>GWT_puNCwaaY% zhG>}3P9~@n9hHi*=4v1IsYWN5lzC2?cZ2RJxYmYaozyKjt`uBH!qI2@F!r(T!WuSu zc`H~NdL6S(n_DPxYyCI$-V4R7mD`Z;nv=|I&g$XSqyi8->s{)WQx$QJ-J#N%NG6UR z-^!8}HG&mmKVn@n^KVgt&Puo%xXx!=0-Hnilvff#rRde-PI%*DR(pH`%y_&@x(`ao zGegAQ{;YXucSvfnSt6(uUAs^Ncv_k5n%NJY`^}b~E+rl|%!8U!70irx1D{p;ta!Eo zD?%kSo&O)gEJas1{LbyFu`xgU!GPUUr1hNsZqx${k&z46`Rmxn3JX1JSf)Q%jFky0 zMb~zmMYpF%J=~B8_n$pN`&A*-CM%fPQdWne$YwL)SDWgwOA?`O@A6Fq39DYJF2xdWDUT6u|6$;g~UN2omRuzS4PZ?F5@QKgLKTa|eN<=X=KH?I{ZzxH}Bx?@}|r3yppP#xT6l=ES!n z*QlFUe8M>B5ZqDBL89r3EOz~*A8eXl0%!{*3I}{hWd$nYo@W&j-VVpuq^c2czk{Ph zP$~C4KQzyXtc@#>2&})KU49V^%89Z#Us>XFuZSyTUG?UA1$BgAk*!5-G|K|Bs#Z$- z9@hzpJM*`*%#(p&=jR2qg%Wqt|Ds+Jf_t9MNSqHo#H!b4kpA0VBB<21_jz$|AXe&; zNMsJ%&X&)QgoRs60BxZ}bm$)~4jR3dk=Wq3pVfO;6WYDDk_akwt$tqAltM+;2qd;Q z+`_D?R0k{74s&doTPRU|#;?@mI+ZXJ+bkp&bl=4sS2chKFTS7-XC|oB`G4|Y4lkjR zEknXk{3>glQ%^AeascjN#n?@L_>NV{QwWY*2T25#>Zg00iZK&d31cl1?cMgVHHl5( z;;y^WevoRiR8zxfj*jwYt&b9Pg+tGQf=O(fS4}u=u#~huDn<1_&&~V z&19$QmjSnuz7j#DWXK1}3!eICntg_$Y;eB`u+63;GMW8SSW zVWq-8j+B|n@{F$ybzc^e=0qw*XK?hB**=S9{jLqRx9y~vni7eTHW=|8ieFTF?Bo8= z$?T0!6tsThDiKtQt}B>*Z}NQB<+lolx0jSw97^n8WrO-kCA81g!9D^nPiLLyhrqq_ zsp3%1K90Jfg3g8riB7D`M^Eri*CDx&UF3JGWH7rKHC zY#;_Be|%BUUg`cMbV1{Hv{ z!%w3f?o;RLy1#`^^SJ3f(AH;ep#-()VQj{cy1LI>p79642@*l2erb3t zt6Edm+0F`zmT3airblgk)Xs>RbXvu*glA50wr>r|0!az93gXkPfcN+j`^c;}h;3h> zK)VA?B!Ws&`z3l{cRt6w?d_nHM-+@K`bS$QhQ07s9a`i`dmS5u|0?5(lLtphAZnc+ z_;jak-pG#jE&;;|>u~)rw@_lx0?V|fPl8bk4@JUi>lQYmR!JyyC{iM*RI@@>XvYW= zSrL%Psx_VM-0uQ@%c6j`P@+YZd}wD3#{HlR603clv88i=^J0w{3|{aJ^+aFP#LBB! z4*;`91fWf~3EDM&X)|2wgh(XV>7%Ty-5cK8x*>WrDwtWw7w^(SrB6Y&?|xZ+kj=6y%sg3BdV`dpPef5FO-N+< zTNQyKXL^WSzxdx_a% zP6^rG?waAudS_qZM?GYMO3`lx?IoGc%s1-_FXP!6^PZc(HA;N?oewRzzPR%`jNhQM zLlbtZLONfqCP)O8lE1;24$ax}br*P}HRfj&`IkBNQBXI5m8gE2ZyeiE zBB&G{p^EZr;RH74-YMRFdPiv_QeyQx3$#}F;7ocF`#8396;RTK~kouqmonz2LyiYpIjC{!7@2f8nREo|LSYzEeiD~&n%+k$VZOi(F02cvg%)=`7Qq>^yEPCa}d=JT`pZ;E0>bTIBt zEmeBnhJFfL>Zj-%mWGfK@<|*ep8s^fokT@En<$A-x0&PGls=V$pvHzO(%BuAy6@?T z^KT^_TmDF#9>R2moq{2LXl1kxnp-F#)^_JsB2R7wBphZ(>n88hL(7le5<#Ufp8@W= zLh+ueBQayI74ygw4Of4AVFojE3nlXU+2V;%C|X%+A~C2*eztsVU9fLZLL#Wtw)X|l z%P9<3oVG}m|M}RkZa@VH3#kJ1q>?_->qCoa8kt>QKO|;Oy`IwUb1+PJ9*#4$xrGw+ ziQs5|Vqxg@BT3{{e*=&3X(_m5O@cfp%&S%=L1V$wcuwOZkJS;rw8Yo>&XQ{9BX|nNJ3EbweF%DfW?^Zz#W$ zw>$6Ksh>npsc&(vY1`hrqYZN=5>B0hxGrD_4;VQR=-EF#;g|gYx^;hKsOm72KUy^q zXbUA|KY#{x^6{bj_wmw0dr1V98q>WDT2lRtdi(N!ta(D(5pL7AC(ss3TyZafT4jK7 zRm<1aiNAk;gO71Zln5&2W$mmHRog$sJGYwW#QQ(H&SP6A0&Ss$>!cF6hYG|wD+`IT znO=Nv)H6QrUVDk4QYCIX;S6f>XSgwA2(MSZJ9p{U59o8J9vAe@;{JR^HQvH7o_Ai? zUwVU-pidn=whkolXPy7yGhO9TLZ#>kMLR?PM4nb*Bk$X@hctF6L7%#?;!Nb%f2Hx@ zDV-#ON_pIM!?d9eAEcfA|Z_*3vpi3Hol(YEXEY;oMXUP|mfI2r5O_MBGO`bW5qf?gbxxx)abA zO31s^^tAmcyWcy*mrivgf=baf5l^sQRMWY9cY&?wwMAPfL3hPiWh|+dZb9=PnB!AL zBB&Hy6S2~U?=M}C&_Hz{jCCp1As1#jA(P}WTHaoRM z2m5Aw0&SrLwK?F**|H(adRhxg)GZ+qREn-8in7qUK8xyI3tUTM-WsxQQ-az~@H;Qj zlqD2t0^41ME!@1)Qz?0kipbKl3IiHJ#E62ZGnmg5PR+ z(eoX&Wh|@Cy5xz6gWujtCqI;+wk-6AyI&T*yz+%=9WS5{WI)=~;=(qWu8O!vRzUa^ zrQ^9#1zbl0P$S=odPy+z2J!FuigS48Wp1Iw%$|XoHiOxEFH;?`d1m z_uSkA;*~?RJ{bg4eM`eXPV)OW>Jorn75>__%lA>}eF!vibcXN6Q_v&D ze20|i@YEmIa(_Ij7M9f1rsbePn^I8o)N6^LQler{<0BRDByKzsCBi-7mSZ8fx7Y@A zK$u%7(V(M0YG!^$VtE5E*k)N6uBlcML8U}ZA=L8G>#aNX!I&2WwJrpI@>%05!RTu#h_z=G zu*+8AL1?B#P^m9Ip15DAh_y_`*u5W=2lB@-XmI5rW_&ldP{P8m9QvvT{5MfZj7fXW54pQTgSfITgeY(d{32`QcS1f=ba{ zA8HvH7I6RXWxo4sw6q_jgzWLVW3e@Ow7bri-PB10m7;q?JYzviZ6MTw0jx$1YKM70=9PpMA)!z{;Fv^EDLRhP*7vm(l$;#`cEe9| z+CmAsN?^6L>BV6A-2k{Z|A|CUDLRf(Te@8gT+8@D(eE!gZJ`8RB@|^`x+BD#E(d?N z{genQMaMB#iLogHD;l{&_c@jr5o`W(C_z^V^o^=q2xf;m!{Tv;C4x%Pag23F0}FxY z*wUaTIG`of{N+%B&iRUxznwL7t5yWAEH5h&REmyc^hvF14bGDtU{p97@nR zA1y3zKXTt~mQem$s6=2q@~;FMD$zCl+!%dCtu@@4dga?!lzeB12C8V%04)>gr}UHG2Q{P4?Q zKYaicpEsA&7D`|g9L`l?sC&&r0^Fwagc`lz&z?yVL8WH)ERCm2RnYb$`WO~AlQ$mG z2jED8bX7DWg7$~LoRzomEl0Y*v7*z_|H^#rln|B>bKJz;0-N~G4vEmU<_d|RQuGQj z&)v11yhK1puvIsr2e$bRDIx4J!aupgQ?aV89JZCesu>Sq*U}_{N>!fbjCQOrTsDyFxPF*hC^56AtG1qR=&M7bZoTRJP`w~1Qq^A~s8osDE?Vpfo+BV} zux=9n5W9?(FJ&sjcs6jl^>k8P+~;U;;6lb;|$jq zi3L*~dC}tuP~qBbiJ(%4KbF$Gn1zQJMhSJY=6|<$h4OpPVRVhTg%Vhw9)G_w+IZ(9 zk$+HW?$oLWOuKzlBB)e!sFOC&7Y-0a->6l5|H+rU$jGMXg=8K-H13LyP_&U%TgJb? zwS-}>>PaJ!5_G&_J(j~;xDUoIDLP}|S6iSVXXC2C%gsL0%s>e`LQ#kN)Rr&Ir~&%b^6W#U=*)-q zcHWFTFN*<-zmC!jM+rLK6eXin0&l;m2}JypS0bnsosShI=57bxAgw9%DV#@|sVPB6 zsG{7A?9I>ZY6nGk-jP-VDn(Z!MJd^^4}Tcl4)VHZN-GW}=m=GmQ87b!k1>hh9m*ww zO3@V+a}E?4%6vkWU{4u=p&IcYdHX)rW>HC7_1l>Bs8^t{M>)WaaOQUar|kKC!A^BB&JIiz-Tgev;>%Y7Ot~x(gF%_R{l#K%@lT+%OQllsZhLC?19;Ns4kcFvN_%HYQ2haS^vzH35eI|d8`}Hm zZ&4{~8^Evj<1yZNY$b^IDka$fC_(igMVU1EBG21N2S-@}iJ(%{Vj=SNUEpn>G1%25 zpJdCR1l8wI-yL+5cX?6^#`k_L*-NMtwbkH$F!crx%2NlnR(~PcWGF%PQbmb8bcY|E zSP!b6yCo4+irSnorXlbq&-1epbiaL0vMo`9>eq@=?rSC=|EVdcmv>48m7=yY{Gz_y z<`%P>!-*|hB^wzf=sp2wQHT4ydXv`BHgmQ_P$?SEjr-bBSNJ~k+}ZYXycFq82^uGk zV|UaF7M3uITc8i**};D>v%8yiBHeA?Z;XR;*F2BD_$U~|;*NIl-FVK~{PG;uc;hj? zZfOUgEtH^sIT(>!d>Na(=^%e}s-r|uDd8i$*2x2P_x0Gv@gW=8%9(?C3uOa+}uJ5>WhW`)JOeT^~N*! zuX+6?f=d0nxquc^j}bB02Ok{BCbyo(Z|v%iUKZvSN>HCN+^a1=qRTUCDen<8Kq9DA z)<#>L+r6|%e?{4~`LFKq%GrFxnE_a7+}uJ5>KljA)KrPo+L(Ses+Z8wQIwFOpUtXbUCAFZ!reiuTL!?XGA zqOxx-m7;#O=m}W%4@-2*=7TaszE`scE+r10d5d{Xe2x1kcF{&1Gx-N!5*jQKREoxb zDN5@h+05@w9$1hODaFcAqDRW>RIHg{JY5?7?JwJOC@;(?T2UgX6pfofouPV)VWa0n z-l@`H+zFfWSJ3xNeNE7hY;Q)&<8t%4P1Qj_TPQ(C3|7y!D{7cHGL;uD&`Tny6!kU1 zFKU^M;cS(Yd~b=K=!;|aZlMJAV#k@ft-GP+^hbQ)vP6lXQq)rcYhe#^G0dv|ggXW% z0&SrL_2os2*W|i}3Y~1=(9;$YL8Yjtf}%V*zeVTXG(W^yG)GIS*@K@F)IS;Z-Pi-V zfbPz4V|8tbpi(qO0DUIrM6sB7Cs=W?rW8Fu3E9^<-7<#l-CZ6Ey{Ie^REkDNVD_X3 zli0#CUa&tS9KA)%aTAoFp6i&QX~Il)5zo+zefN_HDkaB%>>0P8d5$RuyQld{(HxYJ zy+4OrY-Q({dcxJ$K@vfwXmp*TxURa)Mr|z#g9-;rad(uU-lmF@Q298kcA*$t2@jVD zDn(=P&>Q#gGq$FAKKRx(9B2zA=!}720>3lF(JlUqi1k^C&FCm8jO#QY2oxAFWpxAXGjJ4kyqN>EQO%>Mm|^Tq|!c<+L; z*Cdsq`!JZnMTu>}_-1z> zSY13)BB&I#c%rXb`fWqqAptNfCIaIP%r;g^P;X<@ce{QwoWF%?@dVmJ3F@O)&?j1_VzG_QO zpV!4Ebca2|PD=!pqPc}IYu3kF?B3`^&~G^}1) zK?!-@g;DjGn!h6?Oua1;REp*q!AfSHn_1|MIN0#?1*a{Ppz}PQ6%AU!g8wvvCzF0k z1eKyWN6=HY{~2~PwJPW)TLNvN1kH+otFviJ($6S{maAExxh(lZ(K)aX#n=V2;nGgdF2uyVY~{ ztgIub{(%xfrD(J?)_mFdhPC@q7|!|zVkS0od^RP%_+(;ciV9kOFZ5w?e#9Q1E)0Ra zDoO;EqVd&=(yu@$-2Zxl-@97^{$%*0y`mhR7B^DcB?aKgyAPi1c2)PU3e+<3?d{(c z_iBw=wu0FaZFr{{AJH0B8NED<;o0mC_1Wbxk=G7=?PjahXH>!yUS~X$nvF!m6)oXz z#S{ERzN45gyb_-3x#OwZ4pl_p{=3;*`+IEnN?P9|iwciMUHzRLzumDLpY#P-5?ixbN5g?&Ii})+>8|^x|qWa3}0gno)Rzvt$MPsXc>Y*I-{i?gB$hed=HW+N*~ShDsAZU2C?R(982zb#>Ae>T+ujOf zc#LAd!<%4Cbveu&=dSP4`58ux_~4be>V?{+4)~+nf&YK@|CJJxQgWBIOy+iSr4n<>~RwPEv3IM!wWnEAd){@nvFxORn^m@>w zFh;+U_P3D9AqKk^gP9-DV%s1b^((VCp76yHwa;olico7m^2LmfUg#6+jeR)Q@P>&$ zn)2&Dky!7}AK#b@+U^}xk$hq4{ssq zi8S)X_|)+}p5gnjul0mS#VqGn^0hF%53Pj~vNzT7xKF(2tHnIx-#Ce&QXj6C$5AgV zcSZ23o?F^O)fcJ!cBd%Z51LyjLA|$de?BcgoPTnJ-*T-c5mZWkAGUJ}KpjWU7kOc> z3G${WA^XWxv@ZlJ#~u+z&+4yDS-5k40v17zlExL4q9X>aoIL}e z)8M7aWAOW+Bb<)>PrpT;m#5mu$9fZ81K`V@rODT0qky(hLVRh-b+WYY2JNC-`~fQ8 zOBw!HCaBb)1(h&r>laqK7T;i%WdWeOeL3Yyz9^tAln`;@@pFFQ+*<&N4R!sYUFi;n zAy;IAN{LKeBEB~#V*SDwf6%>cW7tzA3TO)@KK%~D9P2isdN%gqyvP?qV~!j4#Ofr1 zN-gajf^)7T=D!L<;`%8IXcMaEc}g{t-Z_2GVz#?@p(NJGy@y{^yCeBw#PdpgRh~Gk zZe(tu1pS)P%ez=%SpF#wPdFVb5mZXd$9wzRW0ut$*hlH_ws5hN6}L%=(`IT@3nk>4 zq0sfh&}PUwL)?YN5<#UtTn)fk)LM($LrtblGEb||_?3R$fX1lNXgB&5phdW~D-7AR znfrbZ#mF%8%t^;C{Wj5`p;~EB&`apUo+#-Xqy+s|&~v$bF}QwZ8TXErzjG=@=NQzf z!+!BKKPK=^XPZcK2_@wF7`-bmB+ebnw@hm+5mZW^MSbh1@RkLl`4-=9(ws;M`rNQu zn$1=Ic=U8`_|o=2ggJ6jj!}HyXEDFyTaYgf=#P=!9yr>Z@e7^FjO#0UMek+P^`3Yt zQvyf)5F90C^=tWMy%o2;)fZ?BC5)>!u54mGk6FEyCwR3tl=bL`F}%KM>uOk|KC+OB zQ{A+4Rw4?QjZ(|w3RhUzGLU%TyO=kRpJUiqY#`7UN_bg&q5n!DjMnRbL~Yka{A%BA z2HWogB!WuGR~5H*6JOMBwqbMEekLtLyIx9&`>Xw^IL-`%u#cuM*6=R#^Ycs4S0bns zeIl6s+tvp*I=Jwk`E@|oS2`QeZw0+Y-ulD8hf)j|Yeiz#A@eFs2|BMRN&_ei3-+!v z47$@;BB+%74L%;{0ERw#!}MBWB$~fYO3>L3Ene*k!0T^$3>71Uwbo2fse@-kcD5%t zx1Ysl)eVrFav9aMZlDmf1_Z(?arPXn)gR!uXGn4ZPNg87V#b_Lusg#M0j0_`N^E6W=f~ z+8?jyc`K!+tIYl7roGyjbEBxCRQj)iSgeN*KRjeU4s| z`rIEXJ9p5n*XL?78bPIm78R23S9Fekb$W|EG=FHNYi1i~`p)$t+TQpDM~iP;d|js9 zQttPm?TYBAHhv#Se+|E0=6Seij^jowKoeu{8rcmUF}ww88}CK)$WiI^0O-aVFj-+vY9Sr$N-6;Qq2o#+N(Y> zvK>CFb>AQG(#11$>vzaDGUM3Q>|&J0v!cx#`MPWSb)y3Nne<)lO;MsjM^9YUit9zy z4U8+AwVe0T&DE7o$kmWFf=bC(<$i4|FSKm7uKw-5m>Jl7y~2uvdb?ie?evLYq<7C9 zyh{Cys4<^%H302dQ9}3=Rc)U`i@m`PetqGIs66jvf=bc%uP8esw{q`1hoa(VM`~~7mw)ewN|`PbREmxm%v|sEkiUAmh>iZIrD-3f7nZG@Y%pGW z%*M_{RvO$Z=rP+S_VF?N2wyn8CM&n08|ED}w;1OSeOTKhMlF-*XW8NiZ``;OoA#}X z)CVPu^NP{R`6S~K-!&nYEw@UL2r6Zq6E%w`>eM4Md96?0><-JF6SWpfP^&6ty8Yz@ zE4MFUf6M6boKv$Q>4go+xWmS$;H`>1uGx~heReU}d0;7<6R8KbhhHgH(#X zO+~p_sS~`PSU2j))mI#&fY4^~CA!h8f1^KKou%50)1me871q4Fh??cG znw=rqrHroR_4}N*P(oBg!7I{eMTx%G8R};DjXLIXPa>#Pg?&1-Szj~u(d2Jus9$+p zlzv7g#($VwDB*Eehxszoj70uV2{2^NyU6EOPdRO&gz(qCG4!bMs#Y{l6uY9aUq?NW z2r4Ch>5ET_v5zkeJHp{JGuUALFeLQaM|5X)pE5aZp@cZIc-XL*UYuE=M~Zh>$Q)LV#XLPF5md^mS%fyjiT8xs()>

4{#Po`|ffAFjITbIw@e3b%tRNmJOi9@jAMM;Jz> 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țlB$StN4HX335M&v_^PmF z#cPON_ZAmN*F8zG1QX}Es<7y1fntH>p5iT=bdJDUN_-`V`&EOgH|Vd2NWb$0)_Qrl63n%xIypGMmIR8K5hX;Y7iS^cXQ_KjEL8<^@|D$V z=kMcoZh&~UR0%O~)Oo1uv)myjdc3F%RcMtV`^sXLz7;Eo&UK542O53g2&^^ZT}8O_ zs*v-V0>tDTZ*g*kLSoA5{IKt!MS_XuJu5>z#$O|*7xNVpiaUv(qAf>YtrFELGJ6{8 z%LYOlPIkUx@W4W1MOQloOEAG|19BflYJT2Rc&APdA1lBqbRRQdiDiBL) z4Mg?sWySDyF5=)8_Ha^@<+oV<+t@di;JtT-y}~dN(V&=kOa4m^=*z{AMT1q8| zcvXVvT`Oqg%I2bCx@QjQmR_DCu-2hgCxPBA&e7=^m z*9mLUzHl?qcg04?m{_VjF@e7V*elH0O1zcSSe)*7kRz~G$YF2jB{d{CdmFB5N2aZq z+N8di)s<2#!Nm46eriu(b88PFMdKkaj=);Ax|UO`SeJaz0Z}LSEvfLPoLK!> zDcJATBEbY(GsUV{Ck0q^KOp9{%_8#(*oq7L z)C6nEBEbZ%hlOlk=u;A0^E+{wT$dxT7WS{;8~kh~Ry=o=$VDa2^TGsQTibd}K5=~i z%VhBJx*UPEuong~b1n4*{K|XwQxTcgu~y8iCcQq5KnDBOR)qKc$N@^%A<;iA2wAJ`__BN5m*cN zW5{M?77?>U{Ka<>Zz+~w0?!gKe_V7Eo1FC%|L*+55m*cNW3YYlyNhdDmJ_IR0m9Glt zH{kK$1evmtbbHpB{#`r~VxuhAjtMynVsG?%Qh!zo9sO&OL|`p^h2W1Wzk>{T*q+*V zUdONm6aBh)K{QGf%)Me&TUcfjX;rEvoxbI)L}0DhNgjF;DQ|ak3=k{lZ6IxCwxaho zo?uvliKCxOLq0>K8l42$54cFhAjGRxn>T1$_WgqYVz{iolJfXH-OPZHl& zqrLs|Lyn?Ff(huo4QpV8>M?_8_3R~NZmdEFUUZiTto5pXNp+WTdLV%@F4SH`f|}`R z-wMvKepn=!fF9!NdcL-|0t8t&kxXkILcM2&NCeh$%`64`t1z(K`2*qeXb-8n*oXG@ zFAv?)ED}tF?DkTEmb+8| z>~IzdCOpUaz`Gfw5zS`&OG@*14$qeO=F=i0$i|tzwBd6Cy_YQ#OvL}|2KKQC*tZ7&aer`aGA=2O z&Mp)n5m;-Yi@W-iyctG;$e;Q`(T7obYj;_&;#ee@2-)on_OU3~V~ZCgMc}aiHHa5^(z&Y4WWxba1geKRCk)KcNuAQgIRa{w5zC{IZtxBw_+T zZ;+v|W-}S5bE0XRh(us5{N6y7hR&l&f!`kV^L!$`QJBC_DAX`@n?oX1FVHtB8b28{)aRv-G**fvZWOV6Zi=QOHuDM^65zj8dK<$L|`qvqQc5)-;2Dh z-i5yYv|Cy^F(E&@aoq=zA{BemVe_X-1lE#w89OfDNvKluADU2MFvA%&B1X8{gLrr+ zuBg%2B7?`2UDG`OfrJ@A+^;m4;k+A6OmuMuD^a}ORA;-vh-pgax&vwA_wf>eweb50 z(T1Z+kvW(9Q~wvk8J1u|cpX~Lh=={vWq50kU-u>M)B4jDdDA2UYvH>9``w)4`U!957!~XPztxRAo`R?j-&XSw+j+1@`WDgdeSLD-O z$m92ig?Vcgh*%=df{{VRtO^=bGdqe5B zq8<`~wXkgfJiI@QWWkSUS|rUyvH@TM?}MO{&ZslwLk&Vl6fY87RyBwGe1@P1CG>pJQxX;-)!ec9=)WG}&5*j59P1LLofU-_%i$wS{tHW^IdeJS*5 zxRyy$chsi$6Rt}H*1|R?oi45XO_JwlJ=!hvjAUEF1m0gmuFTggGWt_vN-u4b2&{!| zXJE_tmPxFpHKoTkZjx+dn84=**oBYGCf5(Pq|5hBkqE4Xv$LUY?dVL>aBpks^<})2 zw~Yy$^$qL!@Fgrdc{s6}+#RaisGaIn-!HB%_QhWZGo3fs#dm;5vFW85tiie?Wc9-K z97`~PBVZsazS#m+p!5M!Y<35Uz*-_2S6rFh%{4H_QsS*RoyR>d2^^OL z9oei#v%>2~6aO1MB?4)Yuo|87vY^;OH$lu@pLM==pITncBafL}0C)b#`FY zE&~}IqhO40`@b9Z^_@ZX82dpzWQzn7I0_AHG7j4fw@=R?eGd1R2&|Q}t}yJW{nRXc z@YgyYGc1@mg|v4W0E9(?2^<3pXM?)V=`$kc!ERrSVIi>A(a%M|w(AFLZ6Ej^w+{pv zR^OOL+OPqzv$RMsfunR`*H^Mj!u=^zi9@-85`ndBw>p6r*$+H4yab;Shk zIuEw+!~(qLh@T{VLa0PwEu3iuSt)i;*zDzbsY7~#l$C-BTsI6XkePp2zR_0nGvt`y zzlF7Mh6>CKmC}`UzGF$p$boPYwp5D1zcY^N06W8mlZn5E&L(lO1EAufrJe*P@Droc z6}7ih7N%wpRoBcCEX*$`O=67o$5m*by1i;Cwzy?FV z3;Ag$T{GCjSz_feA;$>2b=hNhoa#Zpt*9muSPRGRLl>nPiL6HJ;&jR0DpH(2CgiB% z3;AlWT{{A(!}C~)z*;yD0cz>xpTI`E^`+J)W2AfoOyC%CsCgDUjir_>Py5^tk_fCN zXIk{Dx{DRb@uNlm4U+OEFd@fT4zb$A&Mx$&S6+om1lGcNY*3k^%oXNP&W*;~hDjN2 zn80zJ(AVVY&V^5dBw+UYDHT*J~)PB2`2E20U4mb-m@kPR<7hHxywe;2?1aIU@hl2x8if~IaRBN12&=f^=GMX!(S zbT==$A<%^1d8rPBttc5L}P>p21AGx~F ziH_CRk}3jW0!QgW%=YaBvd7hrwkfPg1lGb9Po3^ui_^-=ZGrSzTp~pFTWqYDz;V4W zUxhtU4s$}Q)%2GLtc5L}ka2jcxH7+|o`!ZU3*ApGHdajFC|#(8upmY`c&t8sS)-^# zU@dI%gs5Vl(#q1S4e7Veg&|tRVq?Vw&d7$!^=)Gnm*H)x?YeJZ#jy}r3tK$FyEMI( zGI2ZDUh_f!3}j=)1kQJd_hao)rOCSP)N0;wh6${NEibSmTXjZptJ8@t$jFo|ESSJq z_z?TCd5jV`s~62PV3$N-Eo?!8+B^NxjqV(LQX;Sxu9pLuNQ0`gbDdIX zp1o(Kx;U7S*20xAbh>;4X0em;ZE0qSb5flmOvv+Y;`{pS7z$_?RU<4Wg~jICCt)1kM?U6aDim%;N!M&1HE? z1lGcN$?)u2ePgx97o%rJ`9mClB@-GGayI#?x6fJkszqtm*bs@pS~xEm>H+L{$*RqE zq}#@YK!ra`CNw5+r(o#KvF0Arw{xT~7llg%*20<0I^Ec=+u6{Bw#y#q2Ni6LuPXjmr6ITPb_&hd;;Z}|`oCi+@U0*I zXFP}&xtB~A_WhYquIXd2!&$DR?I8gbQ&gj)-8qwoz!rqB8fwjiJi~}$7 zLG7yYFHNSIf9(?Yw0{Dtnni*MpF0Y86HjPkENPko-ydQQhCh-BtR+5w@lV%?josSQ zQ&m$*S zkznFl?Rc<$xIpB9_&rX}>qN<_nq+1Aaf!fM!uR34y_iOfv+qF1HM1q%YCd5eEn>kU zR1jiAr_(2wqQP3{q<%hGGXbI#95td$`8M?9{8c10`55a}G)8~@A20RVMJ1YmR-Wo- z{QY39-P)QCidakNq+`-I$He(_vEV;1snYP97imZBhgK%H+#g8<*1~rI=EV5wboW1x z$O=d3P1Y?^ecFdPU7(_-)^J)zFFuRthp`#n;NKQ^cfsTebbLqwS~7{l4j>%td4X!( zvFQuKL93klS$msBJxd@QTx!w1uJ6d?%}31dxhlbgs8|W_zgEF4sZ}kS`Th+_do2@K zOXRon6@J=Pfialods~R^y3VAl!dD_9CgcAN(#AN^A%-?u<3v+EPB1LN1deFd>4v(* z)0+bd(*nv_iNIR84k*-tTwafUk9b0=R5;A=wc|T2e~+oHn$d-oPLUO34lyjjg#3F% z(mM3cQ%07-9FGaCB`Op^r(k#w#Vq{76~RQO^JlKB@c}#d$1;5 zU33N+Infs8b_;>E_=<4wsg;DRl+G~5>OZZiE_4HFJ3fQqX9+*~csz(a+uVjevRg^4 zh8$&Bf(iNk_|c&;y=+`WY`edf2&{##5Hj^jw4&Xfbs}N2Uob4e1fDU#h737(RlBqz zQCB`l1lGduJVdv3Nv5AC?oXWX_6fvUT4n=G$a9HZz790`Kn1er)*Xq!TJl^HnVLcu zlPu+6au&mqi(eCWq$G<8s*#zY=%jA=h$9n_aq9=?$YtcBM{$WheR zkI%4vh;?E|)k*3)AwDlXR!{wmSEBzC=9NVdx7KX@{}7-4FKd*U5OnwSsoEO#|6A3} zTKH=JdsSjh6nh47?c%f8GYCTTJrnhx?BOiC z7rft*cQW;g5}MrfDp^`2KX^thdmKz?J2Hp`5OYa`57lVU&P^mckBvlNEm7NN>9Nuf zk0WM=dnF(`+_ol}v^+ob=CI5e_ol} zOKD(TNFuP7n7f3Bu|hjBhQXmRb(lC)u|94Ov#3Ra3H;tbM}(10=+Axml=nmJBm!%_ z85RRO%O}tuFd4>}8PS*;Cl^r$b}s}LVT%M4csHoiy|ZsZpFO;vkl58;BCwXIy>u0O~gXDXDYbP1@<>Tp9e{fH&iJdD^SpFS2Ap0qKpx1kTgZ>0VR} zr@X^o;$I@4L|`rac7X+QWjNhZ><8K4Ys0Yw6FA2RqF%a&(-C&x$eC98B?4>Vvk%NP zi)zvLJExIl^9o4sAbwNvIuBPBSD8Mx-$8tj$~yo|;CCJ}q=(d}zf%s9_H%NiT?W>| z`xWQ_=24B7%$H4O4}UD}WH5p6h)#EDa8+7lTIYf4XK^al zPRL2(Gt|Jg%SU@|N+R>WGzNdIKRlzJY9{pgbD}1FDTs%1glgk~@U*)@pO(8ov^`); zhdzrW%knhjSb~XXKZD_nQyjedMd0l_;#`%)~`n6GRgviKs5 zaVKmWDPP;&P-`pEkwI?u!@1}F4=fctC zLC5w`!OSATgj@;XYt@%T|9v?*Y~5HQuokYP0CAR0cMzLG@8Um=?G5j_`b!X%sl7A8c@ zo$-vds73-~9Ne*qq>XK<)bo`IthJ+R0PHdfi;AQ`)byK2S{0h1)F=HpmSCccO&N$m zc7R=Hdmvm3%q4>Yw<_C8$pqGtpR2`rH*g6A_}P(yyt1#^U{==Bgw`|4I~0<$?r$jq2Huy_c5eOY9s0Wz{J$lK31m11o@2{ zPuNj0kL~JP#Auq~x?d?wZy#B0h&^wbugnrmXmgj*G{YTiZ%) z^)lYp$xBI65X&y!%zUp@2`0q78tPQaDAp3Nz4o@In_XJ4Z_{({u1a7nai<}+(`foV z@T!JaGQ7fx&*#tmn;}*^u+X0n>kee{cr{elL9+xCqG~<#I0V8LbnSK(p%E`?9X?!M zCa~7tQ=)$HBW(=B0!K=kj68gBW&`k4TO^p+Rx%t`h#YNM)!r4O=^doU-;&+umazrD8p2hnZ>K83g!cX$ zP1SlP9(zkJZ(hpAhBuN3tfkEiM$;O#zgHGn5Il?dwrXj9&s7N~L~Wn^qskb+H+%%Y z`M?QTWFMWyf*ZC3U#dkSst>^|XN0P%L>}W~%mFfYaurr~ZC7awOlWJcF}lqm7-L+s z1EfKNj%?Dm&JuyOw6)zRb^{QTI%Oxh+q4zi@}w)oS*rU4Re}i-Z3gfFZzI`IBHJ)DqBqA{A{+GL*TOKXwI(u=UVS}jy10R1&e1;R zl~es&m?%-c4D5Xzj90u{!d2Z1Tu;XNG&j6CA`@6kTj!0YJ;TVf6=d_r7{jpNeIZlN z^0!2^GOYT>u^BbYdj^f@Jz)juG9b#30|fqCm=M2Uu`%B=qT9s7RnL?PoqA1QL;Ce_NMAU|wD&PfFrn>mGEBQAn;UCL=puE2q=leV9a7ezNB~f9^Ni zsg0tS$kp26P&+tK{j8mqR1Y0gXlkve$<^%njr<49ry^B?iG!uWph88EO2Z8IG@QC@ z%E#yT`o*-flTlPQ(N12fx9`S3!74HHi0CWRsb(T|bh6>=ZL=7bU_!pCBZK0pr;j6F z{p*-SV67i%A&_?$Y&7-ry0IvMCcSgy=fWBIwk;A&$ls&yI6|-b+Ve$kj!Fd9(!2mh z(LW5n$B$ODv&TyI_RSg9D+77FM$s$bvUdzvpuAvbao+4>F`9fV=gPF9tv}CVO?zB2 zTeejRCK?=whWFgtC_FSeT`{NvJ+#^}Rx;4r(#?-0Xb(VMZLG|ox zmS95jI%s1Qj%Y-8`rKxj3uFRoeQPMH4hLwz`RpkjXqkM|S>Qy@8b68yi>aIXZ8aa1 z`dQfC&36Qz#AzMq%+AwU?+7m4D@@$JpnA+y8f;2$JJ1j1rm{>snZR23zCkY8tX6bx z-8QV(t6VPt{8G8TJEQR9$yeods5yNxJefsjf0V9Dvkt0+uc4}9#kvWt(P|01R^)}*!tx(N z^Q36Md7rGhkm+=QogMsABCwWbZP7gLsW<9U@AHfe^?1Ut1QYUeRdjnLI-5kqx+kT%1QYUeWw)pjT{y;u=N*4oBCwX`Z_}Q{BUL)lq*5gf zZr8KT-tY`zW6wWI_19+z8@pyBRsS8leGfX&_|{(Oe~Z61do%t+K=w7PYF|_ue!*59 z;7x?yVLs5w9!bRKRmH#lU&hGcDYVD5H3s{V+0q!85Z)8;n817`yZ~dLrBH`Iiw&+f z?n(sK((K9VQwbf2_ovVYkLMfOUC4%-9G1V;u}C7+&$Czmu1wI36uP6}Ji~EAw%J#t z{w+)hzfrB2LfTcm{?Q)Z7+Y3ocCN2TC9oELS0EyOc1K!0-H&w$%=LNwhuC{c^|)(e z1b?KnPtcBk$=qnS}g|6G<2VL1raRBDG6BE`=L_Va8HioBP7n(J= zEUWQYCa{+L*6y}0L-$mC!491%342ksLKE!UJ5#L|5}chHP%VnYD56NWIAZZ+r!VSADbgNatX7R<`K}w`1G?A z^*K5+eeH)OXOk)n-ZbGH-qn$ahHisxO4z*_hX&b?Pe+$-(=fHfuF{2Qo| zJaUB4nm=K5ZsT2~XRH@Q%35NFaO@BEg+T=Y8cQF%SVRs)mX-*tWffQ+Y)hW7B8VuY zBknQOzVZ@MG0Y2c8Y~h_U`sm0ayk(jK5;Oqb=XrPu-2}=qI*vXwZc5K&uke_Z8wf0 zZ%T+dUKR-^u)huBUVrN8;^q;g{j1UvfwdO)42RXZD0HukhVRijh0yf6v82*PU$ER+ zB$&V+I`FtJt4KfGzN;9|cuNG<61}5DWpGoi`wK1=>EGG+6kSIjjwP7D9y*9Pn_rps zD?3c7o7Y<+u-1S%kucl+O6r~-3E$&=Lc;rOAC4uMz#cm2mwH`PcZO<$+hqc4iO)jEhoM%HD8BR!E&Ct>vznZR0_ z9Znl#?-r;a*l9#Uxrkh=nkvDBW-rsm_`g{0R03;>&*Dy-u4>q{VD-6L9hwOC5{m>A z;t3Um>HQe~{~)lI_DpA(h{gYXc2x-`w5Kw|^z7c>U5OTaz4dV1&D^J5C9szEZfau; z+gzD?ZoPPT!j{~3@IQq1c4=e0eo%>Sp7b@nQlH%STqUrUm}BO;{mL-SK0|I*qR!{O zq`w+wnoGynD}_R%4R)J~@P%1mG_ZLTz$X3@YJmFd}E;|(n<pIKUXo`S%VN;RbotR}9K0bHeRSB#m=KSdkiW*I;!GS3i>9wNw4Hd4L=616L z6JpjLHLn<04Mj(`LT?G>y~5bGji&X(Okgc-?J|nKDUhpQNl*RfCa}lhqMoI?s;Lr8 zXseIW6nU1oDS!OWu){86HEwKU~VvTITK<)V&%ewA}z$YeiJCwl865U!oFN zOWs*(k;@E4E~`6BOla{8MiIpfRfn$m(<+aivmV!SBOp`)YvEn3PRCNCY2Dg?*pHpR z8J1weqlH>o)o7~4`lVr68VKi`qDMq`F17Ngx-0g%qgDt7n{K>0ep}n`7I$-|lWdMN z*Saz0J)A1Rgckh`&y^p1j{~N-a37_`|6(+F11w1Ched!6emTXtcM2Wy!_$`3C z&NqChKBOUY8m>rBJ0`Ff2<(Yb0rXj6Pd4Ozf<#~~`8{u;_orP)m0)LoCaQZl^IMAv z?9I~Y#;gdUeqE0nKBgu}1lE%0tGd_A(Iwf33}@mL^Bzu}eJ~+=Oh!KmqPtJFH$1M8 zC=pmoo_AL^52BMAwlXw5lc?_D%(Eyau!jb``V)ewYe;swt+PyEEqVR$-5*4se7Kl? z`BtKN52vmMn81D`$n@?KOsn==c$n9h39KcrgCDyGQr6j+4>nJSD9oeG_&inj&k!qp0pnu$H{P z8rtv)S?F0S;q9tqv$yX*gd71e^i&QxIr4Gh!e=spwea2!>UdSkA@N_IB*xpONV`5v z;HU-2t~qv}Jk6P;oM|l+SPMto=yYZJJ|XjNk5X>iBukMsn7~mBP#I5oKze%KR@zpP z39NKuiCRxxV(aj8g{j7)d#ywYRIDv|xk#e%RT{n#_>uGH95NGr;igEqYW#X@0GeYzy$V^L*90PGrfL! z5%a6Bmk6wdS89ldo2sLKYmH}{J~e3CS}@CeX+Mk&nAk`nu$COj@#xw` zvVBZ_w%9uNX;@BjVpC=2RXnJeS z5h<9E{mwr&KOi~XZyUVI$pqGtBWn(Sxle9|-ZHqiHO)TeI2laHUgWFmACop+rWj`V z$OP7s<1&6mJs{itM;Zn+%bi8lcppq)&o6WAZM9SX62t*yJtdLjY8@U%a#uA~f(h*T z{cnV+IkE<8iO=G+ZHh1*qh)6{(y}wn5=;o6u=saVoaO(Ez*-uuMX`?4#%N;6097TJ z5T6~JZPH>ne?)I1ho2Nl82U6fXEc{EYvH&>h|K&HPLp@%l+n82qn zsH;Y*(wawivXqK4fwg3B--q{A=#rtESZ$l!2yE5chY5U|gl+|sYEj2^Gnh|c0f-z> zv$j+MYiT(Y>c8uBp88reuKiTjqqi;8uWIGQ1hyK9DrrsV)vxysf9ql|5m*abSRqmt zvN?V}yMMSlWOHB%Ca}Fsr^6OuOkgeXSmT( zh$WcNPA_l*5SG+}sll}4z=a7TEaxcoZ(%KLxd-oSr(jyK@S=p-4RgZX4E1AGr*aHEvYHNe3bw#H^i{aEJp`CJ*uQM%srp$g{2W|VUG~htZ@paZ*Qh6 zQ+-V~0J9ef6W9(9xiSs?=?>;Wrkyw08O#LM!k#U#g_jGU?@yH^HH#;jZ5gUJ3=_gi zVab9xcL{nlxHKoLKjzv?R03;Z4;|D4cmq&6a619NRMu!6-=ObBa>IZp%f=-o@v zL*K>{VQxzx@7_RTT(Gw zqf`QG$>)zP)1B$Ie;1M&3Ay`Sy|A8PLfgq|QQjLG6r_QV_K{8nbNAFLfwl12N2j}a z%Z_@m17zQ5y>u$V1db}#>2^+fMmApe;h|e{JGdA{Zx<0e2)iVs=-MKBU2Hs2-e~F) zvugNdvKu<;ZvHu1trnW?8*MDt>LL|qwW+Bw5L0%MYL!D_?b;6S`M^R4Nmgg*r?+l0 zWXqP-uk9TLJAjK+^c4!(9i`G*)l-NT5yaj1W$5X8p`2D)&qN03AZ21KWNM_Ai=@x0t4N44(9?F$iVjF(m^#2B)B8PzJ2h}cLVB6DuAv7XMn zWOz7KT)dS8y>^VpOJ9Nv(920d6GDul&hpXEw~`>Y$T(S_0Yu=F(`>_%NM0|)1=f%2 zP)j1rXgu?Pwtsv{r6B^B;=csXuGd7e;N&KD=U`{GI=)8>e;8w@+WoRn-m=E#asLvn zP8C?sMUVA2D=!nrT24Hmx2c z!Gu;JSR=O8g%kZ3YyQH)RIAubU@cL%7@mHksgppY({XZZsWq>dT>bGk| zo!Aw`H|HjMX)h}LSqQA9Rnjtwp5~_KbxK1m~6IwsFBu~5WO?aPYguF zhb$+JrVMAhow`c|*1}QKPX z4K230C7I)WfR!2C#oPl_m0-eioF{l>1L34EGO9AYmy>^I<>#JBH6#LSiP~-8w=kNz zN0giVS?PC|@C{dT`!pCu2M19r47!BC6$V0W5w%X4(bNmVEhtOb`Xz+7zZnZPge($F zh;GDN-iI3lC%C|K)u-o$Y|NiN|8Y`QtsNzR2cl*Lqw+)V-V9r`mE=!`8P*Z0BE!Z>n5f z?K*9iU_z^NX*^iU8pd$&-=5g?dvQK-r>V2GnZR1|RqY5Jk!a;ufZJYdruOGFUoR#^ z{mdRu%WJbw%QvUkQ;!n-MVT%diY_SnwK`1|nf z*12^cRS71vx|6Cu0XjQe-@=+k`*W*vLFPJ;DuK1MdX~mbCu+hN_bN?dZ{L>ZEw1O* zvs5LR5M43FU9S~W9f&anE3@J2V)?=*<;>kSjiO67*3#;78Y3e_zq^VLr?YDh!npgk z0CSa1Re}jT9@yJ^jb-tNOY?TG6C?s_HF2=k5112b+)>pF#>k(zp6&kN&X+uj=U9RX zeBU6W>iHU0DhE7$>+3-GY-`BEEDtNB1MGeBKn_lMquos(Sa+?UE>e&YHv2Hf_Mzuk z{Fb|H&wW!>VCXhu6#d~v@Ag_T!N$SQ{is&S*9hJIfT(@>G20sVmsQSE%r$;h2`03P zo5q&OqW_@Z!5o$^=MNiFJGaKKN?SuSSTEg72aGIn9vnID?f77yxs-Bh;V{gn4Nb)L^!Q z?#Y41!oj(qHytaqXYNBhVA>RN*D2y_ydIvqTg z26p{P2kDz*BHPynDpLePHpdZGJ+*Z*b-SJ6a~4W%g*P)~7a0FoEwO)J~Xoh0(cp+4i=kiq>WVYvJb@ zxy5cS(-*v%9}UU@iGcocF7-AthrF@02i1?MjmMJ>KZG^9tmcI6&@7tZ_J&U^#6bkj4XEq#JE@FKoU#72n{=+5PV}WS z)|<8;i1oAb85+zS!hNgMmF`vW?m}=XiiefqD&7CFkbbC>xQDxeI1?LXXwr2EZ~dY! z!xBt{-YuYScpx5j{nvmPHGG?)`HO-4x$Q`az*^V;v4viyad3C{0CDNUpY;Ca|KW%2 z2Qw_egjlbvC(I=81L1nc+px~IEsse1p}w_d2`1icaDd3= zXgF=R1Y&ER0K-*deZH<;QI7u>)@pFT0rIh;U{5V#WNE*GY~Gl<{Cbun^d`4RFroEc zSNC=>Q$M$6Gqxu2dl6+M0&9s5?LD4G!1oJs`C@J_9ET>UmlgXlO4?RyhSSkwk!DnHNZtIq_&Psb8J^BFjocxfqXITtHQ)NPL z@=r-Ci^st3>@=<0;ak#xIkAvmdkct&4smSgt(N?F=g$mFFoEYs*q6K+%Z?Q5!iObq zhFR1yb7DfCMQ3f8!yF(FdC=?I3`;OEeZiNcA8FBWwFlvgih~n8^0ch6*0xT7J~EA?MjQn^5lmw75iIEw7yqlRjJtgYg)Q(YeP%Hnxfv z-`m3ns+e0Om=LvQo5zJ3QxZ53Zqv@PfG-g|&o|kAg>6=j%`%gqE~K&8nEOCDcfY~5 z>~-Szwuggn!eST0#L>@}RC^ih?SRPlE1S)4=*f4d`9ZXZMS=-zFVpE_wxqN1`+RxI zi9m_KTF%=~LWFmSG28bEj4>s3J3Dfs1~0TJ57>9#s_)0xj5E?ZapBx6u*pRldptc1 z#M*Y}SiNe5hxD-JSc2c8+ZUdK?=u2^*>oW83^1_7ok9EK!&6{hrgTa=uf?^Bg{}4fUSo+KIJP%m!umlsL>ap3D37IcR+3crF1aDbK^ek3u z6|19TEqt}$KX*F75^7fD(`L!v9237cy$4%n6vTKQf^YuV*1&E~tHIkW_|EX(!dm#Q zgP+r739CJ_887zXzBFIqyNhQfoo?f@Lo8ujTmFHsljbW-$Ya`P z;dupWv8JtH#acGvOB%g~%Bq%m5EJ-2L2T`VEv!-hM*Mujqt875@Q zcF6^6S(Soy`H-MrP)*lDU@duu+fnD4;kI3KK5TJ5-g<@u_#a}7-P8Ni1_uhlUOmQm zvgRPV`F%nCwvy2>`?P_lgbb;~78h&GyL(!5EWyN+OE&sGO0=ki0K^5S25gg3m#=^- zf|$TsDGB+ZwojCC>9KY|tnD|Ib&jphCtocDCrFC~6Pw@Xg}N4EJ{|@{M2%@|Gp)wm ze>+G7)~Ywn3UUD=pef#_9v5BSL9 z`A<7niNIR7FZ_j7EgbBkBI>?Z&IQ(GP8j!J?*^GK76~S_z97cfNuz-HzUeY^+v3k3 zPVtfmtR=dY2%p7`f4TzEd~HA0p<)l7W}GXXaqzk;@50Nj>&41E?!d<^%B@4M?!qx4 zueCmxhOh@A9eMW4lhVE&YvElc^#9mDgWYs(!KXES2)*L1pt@;{dM3k!{3J#GMOWwVP&RoYrGwbndLqEg5-LkL61b*_N(?)|W>>$`(cmK%;bw4Zw))GBb z4p)x^@6t55yUE8-Fu$}UKD<)_wO5K+f(blp>vU1?&#;y(ns+@{Od_zB)|*9*hJh@) zB{}T+@j$+QvMa|DOyG3|;u#7&VY7CY=4+X^L|`q^Vkw>N&7zr8}HZM25Z#Aq<&q3Vbxy=w#Db_b0yX;u!TQ+!*=QlbEm20|7TUR5Q+Dm zf)ytSYJf}zVxhr`_iI{+e}7w6BCwWxRUIB)V2!2`{-{I&nBgpYD!doLyBeMDSlA=h z*cirF6mWz}A(nj&sT+&mI$mRdw#l=-p=kuM{sXe8cs#|U@sD(dVVm0S5)xIOxww< z>WA>goytfA){;F~LtQVh%0>*juvKME7_T3f*LB6|cm+xrTZlL)LORtB^83?geL zgi^N;9{gs|5vgVku2a)8xx5;2VX9fv)x9i*|M1hGz6}f1F;|FLutKPrFg3p zqcjF4Sb6Ym~hWm4!p=FTBmbgWJ~L;tijv9l?kj>r-MIKHi}f^PQV*J;+Jy!#6aGwbsx6( zl#BjHT0HnUFN4L)1!|JVLbbQqRCJ(*=sK_zU4pBMj`tw`6~cy-XX@HY27H!J{oRo(IsuLksic zi+)383X232IQkd-IBkNcak?XS|Mp%Yu$KH>#SZYNt^PXlr1YN*OE7_}TtVz$e}BsV zIP%$CCa{+LvR@oMeSaIb39BJ|u> zU)cZT1IvU(f(d*_Am_)`l}`F6h~L`zh+zV2Z9GvHV*SG4_n!%4xGyeBD^CmJ!=Jx_ z9kNA&34BLjjDOv!Qa+L=wmKpaSPQ>tP%A9jjlMq<$~uW9z7FXExiTfdj}xHAt@Ke! zK@O8CvI@tV!22<99J$(XJ9}QKGejX-VpTAKqimq=|A5ux=*$~z^~vTEfwklq7O&|? z$e@Qt*3GV&+D*_L-GT`mYXm-))bpf!tz)dkmqrqSwM5n1pb6!`kK+Wtd4c$Bl6rA3 zEC0D6cwa3NOvq74l}cFAcKxofLXNo|?A5=8wZ>-n>7lAQ#D0{8G16z)(7Kndu)=c) z@N2?B6ZDwV2TQ(F>0^Z76~R^)h`Lv zVk5xYPk^XdYcknaE0h}o@_tGacm z9GSS9Gpli3!Ov-tU_y?w99FR*8UOVi^Iq0cBCwVmDZ4UZ9w{|Gn^C_O5PPn6qgC%a zCge40lE(&e;mLRA5m#3tuojNeg?g6TFOe^;vsh?w9Vvbn6L=+p%&@jkN!cbJ+1Ltt ziNIQN7xK?7vGSO}D-rmF1Fw_W zwcGMW4aYzoNK4#2-u25d^eJCAkf!}Qa~d|4VF@Pi2>?!BSJslhcFFv9$XbcOT2OBp zD$qnh%@MJFjNZ7DtRB&kUthDBVF@PWv%%-Do5*(iR=oF%6B2>7M8x;e&yku1vi`^| zBwcC6`x(T}(jvh`>?9Apb(3g_ZWK1l6>;lGhv|*E%cmTPz*-N7l!gl9kr45p3WU$p z&16UCCY&F=%di9!;=FTqRTS8FdjMfmY882SrzX!*@6im zCFkzf<%cgjaxB4w)?pbew@ELC5{m7v8VTbGpzipTr zH;oB7mh+DLM#K6gw!G7=CK7?Qwon?9|=M z7yptJ$F@M-!7D!uWA(wj%8fWFh6NMizIn8WgsjaF_&r)rvtl)hB=Ifz{Uicw;TRp* z^=*kV%s!;&v0wb8SR71f-MYbQPyxn>=^tvaui1cio-N1wU@aUu1XZB+#VP;HtlCes^;*tCMA*t%AHbleXqHVP9V zyWREE7es?6z9$g7D->0)a(d*_N((7hRs3kLl;hZSr znKF<$XUeX^yMBn|aLgu^8nxtYvZg~_&NBMH*CN%-DNNv10wPSCWs=q3n(z&+wn+rm z!n=N*uF|Hvr1aD#{L`0B;6?UT_qF1jfeE}yK(6WUYb4#VF28#8nnYkNyz7U~6xLa! z>e{+IB;YK=5=`J#0;~p8u98vq)%e%WZzTe2;axvi=qukMFJ@Kad0RbaSb_;W=j(L) zE}kQ8o)Esha{)MWT23Zd3-9`Mx)b|Wli>%e^Xh+XAn(p{&cFm-B_NVx{X+8TnZnl{ zbC(FLg?Ifr-G)zX$lPm{xqi7D?0qcf3{2ov0(Jle>k=|5j=$L8FA-P^@A{#(6k&l}KOBCr-d zCqQ0N)iH_lA?v>Bi)I{4Fo9PIh?;EVlW5!GDQnm&St78OeExX&c}+si3pwmp-W14+ zx13BcfmaEL3%B0RX0O=DR*z{fMcx0}lm|}nWkt<)DFR=oI~+NctvS7qt%iCTIMN;y zIEo$YSGA_HsJ{o8H&i3R1lGct1kh_Me#A-37d_mfQtQ;JA0_Yv~`wre{M% z`JXa@wd8ehNy;)q={84LzkI1`M7%k70TVc9L#GSMJJ--`!6~-$LN|%PTJqgZYf2Ko zy*kdCIi*UgGbZM`IjI?L;+zO^G98!2MU2W|4P$yp1lGd8EcjST4pyobHn3|cJ*3|p z6HhL==vNjGRL`OiCv*9jGPBlMHe*v)iNIR&*||ck$4ca>(=2I1S2(M=Lhed=_56wn ze0qgwbw5Y)e&Am=BwIdhV=a8rhir}w^+-_N&ust97Sf3x6ZrHBdxj>xi1#jg9^JUM zL|`r1mhq(GBJ%xBK0f%RY?;7>nCHEAhG^E0d;2#M|0YFw-Zn{+Edy&|%L_!^&s{}s zT`a;+7pMqVWwF*^0_VX(*6-c}<4L|`p!LDK22kKIesU;FUdJwqf*5hidx zFPv+eLS3r8q5NA>4~f89*b)Xrr)Ol*C0|~3kB4OO!UR6OLVU^4&t%EoU|wgSgG690 zY@dU-*3*Fo)b`+BqY6uwJ50zY;lPFtG_}45x2Y}@SPOekbhE{ew6Y1$+L$$MR;H@?_pX?)Y*n z!~s~mh?u}REwE2KFiKH8dh&b`2PFb)iO+|t$3ooec-SYtnztkU&ZM3^ZNfo@C76)2 zAeL9&pT4q1PhPF>A&J0Rrw2NKbubpZ025%00!PmpmMu=@mkj9)OE4j4LHy0WZ`kr3 zx-MFsk_fD|uxCNoKgK{7-9#9p@|sw-u4^a$vh-PoC78gO3s8^jWmVR)UVHv|+Fgmj zT4F5`v5L^!3C4(tnZ!oCZoz}gK7wyJd8u10UF*;`qyK%BJ$C5ApI(^|_Z#X+`Cphy>rz1+u11rm!9J`bq@W!r5**-D|efkeoB0bzIet<4m@kb%o*n_!+D0>qqdq3pRj0 zWzv6#PG#RB21?nPm^k{mh#u;!L3M{da8;3879<3-DJ*|gnZR0j-Gwtw&6I?|B2(F} zgno56hF>%?u1oRvA)v{$vbnqc`>tN=R zBNJE)`)+l*f3m$v71tq*KkP46*ucc}1*P->t$ZLid=lKNK?_@uq1A`7`A&T$0&C$q z3Oe1M3M0w>4xQQS-@RdfWvRn}iEj-(p);vBbRU`uV;nB<54oMU8|&S%k3?WCagK&_ zuGa6abKXBJCQlxIEl(6wbg;xz){c3e6kZ_6xPHl4Do0J0$oRut`{&^=QzJQ+V8S)? zO_HeeKne=$N3Ge`yfXXC(h7!21lE%Cwi`@%$4dQqz-;zc1iR>`q!)g{s{afVqvw50 z65e-{b+AB;6|Z#XE<1EwkqE3M=btxhlb3&R&SHt#ayB_8L_BPdr)9wh=?&i_?)D?r z?Zz3_c~NaCV;yVBnZ@63JZ7Df*Rw{hjit{chlL7FO^IQ2t59wT zj*|$iB|eLYJ(Gq0V?aJuw^(^z>{<-Oepn=!!1iP~r8fCt*fz2xKjvReBCr;=q=V;Y zMk1@xx;S65w+hD+OyG=4SO=3^FxQ*5{Pym85`neksu5?BMzdW8N4~_nnp6V<6LNN@ zW7Fl#ZKge+dc2B6U@cr50OqT-4ealG7e3KW0dIz-at9{lx*utFCt0+k1AkB@Q6jLG zTytRc(=%+*3`ag;axBcdzmkMUT2$-6gdCH)IP5WNqU7hgJu-o{@SLyHtvvsc9R(kY zb)P_KZpQ?UfrYs6Z!cNSi6T7Hxx7SRExdL?-NBtNS)<$-wa2Iz*=&~ zNZwgH+4xuBG0_*6@;@+vbBrK5A!rL*m#;dn*27vNu$Fvc`95F>Uq5AcXyuqf25`new=@p1QwuUj2 zQn#^c1diW_-Rr&6>GhU&<}(eM5`newDIEM!$NMP5Jv#FkkK58|8xyj3>5JzI zB;vMQgq@_I6`fLl60m zM7t`;O&mqURq++2;M6Lv>ig8DWKZ>SBy-t7E+Q-7WCEQWClT#z0QR1#L|6xf4KpR7 zEf9O+T9Sm=a>Uqepla(gOE4i$6PD`u!BvhMMD^FCi)j!CV5`6M^-czKEmwv*71Wm~ ze>VfZN22)LvB-V(v(PpD-`~F@z*kg658aaa`X$E_=DHOTF$}q%g_iht*q4N5Bwi{v zQ)yar1kCM4pazv2#B*Izpwc*8w<~m%JEeHEC;~Zurq2*lR5~-Eb@h2lOuZ3sD#DT? zkTGD=!hZ>(+ZG2KmT$SBR2rBjjiJ+7h-wc+zN+x?TvA|-`n%lVR$U>D&kvCZtkt=b zgMMnNtNzpPYe2-7pJMR*_*z-KWjM#bbA@(=^evNJ^%Gq(mGkH9!C&ht?i~>KSI;sO zK6yvknk^GpOL*KjzjuM(XFMCa_l9L(VX+lR6$`-j83Ui2n+APJ>)Biv;2X!G>tk z!ec{(>5pio{ucZ$7U6KT6xkCCd6UGJ&;(#{{~GYDE9@ zg_O|fbS3&|n(F^BOWq7CriY4tP+#`I|J%^WOU6(&yBET-RD(nUYA zTRL1-g?j~+F|kXP$|Xif1lB5)w;1F*n~6~#-b#(ELL{cqa5aWS4pOtQFA8RsZBtL5NrD0aw+e z#+k&wx4qJ@JR1QswM8P{>#Xl4Ej+ePH+)z*(tqG3#nox3GzKQbJd%=N2Q%IP7~^q? zh05fMHcE0?`FmikjVE07yZYMeA-@)g;v3xwo3}*i+F}^gb+$-uUvP!EE_;36zzP3z zuUfV`u5@XxSLTnCzd0s)JayHJx%!}xuVlRC2Ip=>V zC(903CQltM5m-w+3lm)&VUAn@#O{A|h9WmSNXVsO>QiEth}C9CRYzFE*Zj{_RUTza zj*vRa^cM0Mn23yUg*rth8mgYQJ*C(cU95a5D1Q&E<+alVb^t|T53vERs_~4qi32Wv zNzZqE1aFyK4E9jgu!0p+pidvnS2pTr@utO2vKG4xAoiT!nCR;cRckUwsPCXzf{7~o z#C&C!W)tzhBOyM~B02im2_p9Lt6K6^%}YW_WZ9wi3$RWg^gl9Te)|40lP7=y9)!0#xCrBc6VcN z@9x4ivAgxz-QD`1+1=xv!+ZZ9pAWu$%zV$BK67T~oC7WBidqzuqhI$B>87o^c%V=; zR}RXB>&K@6eI|lcZN@HJo*5N|BH!ava5i4{`gSmkHNkC*C%i418OB{ESe-p#bm`v^ z>9li*?ZvY|Aq)_d67Ay!dX|%hpee!Wv&vt^3llxWVQ*qN85D7$u@|g3I48P8IT{Z= zF7)p+1R3r^4>}eU*(-d|gq*qqANie=lM(`hET;l;yA4O^b5T z-UHSTRZzaRA19pK8K3g^qHM6*WQLKGPrUgxJH&5h*7A<}oN)U#+*MRm01ITZ6lI}Y zLil_u4%R3)&^etzW9cbgXq*LZw{-%2r=!>=Fmp&~w>!c57_?3=As-vmS&sS?4|`4r z7w+h=e*TTX9(A$FB>5E}Xj&BUEzW_4XsW_iCEU4`AiRv@5R}XPihBs&fre|B$+T=- zkXpL3D0ZwI4N*vxl2z*&*!!q#c>g1;k9K367_192xb8=PIrY>W@jcS*|4 zDbLu-HRuT(bp&s%#aW9uaf;%Qz6MU<%E38!V*h&uXyv`~G( z6rtBE9znVI5fwlEpmoIDnnKNVqn092FEJ-E8B>P+W9_)HhEO*hf^yL@53e=12va+? z6~rmoqO{v|-L_-}-QcBYj8aWk$MmsJ!>wGk8eN<;4M5lQ z-A+mB?orjm)*;axf^y;PUG#>Vw%>s>?yd#psb0%%#eT$Vx9>5^zV}X=zJt3WDjQ-Z zzMqq^-A6s+b43yOU3M3fvH5B#)y=n}IJ;B~mn+IOHq|*KzMre1w-@_9PU+(Aq-1Fk zpF$~n(wsG|4|fxMuA0goYUIKTLe8$SoD7Pnv)2XY6c=N4F14YO8u(ceM?Q(-5R}XP ziVNx_nl=DYVIN)9;yD&sM(m1(QR|?5_~{xFT*X<_srX!-A9_dm+#x`{^PZPM5xC#Q z7&BcXtJ2IxO=><-2;R?^gL2WC5AM@{USIt_uCQ45a1`9k?F4I)C$1S%zQ3F_T?D_o zdzv8Dzyso|Yk+3u{<5X&{_>*O`^PAFNx(s&h-bx}VD0kIQZkt)4h>Zc<(VdWdPQ*v z%60H#<`A@D?R);z{svVtY#EyxHT>a9LXt|RU0%a?<4;WuisIMQJK_|Q!4zQiK-uFto}p0r=VXeWW_ zaS5&5Y;2lLeW$ciyPT^lcJtszEk*FxtccE(Hje#Kql(dTg~bRUklYTzCO_vF+z0CrKeoKi*ICKkRF$L;7T z0{0=f>(h7imWvtGROfwS>Z2GALAiL_(&dPEN{?S&(v*qOaG#9B>O>Lm&bxuFCYRBI zzg>ST@GI{A9StXu92EO;H*IH%?Mzbw8`-W*YC-v-xKxVaWKhKLIj*qN%L#U^uh8aI zww+WqjL9R7J;UdUa@~t^)%NE)qW|Z8YO-2K_1)GNY%&fC&QEtjb83B;&(&qm%<7wv zbK;RPe6A>>WCs_pmHB{$Edz|fMHNe_iz;psCrsgUMY+DTZ~;A(A&z9rr+%%FEbjgh z4QCo0l!f6qcX(?fmCsd+Wfj!N3wMeSit;ijqG~5+%~pf61X#*iq$%(#zM34%)h^|7 z3UdZ69PeF5L}HuP>anZq#m7A&;WVIw@?)bDjJX^k@D_l?=PLea2{r!cdNI>xK35ch zYam9lA{x%oZ>**!XWSs}{u0IIigInM>jW!vtwwu*~s&*JY{SaOM%Bcf^zv@%MLHb+zMW|xE5%y zThA_2B2JfaZ-%3UGUn%sYuNK4S34Fv zsrPRO2tg-td^jkm9c3zg1^a8dT-ld?SKQXe2pe4ZTu}s$|NipDVBAwd#8##m%1R#( z@l~~WZDwGU3yqzj)pRbtJ#x(3sl2hj5O$>7M^VJ8+Rm_tVlrC9=T{3nRV}}M#Be32 zT)0E+Y&{1ph&Hl2j|-`-)~~j>o#PRd3$+@!=Lg3iAVz;`s675OQP{Ya-+56kn5V#x z!w?}=ChWuhD>OZ}fA-7FU>`XVjFz-d0hz(4vMU&EOZfMK<5l?iUYI9yhlpV3`jeIP!C=A9xw{LCB@P(eq7H7Y5eHdDWHiscZL3%@g% z7@}KqQJs3Vq~*iBXm~N*LAev^1nb3NxFax@t7((zN$UVjMvHMVoD7P<@7CFMw8_*m zG?N;!d!8-$9-k}9h3`;YYjWjwMrUPhlXy7Q=%C=9>S#lqi_g_nx2$S~ZewlA0$v71;0~#Eb_L`7K*sNvajJ8V zd16apBv-qX>-uSzkn*==WA?$ZQM2R)W&f%;xP8w-!95k@;&YYM@TcPCJx-QPahwc_ zz&+LGw^n1`oiik_vVQGjWnvi~LAh{G1$PJ=h*x*dD!+>rRN}zCODSkIis+LJac~y} zK35%@lu~C_T`cC`5zXa_B94v70`qRNv8qLRtWuhsFRiTnqOZ<5x9!t(16*s1-q?v; z;Hk_SQli3sO*4R%vzV;BQwAySQE@4hLJ@qf4rB{dMrI6DBgRK_2+Aegb_f3#li?I@ zRfgmktC(QLp%jYX%Q3a)F3b6!`NWtr@tT&w%F%aLHn5)mSDCQQy@tDr%Wvq~5-Tl5 zprweRO{Ph$t11_BY_<8RJc4rJo)%ufz!rqn`Nr{r!tJ!vmU&>4`4*Q;TV-I}3I7z! z1zOl897O_e_wfn4Ev|od*D`-(9A`J=S3-C*9WiFb*)DEy878^fsmoW4`Dxe z0Vstc(7Tu|qo(^ndw4aSD0nq>5$j#x5tK{H;;rdddJB33!W$6v0YNGI-sb@A{^oz= zDu=bbaOYSt(fu$lgCcMrf_kDZ<3PY@VeRNFV(0Tbf^t#a7v9@`>LeWha8vmFkk|An z!h5V|NL-M<%P^S+&q=gI+&wRBgZ7|lQ7(*HVEYodM|yJr-_Lz0xiKu;<$29F7z(qo%*=5fIEIqC5p3;XO$D6t|-O*im#?wAQzvj zK8cIPLE$O3c_DnRC<5bU3hy{>$SBc2P5e>osJX*oK36{XA)|Sg+2Y8g8p4E@@&7_FF7$Jv-ldoPa@%!cnJXED?*8#`Lcu{n z-))S~X@kol8}=U&lf?TohYIC-^D-!+&R$*j%vJNjAIkM$tv8HX zh6o>8M!4H-v|Jf-MJX78^ysY4MeEIEN^0#SD$U2(Qf~4xDB{;eAMmTmT91KO5hwTw z@GHOh$|EQj++3zncQYiv$`h&ct!itjwGJ=M#QyK?!6@C@sb49t(GF9}(WT>l$MlU&L^%0L< zj0a1qgMz*~#>MCA{*LR4f9!qB?uq<9iXzZ5b-2(ZSbe-<1uSyqpzQwopfL72pDW6R zmZ|QZ43Qyejyj>_H~IX}VJVb?mMP)(M9oIV=gRN>cct;d>XsMT_*_v0TBhEeA0MoH z+~>@lDZ{TY?>V0<%7r(9&pf~okHa4+Ey~Td{M9)Q?1>HvTDiWo7z_Ksmg#o%c~Vb9 z#7h(5A?S%TzZ8MCtjd*iG(5c57R<%yixz)CP=LUxP z$HHkq2L1uhd^uh3B|Rp0 zRjrM7+t!0+n{uJuCviY;&9=lNUY-n8d;8Y2-AF;Jvm-5vKzoq=cvqaSpsvTKr%yZ!93T&^erZ9s_w zS{pl-{k>MG2fDPhOv)d@At)ExgW&Wht{+fWqkCRfGXKa{>G1;HCbRi%b6l6Sc%Q?C zn&NsT;7o+C+Uql1VAZo$^VqVs9Z(KRL7NOipegBrPI+zuD zNn=fq#(3&lQyXb(6s|t=Yd#P9_gYIS6oGaPhBldI*ZU&xIJ-LV0jz@*LAh{!+<4@9 zkiI|1e%p4j-TV^DK`9i0mJo-G0?%eCL*)h5z7ShW5tIv8YLubtC1oyFQR3&E4(yQ< z5BGjJC=`L3b3(sHhVFA(x-ZwsH99a2bOwsxxjf;%*Jn^ys3GULdB5-X-*}Be9 zpf!(Zp*VsDez(b{^j*_ zErU@gf-lFwQft&L8CFp|!xL(ASa!v zT*jh8$ptSTEs!22wKzNslM7;>~^WO5VRai?pQf#4$-F*TqWFCYm=Ve zRx-VEwS0h+FO)(Ns0S^3eKZI)Cy1jAr-Ooe(3|x~;GKhxB25WSXFl>(!)9IG@w!wjCxaqTtHDx9C}ILnl05#Vau!ugLU5>qfi9ZiQt{l zwK>!qFDC@CQZkvIrt>H=3Ps>IoRibg@ZR>! z@@k7-Im~?_29lP8a-lYu&@Tp894Ps{9CrDFEkRC#-2|ne)(4um#zj+tvr9Vz6w8w? zay7&I%5p^!r~&3^6KTj;o|;*0^SZNb#$~-kLjx)8Cnuf2{hv%P2UDc751Xd1*M?Nk7A7#S4h8)Tzm|=#ad8Y`} zeHj|=Lt7h|qOvl(EdEzGWw90F;6@WX45hJFLz@hSeEUz&1#a z!1ojMwm}a4N4GlTH&QTN#)T*9o_sIKBNFhe45gs0H(SQi8W*1{J$?L_mq8I|TgH~v zb2T`;ws?1Fs4adsUsse1y)u}lKF*sqY%8RFDWZfO(Y-Q^LJ??NF1(|nA!A_E*}}l4 zUdp}`Jc4qeJ=y5(u>Uyxc!6-?iHB0_Bt{`QC}>08_g>V}<#UzUdA88Q*-LqzP6kEf z+~y6Z$%Bks?R9%1G;n$%_fChPTxd_mJJ0pH+SIb5I6C`mIecGy3Z~fYbIYppt8D~4aC!T82RP5HZk34S;kDy#F@8dbxF2eSsfO#LiQ^Eq(Xs9z|A*6oKB%I(z3BbK=7;gT)7FQv%Dv z)YU?j4znx`!LLRs6oDR0)O~ds6(f&|)9ksdJ=1s(FXhtD(N9IlOJc4qepR-1n z!^Zf?=2S;)yC;je6U@69g;J`+8Erh{C<^V?Me~wiGz{*ebo*7i;U%OGE6Q1ur^RS- zGK@kIyo|c5+gm;y&SXh|RgKEPNEdu>5N%cz4R^|3b+$Z$-()~g3PoT%3+_^Ni&xPU zAMvkMMa>7mH$f4UOFvV84eAQxeawU0#mrN4TQgkOV_0w>juebYnG$kGn`?1?Tva>F zuwNZ`A(`vtTW)>@xuO(`!043X&UZC5jtu<=N~4(FwWt);*{#3*$^!lYtp;d zBLk5dy)e1Wa#ge3Q7ad&=V-0N9O7E*uy`@QvuJ1ljFKMFI=e2Li zFBu|ItD25W&?4d)g(6ThxD)zcGH6W(Lgudqf>J1g%7!~-hyE50 z|H^IqaGM_=lneD0w6^`b1u2Ch_;NVPo}fjZu~AEN$d7Z1pcINgtp@!D`pmGv)kpj- z-%l!(a~_$vU&ya{jvMiSHk|BA}AM~`TmqU0ecChpeAGZiUfY9 zu6BB~nD+5mvNvUBm4G2y!_h9)}&+{CxaqTtAY2Bj2NbRXHSXU9wu8$ zCGiN#MORd?SsqT4DnwLNqAdNnwU#1KCqgfvt^rh^SYI+{9wo2(6vZJZ7wRZQb__KT zRQI72{8olVXk2`*$`30nb}NxVa544QaEBkDXB zDrXNAc7q3tA}AMXHPBNHgiq38A^VQnLNTxuQ3`4@=&5EJGOH2rbGl9z%g2qhY#zYN zpa|?&V^hZ%u^&JB=9I$j_mQ7kV!2#VF4TSI*Bq;jV6c^BA0RGwzGIm>OOFv|6x3>B zgT`yK0WBZA9+7RYkY&OoVGsDcD1{1xv+XTu}rce>JW5I&D`J3ODJ{v?v#TvnXADrlu5%;N!1cy!M-S-&kNlUlGj- zYEJG~^m0%%+}?UR&D?O=e2WTl`ZF z_~0WN(<4~4*dIhfdIYBH;G*AuFoq0vUmN;|**Bt*PuDV@Rk|-9P8%M09L69|aUjqi zUAa=F6uq|}*)Ut_@4LZrH;hM6F7%Su*;@&A%0*!vY;+A`)%MN#6RXB3=tTy5Qp&$= zq`RLEQ|`PtYMC&Zmq8KeQN}Xqwv3tgla(!NM+#q_>al99gp`X~JoTE|JZy_7`ttT& zia>v~L&oOq3l!P@R1hoh2+D;%Xn5fmO1>IOjz6nv&KEm`>al8!f_`WA&FAW`?}gN< zQAzTTRNk9G5$NHa{ZdHLJ=GanZdJ}@+bm3S=5s~4(2M+LeI2MPj8$8B=CEAsN^P<0 z3f+s$DCn=vRJyLl#ph~D+uO>-pa5azaNed*5$L-G|0B+4Fv5@2&!!w}olShVlg}09 zLJzOY=vD@zq%ci2m8qgkI6YkRR5J?tPeK2LsMCB}R2bH&?!mg<=a-T;ZoK7UCO%gb zfnHgNFG|t= zW_l>@aAEO=Q@jj{Kp!jkqck+EgNH7uT?XY;W`^srYAh|vg?`nwgJa?TYCHkjB00C} zUj0;{si+>S#wh6LgjEJ25QnF$t42nwS|uY^jme-0{O)Yk(QwP=U~g3_+0LA+74Kc5 zT<94EpB~H%=o#Jm{jnVK`LV?tZdIWa^mBq2QRCwK)lGSiT)ojdi}x$Ot|$UMqu{|b zWSor)R%15|w*Eax_l&Y~P%iY0cAT*)MfZ%dShX-CRt@Y_8U;O-;JeknvCZAKbV${E zN$JB~)DH7^+LBZCST)9l9!0Px!P*!r@`#VE@OCvgn_!O(q7;h2?@s};hK3tY76hsT zL+aYX4(QQsOgiO4-(;rJM{$OO+VvSB3EpvftQw`D4-!s&YFt#d$&?AA+wwtl+Xy3` zfytl<^hd%?G=_}x5Z!j|nlV-2f47WkEIofZetYmK~}DGLF3|c z#iH8^8qsY`21TGh@>9S~&=Bw(Bu2N{Npu@SP%iXZ&VG3lR~$fKbXz8fZhK@zw^ZR;1eOx5SavQ^GnMH1b{DCi$#2=x3F9RFNHn@q(ay6swpD}SQf zb}xMmGua#gJ!TW0ztvVXT(7{ck*Zq>i|Mx^(QS-E5$I!c$S7z;xAilk+Zcj!;o6RW*EN9j(QS-E5on+D zDEkg;9afz5(QORDbK$v(rchTH-Il*!Msslz-Nq;sf%?jn5Qe~#7xA58=?pp?MjNvB zp!J5HQTBI}Y3GH(%9F@jfy<2OHb$Wc)CN$tu4SN%eji1P6=cw~C>Lr24#fOnlayM& zj|O%!qT3h+^#uFzX3%PSbQ_b65=^EhB)W}JC<3(sh6b-FM7J#`(QOPtxlkKukhOWL zBf2dTqT3EZIcTp&KQQazxVE!V0Cz`2bXzwP-Nq;sL2Crg#N`-h*-N6^7=m)4UUDyr zjX{iVD+tkTE9&Wb38PR1Uyj`+*QnV~Ew=<1(QVy5+oYoYff~Thp>0xGbldvH547YP5hjZr89HK*#6yQcnkbQ?oZF4UYLF5Ez*k8WcW)Tunm zc1vZ^Z7ijKN4GHwMW7zkY+CQs|Bh~B2+CzdY#Akg(Oi#i>l&a(w}FpNqohYTG#NQq zR3A5JYW)ef+)y2dB6$7kV@eLS>(K>)2YUQlhhuq1O$}o9VQxnq?&eDQ+LJF-__q$nxUhU@KS!kMI^6d{it>7T zCfnJ^dJN=~?~xh><-xrlsqEelu=N>;^f!etN_vDt#-Zkk^7PO|+tH7D)FhLEdLr2R z{w2?3x^CRC@!E02hL#hG!0&7sqm5j>u3J+bv2V4xa^pX7eSZ*)3pGHn;-#XN`6nJO zp(Wg~K`HpXbEwWm%VaXCAIhuyTbv7YE&k_JiY9|1P=AE8GPtUNj8VNN%3o$wmc8IK zAWe&Mq0ZR82SdDl@=;#-rl?&0Hr|2lprD>OdY;ZjQv#34=ZxxtA_Z)JKh%37Yc+~M z%`)0&LaMG=F4+_(=U5ykmj>T9O^b4&&Im6e8i=X+-PCpwN32J8#%gyPG79R6O}b4= zMQxI%1lp@@o?5TVWufmrJsytbiXw169zJIh>Q_)#nWFc|_*qgiziomriWx0-qJK|^jqhua< z-PRlKvY@%5T&T&;&7Fw)6=-DsONPpuR`@Cpzv>=(#imcDWVjL z(BsIMjMc>!D?bNJlINIt1m(iDJvL|>tehAR_qo$_rTLo&fkV6MI^3O5oq`(jyYtgD zJ-R{GEZ`a433K~JMAXS-i+EK_##@0Y7wWq$@BbG9OY**DL-_-&gETFQKuwmR;eF?p zZIlrAOt#(-??Vxk3w2Sj^%>f0bBJ!+F}$s{ZHjKcVibx%O%`S78gl2J`;%Lbdt`p_ zn@3PC^jc#r=!jv)nQDw~qZG_Z-&t64J#LN3)>}~j_m2hg4;MqAw|C889SvvPyVe$8 z^j>T|XGET{v?!Nh#nna^Sm5-x zjE#uVx$d#NcQZSvj%3agtiXzZoJ1%vKhK5mlptkU2?_s$}x?E8% z^!ygy!4Rv$3yU|eG?A|#)uY=O1^u-LKTgtm1}z`lb>DS`0Kf8GhmS>TDT+YfEzVbZ zzv?q8NcbxXzK|Pzm!4|=)0YiYc$jqdU$Krb`rg|RgfdD z@d(O=eq3`>f`Pbop@2B#Tmg9{%)69={#snmiSN#+E3ao~MbCU6%}$&Ccoa1XMW844 z!P;nJ7G2-8gShbgLEFdWe6A=LdSX$l(dXT_|r%8W*h* zczq~cbQ^11iooyw@-RaN_3+ZPC>Q=6XDHq7GiiFNaN6~6OPQB?IT(c^4BxFGLwJ}E zu5O92ctR{ErJ(QD_geU$abriLw}Sb;iX=Y%Zg`bKnHxm_K#@rgA*Y)T& zv>G4qw9;m0oF9X${A1X!d{;)B3)NX_eh%fJ6pBDkZ24O)jkz6r82+&6HsSVb zv>R&CZMOO^v~TpWUToalKzPJ0Ol}U*ZLdKFwQ}Lwj@CNNA#d?YAKk`mVd)WFAHLD# z={eH>)inTyz;qcG`Xkxj9SAH3rBDQ}?V;WNO9n+yF7&Hqeq3YdZ!4~K3F?B_)!K;i*ljH@h?OIB2cqn?Tq?^VO6uxl>UDi z6oLA~oArpZ6 z6pBEt=H^!w>I!wA^wDh$LAg-FSu5QhGHXsc9K#9mF-2u5hTM{Mfit z`#X3PM;sLk4clVv0+HmDLJ|Cmllt+4IQ6)btt-UCQ3T~e%?UkmdN~fSOR^mvGt>MT z&T~=uTI9QyU(`!ZtX%uAGQmB<|0A-WArL(`_Zp+*eO3pF z`x@mDI_}}ubIOGp4q6R$y`*!O(PDgw!JOpgNRD&xzH6?Qk|5tIvk!q18)8#=Yu z)_Zd6yH(8(z|KG^6oFA_mq%f4QHQ(aJ50&s_ra0`F<$6#W#vF$D%=XJ?NVqC;myfb z-Q-hyN{S;O&XQ6n0==&&Lm%gBvrbXu(Ds%&{dffBLci(|(F%378pa3yoEfeP^&ujK zQqY&mxTtKni}jA6RuewS{bKPYGly-GBJke(s-3Kc_qBRLyaHkpy1-dvilAKh_b)AM zhLuyxmiAAAX6x%|xvjM=*6z)(|NQxeH_?nXrwHU?6pBFI*Y}!^hFADTbXAkK1WGeI z4%beyG79RuE%yJTwKGjgmk?F2wbFmB-rJcBia-sSp+Un>tSXK?c2T*|UXRja2%by3 z&l2hi@BZwvvVv&c_CEu$y)eH$e9eVd{LLhJ`cP%hN3;VlRQ z(FYKH08t$fl!7{SLcf~^mqRwZ4(!}k%)NS|a@`y4)D8+o;4O&|!(yPrZ#hd_22Yo& z&yL^_lneFXimo?}7IgX1NF3@pT8SvF+xi#H}&ZwC>6l@B3&t|$UE z*(cwx8R$_{Cx|N>+>r0L;}Mh#Z$>;^=&Clym`wezWs@!!*(sKp83T7GI4G#SUO#=s z7Ujx6`x;wY_;C!?V=Qc8t1$7hmH1c7+go3yK%<;%7t2{%V>t^R<5FW zpwup5C};qbf|@0~h@hoQYXstsUd$6uKD%z&)r6Np5vYrfOFdwW6_36dCFdhgLs(yO*XIb4l3GKH{~vTZP;c^^+Eif|}*K^9t0} zpOY4bW!w9Zn>cQL6R{s4XnrXIHBQ(;YG}A2_F@}x|Kp>=teHH5a-o)4(N#9I47v`Y z7Iv|*RimJO_4guO7r|A{q5IrALuXbFY61=U&H& zMZjxGDHMS^>X>;t+GMJ>s*V^D@kUrNpGQzGK3B9HltK}x$$pE|%je){nk6eC<)Ssh zBWV4g{#kwUf7O)(!+R9dBOF?vfk;T7D=d?Pi+;b^yXS>;OLU7~N>Qv`agQ3KGUKa+0nQ&2g=$)Rv05zo5>123h zcT?b3Z8}ZQRd>%j5DD~Apa_gYe6wChLj+yHVan5^W0ZI&K39|r_25jUZ)@`k*x^o} zR(xW|NL?z7&@^O5K~FVmfT-ir^67O8?}DuC0T$T@g!*a-q$d zX|*QP+lmd8`a#FV4uj&f+jMc{U`t`l-GjCIH;qAnT$R3uI<=`dAgJz)5rzy+b#35` z&a4J}o^LLFEtb>5DT0?VXj5kOd!f6Qd~l}|O^fHss(Gz-d&#;V6=mz&ipp38S7!%> zBKQc1t}|XJw??lN#tCuSEt{;g7&};_ivsJQce-eiE%joRg}r0NQ z6ZlvjzEz5k2^Myj=Oa=m0weae=09LW>9u^GBV|s_FG}ewO|@687zJbbaD~GtJzhrV z(UX)yzxD`OL0h5}idY4&S}hFM(I%7SsJD74&ThWnjE_R1To_G8rx)} zc|{!_LAfwa4qj2hQ46)Z-M@nJy+{S&n#$*ja$#)Tln{on?LHyfb~h9@!`_)vFlw$z zH=T=?&t$6aFRHV4Z?K9B`8YC)zzDi8ElwDbXD9O2RktU7wR-yT2+C!=R|Rd3Z>&Ff z-3@OyPH49(Qj0cZX(0mMP0-Ti5!2e0RTq{XVq4Pq&kJB$S`>lNpS^pXHgdJBQG2CU z{q}Mp&@yORlnW#1Ph?v-*IkR0f2PHyPzpxS;m#EI{d}&lEvlpyR-dKu?LiS3S;x>|!8!a} z$saIN-tvLZ73IQcx`B^x7;|DJ|C+YDoo35H;G?JO9lle2X_S*4Y(X$4FgA*P4_NeB zj&CwcRLv6QQ-xuV@tq$BWqwU>h~Lz?_*|80*i9(=`>?PGRs%|*2-Nz&#W5L6HyjmgCyU8= znw%mimu}@|eRp5OslwfQQ^kX@a#9NJ2Ve%$dOJqjr&!j`?Ak6C8@;`lj*_BM-`cTSOa z!Ty}u;V2g!H}D>0cuiZ;akFJ}x^YesxO;JodF&(QYR;CYr9)6IewL_S?5bG3SR18m z)i{Wb&I#TZA3L185%;~v{;GeUE0PcFCBAle107Bd=9+R$To5)v@vIz|LMarHCvz@{ zXw=d0Qq!@WVyR!-lnlA!QYZ!AsDhqi$9LRmO0ehbo>9tqxvY|Tdn_k|B7Cpqf}5aq zw8?ZgY@_HGx=Xp~&*zGAmF(aPJtl{J+3WSt9<}p(N@L3gD>va*U`jz7*^iAn7oRIj z0Z*w(K(KNe5R^g@$}nG;OPCC&rRT&8n`SGUfAI*)g%+f?fu8n}>T<|c$rs+ror&)1 zBJlQ63fjnE=7za;ak{!X9_A+1Op8#~nRyu$fwrvX`?JG1p93-qr8!9*YD6kWTkr_V zMdwF&<@oPL@}P51t;fE_!#e1gIVnQ8?F-|a$>?p)6|_FzSk=0=RtlxyH_Y$$k{xD< zG$oU%-i38ywp|63E3lqZ3Pp@meM9`6vq4Y9dxY0bs3Ki=-lxoZ9K|6hSI%v|`0~45 z*L^BHnk}x%`BT1FEk1=(&|cPbn!6qKXet|ed)Zo2%`)$lnFAv^85A+UCfXC-?Yi!B ztoBZ!OK=6@9PH?6T9m7DC12?I3{j@=RRYA-Y;lPRvP ztLS!lv2YeV)s#XJXorK>)HO8hajLBn3%|^%WO3#Zl#9-MV4=^xO3eBxrxFebN}&iG z&B+Da?BLUb_SkWMpBPg;my+oO?rR-uD&?Xps=lJ(>6Zf)CsadE%$d)=_O1`lis&(6 z-#9BlKeNM~OCY2B^8rG{`@hsyaDy_XPy~8Uz=myyv%-MzEOSkmVwOJN4s7{Q+Cw5u3GT)%c|$(aqnT=nkK$xdg#Hp$F1z>G zRLIrw*y7^x*ma8fMxS?C2`N{dz34?|h}$uR#RYdZDed6?T}nB1(g!?BIYCE})8(pN zx545xVZKuGA6^DU9Q^15>!6N?`yWGViB1cbE2ZJ>Wm-bYg`O0!dfHz!+YjYvyE#ZP zh5k_bABs&ut4iCz-1hBt?}}(InG%v)Yc+Ec-epn{wHjDI;3whZyGu&nMR6R0a-j`~ zr3Afw=T4zP(SMXH1LL%4LzXVuVUP=Z21Ub7o>ej`qX)iMvSyEicN!ehDZ>4V2l$F| z!}z!e^SB5}t{3+V# z*glHDZ^ngwPWBDAg{IC`Zniw4WG@hxk}ab2@VA1@*a!tVCA-7 z9`zIuRd4m%kL3W%U3JjSLEFxu=?3_HX_Wpw^MDh?5&9Z!K4<)7HMG?}W&DKUU?X!- zD56FeCvD}_wTxYne(L_5Ta;^VF&u(&wY-mapnKUHRda)W^{&3B+CE^bl4pDjMD{x< zPrf_BogCg87nN-?C4%J<%kkpovwf685%`T-hAt!SbR~6f+D2tjp=d5wlnZy0FPixn z+R}!Vo@%rEsfw*$jCRh8Q68Oj)-)t6EtL&+pNc-JZ(CJq^A}%oiom_-rBONUV1a>> zCpCPh;BWaJJc4oww_UVxt|RsYWl?whI-$6B<`I<3-`Ne$eC4wHJ7)kHThuUhyI*PP zxo11#1Z!}nn92B%{!|JK^SH9!}bG}keP1>c_ z@(Jf;Pz0XGLS0nv?aM}FR%eX_OXpU;Jt!CL)7aX^&!XwoDTN~N8&?Be#=Yx`axwXs zvTtoHH$EsA9h-3b#m_csYECc3( zCewl$E!8SB>niW_N5M&N2ZbW`r2T?-y`1dPKINc2az(hR%G-PDikICu8M-wpyVeF2 z4fjUfX{vVeZLK7QM5Ry)Mff{A!A$J|+EONvaqxUS6>BTZC7MG}F8oHlR7Y6HG*&G= zwku~ZMsh7U`=u%5ToGSb8M;bz#xR-EwzX9E9$u}~pBo8&1IJuK5j3|ZlhdQt>cx+{ zl-3fDpj=_uonXFVxoWX|km@sLkeYkmkQ7Rx2)-P?3r$$^gmPbFp&V}7{uOmY{4PDw z8G6QWSaC9IJIGTfPeO0s-0Yna@wKd&>kE&dT)lf`fjE6(2r1dYMbnTe+GP4(bB7WbxI?@G`zT7G2(+Z54FL6PlWDD~h5C7Id9~8K5gdYY z`L%NeZOO&h8$1~4tj>=6TeiuuDU@=wA)dB()%H$QHmqjj3aU{>0~D7HF`NvF=-taX z1nSPveKP!>Aj7Xv5#p981#Rr8v7&~=%XrtUvU+otpfpa7=44O=?B>BM<8J7Q6_xW) zez0S$;p>WW;WujGx@OsDYhKm*sFc$1a!d-P^L|t-eI|&BI&OC;~k~ zP%D}x47AsLE0lami{$GN@586%;No+2Pg$;9J}^TmH$N_gr#KK_THs9~dYRz9zS@GZ^5SqN|3a`w*@_Z=pDVIE_;8`}Qr}sH zVKnawMjamR=4wAkYGp|?_X7l_P{f<{#bKu2sG&_Jn{&9nk~Pi1a=-PV}zDBPdtV8~#|ku%oC0GTdj4mRxFNkvdAl zQ&!b30{*(fVEb7p4e}}qIrPJ*Xen_(L1=S77*(ACQLDpfX|LT$3LZEdtb+~;MNA1P z7P2C*FGN?31Vla|T*}nuh1j>+NDe`{&U`KiFJj~ZJ-#v^g1+^Yrfe)LrSuz~LMaqs zt6vPXzFbi9;ec=|F+l2_Z=6_q+9(b|xo9nbjHi*3)3g|IpM7KsrBH;wvp?K5nH}Oh zyMm0supv@pu?eDA;3y73x%k>0n$SzxSwD+t9jfm+KLxl#beDVcY(d?! zo#XgQv3=K@XtcCa3K=AQhw zm}9};LdHP;&3lS4ER(@{MC0C!i^b4R<%LSYJc4qeJz)J}_TF~O19int=W>eqCg|}# z4vJd5H`|HYL{|%)j@4C?`ltK|Vzvcy9v;ai1ngMa?(X=KFK9)yS$fsXCWXJQDxK^RsaYV`EiMN3u+dV-83jOR zC<<}ZlO>u%*gL2AOLaqvNjqysrceq+(9sNWODFP4$2vR|A9auB5R{AN&}15;_(@-u z-4pw_i)MCFtxhO{j%JA29-c=!?H?@$ERW$3l#B0`iFfi#E0>KFvyYBp7B6iaPy`*# zaK~>KKWXR9l|uLv9>H@tdL`Vnkh6f~6}>`Oa5sio=(YYz5p)g)8-Q9sn%eQKdF^8! zLAi`5t6<$9_0o7V4DU$68K2$hLVf1=jq8VgbL8rhVJiGj!j`ZoEpmWSC?YQCXR!Wv zU+C>?bcAVZIt1my@6B&_1vj0Bh@npxD;b~GkP6L=g1al-;S8t0)~_C{^?(snTpN!i zJGg_5w4gCl_bA;*sq>_&^l3yiSkE04iZJXbIKzPqHLZg(Y}|V>A6Qr@f^r!CIU7}9NxlkPXVJPe%9TbW<+VFGm{F)vi;QxSJl^(l64fn8$8c%2sHg6)5Qm=Q5tQpr=tpRK7l{9v0*IhRi_`}3okhko9!q}gCQN@gsA5C4QW^Gf}GHL%%u3vp^@jkfD-rd2tme4*8`*V4fTK8HROO@oI zTyLj{g5%!@7rSvcxIxx2P>!EhSE)D`?MvbjlncLi4t-)E#x!wJ$FJTf{IwfbXE)qu zfIixjMOJ7d%Gi0qUJ7^H7h#5*e{zpdqD~asM`;u~qSBRPPjj`IeXB5X1dpIxw5Cm_ z!m)p=BSsz+9{!9-p%jYX%i)t{nR?v+is0UdM^G-l1eWA$63P2%GRJc4o=)9is0)iWx#L6(*2$2R{oEb zQ$tWLx)Pa8FUz^9k#3^YH#m}8qbP!}tCzEPD?Ox|((Fc29D;Ju6%}@OeTT_C?ue?_ zq-e0*d4^nQTpX-`LnLG6w4?4zJrHnuSzl{aTx%(XB8+m_^?j6ca3`fl{|u`CEZ&N~D^8rk(vU1|BHh{8Lv;0^%niP#fQ{d7@3z7|xkMt|BvLr^Ykn>}d-?D`tjIq0Tx zu1=Ja>Kd0qZ6wt8#@ip(?8>D2#>6SvLDQ!cis0=&^Iv9C7mQpc4?D*rC>L)PDO35E z(yI9ex#nj+0-GZEI5J_$FJLH+*Hrfo9H!afSXvhvX9U|q zQ9G_`JYs>^L(ST`rh3AruY)WtiWok}1e$CSJM6qbhVn98_52;AR;#vC z)w|+P_APmeg&UcSr|q^63E#0Y?gXWbyf#?L~yw> z=0wfjPUV?Q88(KhjoSWD%I_GZtvIZ%D1!IOl<{w-_U`db`D+c2pj><}8Lwngvz>dZ zT{Kxr#~neWB=?Xw3&e-c(3J#4|e5h z@0ZFlKORB3_!+MH(Rr$9v&(OihiSSGya$(xn!ce8Xr7q1fgghJg#JuTE-)V2<4W^2 zs<7my5_E4c*B*Z_`WlS>k8-<~O0=dy7j3^ry>jEK67gy<)55h@qllGxKf^xM)4ni# zHpu9*b&;C*GD(@`AHg9gm$B|@o+hx73Cq+MeLE`eydpS14n^Gj`YHHmLl1jIpIIPd zWyNJ`)zq#^lqZj%T(lM-TI9oO^+|tyd#^RhPw&L*P9LN4-B-tLdK@->hk`texO}=RE7xqUGw#!Jme3El3fS zE4>e%oBL!cu2*m?_vfYRyo;r5YsW-z2+C#b$l#_@+&drqwn9aHcR=O{rc-Maioov; zM|K-B{uhFAAp%p?bG7bA6X`;lkNV)vNX=Rs(WkH-t+iiT6a;%x5j$FID_1HCc4|L6 zSO+0jrzZ}SDqbzAKIk+YVu~CTiZJXmcHO@FxX>7BN8AJ@$?1<>)Q;BW&Q{HCY#*1} zKr*Z?+DL=9APSV1wiU>$G&037+p9*QhRA=_VY#BL#|$)sV)UhsGwAs77edM zI4H>l{2|`Q!ww#dbTUS)87&RZdqQc{ZUiTTA`TZS2J!T{z{eU1G7e7|EoEQ*N?HHU z9~-ikE6T-}{QR1p(#Wlcl-o5&F*~(ZClrBo*r=MH(RUvY7$mhEF+}x^9KsN>tS?~KEvbtdSj7{K%BbIJ z|FOMlX;Ci24r{bW=DK;Mb``6sRUVB>p%jWhd0@-bbVls;+E2or>;3lAco-TXX4q$p zSe$3|n+W}#*UF>e_B~3W2)=gjoS7(CHosT;LcAzNP%f%#n@m%dbrVXsyir^rQkGIE zg4QAIAg|64N@lI7Mth)V)DeY5x$ykxKgEjMcP||Teb=q@am!t=Yxuc@ zav645ZB~L4FE{f`*+xbyy<%gSS4N{ygkeL*IT5S|0bTv1lFL@e%fKQ`)1q8xxg9>| zPO2U|c>R-~H1g?6dG5WK6iT588p{nfvg88Np}${PPl835A}AO72RqK#g{N~NS2FP^ zQZFKY<2YbmOZ?UmRs-?K@If*PMc_C1JW~nVFsrgbVf^qRR;uoDD<$B^QDLoKYx(`lI3Y)+>I}z@{(E8{X*siXkW$ z?`dfD*biz9Uhe?}rBDQZv)%)7KK}#3xG*LB*1Z|&WiSd&7r$9w__qv-U|dFSjg$8m zFZ)Rc&1bDij&f)eK3#e?W98R0aX{0f*2M?;+ZN~;q_;_!OiMOx7rT|eYRi%>p1Ws) zBKVsrq#-NSvVmUG#xjFBTez`53&H&v?!1^K9ej{kC#xfSR+WnM8OquED8kt3!9I68 zwB5qIL)AI?YDpJvMsNtqg?n6P>ou9C-^^qUX(dXo@alqLTM9wjlCjFb*`h(rwq#h< zj5SIkYm`Qz2xIjzysvFn^bm?&dM}2T__Gdb2+BphClJ}cJyAIK>6N$yR!&Nx2wX*( zkJV)QXZloO!pSG%+QvMBay|K;1MEJzU{ql|^@gx*LY;@l#OpibK$CG$C?Z=%yag;5 zc;96}lt?YC#^tT1+@BDmy&l10ZfN`+wa-8=dC^Q=-!4e`?yx#TEY3-hBKTcViS5nR zzv}IgqlW*nI%^2ZMeQ?i>rk#R)$Q0exnI2~?gS`Bpat5o$9XTdRx985WwnAoil#-m z3@;GuOVGO%o;6JE$cx#q>>Uo-yfyfg|$;CJs{^Mk>G3L`2cd6hbLWjD(*@Mh4oC>Ql? zU>@)%Aqf9H+Rz=Wwa&Qa!OLlmZ(Ds=?e7h;9ss`R;n|gWL$iz9cfE;SIyj=o6 z#{u}x3g1q6CqNJ)W**T0jwzc=Bfq{C`rMD_C=~IcnF9^Jq9RkIiOVc{0Ry9~*6Q4(N2+D=_ zQt+b&2Uk%6aVl3A8GZ%zeS8q5EDX;AdncWXmI-Etga%S*c8e|UZ4@ViBBn3M0(Y!k z3*Pr$0U7zvRg=OC9<_GA63rnf7uu8I)~R3^=YVjtn{00O1WTvi@j;Zb;fOn&w%57% zT*X}XmF^8|5ZJwREGL5^;`?O<&**jVP1!((^1xH-UN$6f*#I6vxz;Vt3RX^he*_U# z+XP7UhTPoMbX-gjrBK9&pIO1bdLy{rT`M3iR&lNd9VJ0%TE9A_;P)Ueor|UfdxLC? z0xy*;V><#Rrxc3VUN>9F`65h4qrtDmWA6)_6Qwu~LAlEA$_D4poxqxkxhnSPQJ^Vb zSqr?*6+|f%(ei$_kg~fNLhJ7E58OK#G<=SyHYRekNe;qOjW|B=8~={Ai{DUJRc{Hl z5p$~NeWD{6g(AMR@X&Gv_v8R#WcZk5iuJ-2&koHtMYW*}JCWw}hmOR=gJDeqSf}10-t`;8JCT@E(*ZjycK8R8% z0%Hy4*Sry|BLaKHi|5z3H4jVR5tM61UJtEy^^z~RP*2L1r?Ks$b94};P(;TWx{RVX zu&#Vw)J?8)bDQ}c)D=ZgE?NuF6C0fu?oDtqcYhWiL@5-(>PpX5K9~`nj#;JMSx6C- zi+$_0`@Zrj$>U~#IpFM2ZB8uJ;YupbqH}Y*f<5VSYR4G|f?hPs44RS)uBcFsx4!eG z(SJ{|6iyw+acwx_0=L3mPW{s2U^>L5HZvuU7}+vp$#9NB5no!kfM@w)s$aW9Amduc z8&ZZWL2{vny*UKs(%&V!h`T#LB!thB%15lVC=tVhC`HQR0xQ7xXHp}d9^q0lnI4atBCTGs%5rho2#!J#_~MvT*y&Vw2NYy{ zzy4T!-(aK=&?1gQP%e+M&fw`kWgsRMPZY%KW}E+~_#mXzyL()tK+HVE?CRgx=C}sy zZJvAH9&L}~++h%hpj?y8Ov*}l%`JFYt8>f?^g@M&%;?oyD4GB&?uh(ZPCNaZ5Ds?@GLT5qclnmZJAWw&L8Hw0}QMi*n)L{n`yP+C00{IUz7?s?8f_IHY)wU7@9g?SJ#@ zGVPo8F(v{3n~*}&N-tyC>yd^G2PQ$2!6@kw-{LkI+>rm7=iIq5uj-M1Q1>IR{bq&z_6-$e9)w_%H)nT~b3lSUL6QyNkt661XL=cXN z3ysHU^LfGXE}*v^gqzq7Y9sAjk+H^V(Chg_X?o%y^ANcQN1+HD!T9EnKF+_+pDhhr zWwOTC8_vnV5%ue0g0=!sw8<3FVU=V_-4ZzK*w7$Kp$HskLtHIV{2c&9O^H~<% z9l#+d7q-ppmoZvvf*rxI4&*9g<}Q0KE{X|II)NH!M}@b|CGoeN}&k89M%Lcso$hR z!pe5B+}w^_E%t|NrNzA(TP5I(d&x{e=H=bw#^>5=dI_UY1op9fe+^94`|gRX|6PJpDm~1VSMA;Q<01lAy_EXE$&-1b62W+#$h30s(@%-*LFy;c`c^ zyED5S?ykYz?Qr|5W~O$l6TUz1g*R`je$z8OUH$9ouC5jkTnpRSg%Q1tmRRiYV0qqH zKa*qc2n&~BO)r1ZQ)_+VQ<+*Wk)IxFlw`6F6UyKStm)gMBD8NKN!c?+%T1ci2nmfH zAt1OG*4xJxziRDRk_s4oiI>5(iG|l%uzyY2ubgeXO8Y%Ef+Q&sr;9H$5bL8Jhu)cC zv-#cC`&}vn``w;B)}<;>k`gAhmM7*eV;j|Ml<*`Rf#2q4lg$?Fi7>A3hFsuEM$_r$ zJp=^T!f$hF^)`$C1^+DBLf*9LThhdFdJjhofCxOTAaxJ-u=p)G;=r+x{7+N&aD>1z z@H~3GjAFyC$OqDI52-a-A6ZhL$hEMC^VHQd=-~!K54YIp;Yh*}*m~exI*6nBQ4dE5 zu7xe;Q_NI2djJuYzE6|8E~uU~LDqXX*O532juWtDz{)ga`PX~6*gojR;<)5f(mi?8 zwE-bZ4(UA{NjL&~xN4n8S$^EZQ5o1@<=!&cf+Gu#)_b@x*2B@0a0K>UuqtlRM;UJ( zy5v%q3z#lE(|b5Va4r0r-R{jcS_$@WeV~V1YV>eaCukoZT15uU+~#U`+sLJQ4@VM?5Z|Lp+j;Wy zD!YTHmy5FSR*j?AHrYE_alDJ8%)FO7TX9?}N%^YJkjM1?HRM0vXhDKsZEM|bR{U!G zPOuj6@}~T_NSw{fB$O5_9jU$ zy3LaVOcR6imDPEWB>2sLp4`KVtwemPI}cXM+iTCP>~(F3AmIr7{@>5_f*C^W8Ajc4 z$UP61O)RGL7Z6+vYj+b8eFX&95}%6uQCz|icn!N%j57Wgg0v7pH3Dofb)S6o!D?I6?)`+P!rtfC9D6L- zNAXrENf%}fl{+uI6q0ZK2tmRT*k7G4ZnNn9RYaAQ^7mMOJ7Jbm(XV>h@&_H zYhlupIEbjix>`{@R^Asq$@cj2aN%BD3+rm}fbmwnt~vtajEStCG}>UCDwY4$dH}A4 zy~Mf3dyT&22r$mrKL&)XGZ=>?9DzL%d_k@6lB7b5=E|;EDQT23rlvBmXUM#1pVpUf zv?R5FF?IR5n}Qb_V`_R{j=)|5^0ym($;$^Wxysf8rp(WE#vugP!tbB_@qp2n@X;%m zpq=NMa7eQ|@u~O-nM*hVZScZ~!-m!N{qRDbQTVm#ZL#her_xh#E%BTGFMp8ki)hHW zD)tO+FO?*yb@IKgC6@8D@B#C4-*|h@efmrhwJVN5yD8NEm{G>>@q^@|E8g0+o*N;w zE3SogIL7aUQM-NeU62#5?=z*H+Dq$qNrLtPxmYzT5}&H#Z#H?7@;PKf-~gcvjzEk0 zZQm)wqJpYSk`EvMU|W|(Z0%eNZRNs<)0%ZjQqNV(olDkd2dQ?s*3>Y41q*BkthSK% z4Ef84(n-bhMO(OpBT5Hi&gJt~9kI@!m~;iRG^iHC>1$MKp>j5_zImQ`4lWF4{z-zMu1~zNc9#j_)9Bh8M#xFV%Fhn}sdD^HlRb^G5=UCNt_Jb!?v*$X;^UN9`Sc|} zd($KON*qZz0@v8!G-6{V&h?>_T;s_K+lz<#N*p1$7OuAqS0)=PaVPq9wZDz*8oJ9^ zi6aTFtA*aswQ$Xg?j%Xpr}gE0ZEM*6`=GDHk%S{~jSW05wh}jlt;7+6YvFoZw|fc3 zO58KKTWE#ATcKHQ;mW9|bo>ULX6ZG`Yh98GC;7`QfB(}oZ{rx@dvFBigQ!y3X2n%m zIO}&(4LK~fmg)A1(E@^NC4bBUGr~42WElrUtu8g?nN!l4BHNC(aLIN5EYRcFtu6K_ z!kr{3Fnu|B(%wF%(mlne;t0$X15ad)G^tR=nuhJ=H5;dej4PzKU20cc3o|L@Pp`nv z0$hE~w05q1u=TK{Zgrw8ToSS?6U%gRyHxyoPn%=KVN;lmLwd3E3uQFO!JyE@>{uga4i?s=T@j;JzAqg^P ziL1I6_9b*DJ?f=7i+UjmM<9!;e_XNXQ7;7{>ZLDmZFeK;g%Df|`!P5L+@eRlTz%a& z^y=%Ip#=@DB?L0(;sJV%@>++PiP`@0o2q}Ae)&shHToVLfh;W*}x z)_@!Z=Nf7)Lu`p-fz|G^NGEC~9D(eUM!E?+iE&GnOMWwd=~E_npuuW{;9AH;lb$p) zSZx%rn%`3otC55wkgp(XEcieeC$r9bNpPJ?Kf76t5L^p;)EZL=QGeb$bG;Y7AvFzF zBLvq%?i$*wnZZ#@fz<+m)hKR>BpiW#{?@vLNXcsW=Gc=VN6|Gh5!RK5)$G7(1&z2R zl5hm}yI8i~i(Ug(3j$Wdo|@l_Yaw?*^runAoB8hGEf=sZ}m;W)E>cDs;SM%)r1 zxE8XS=gqSXsV_@u!fhG9kJ31bBpiW!b*}Mki-*kG!8!G+&T47`;64B~Tq&7Np^Pt;$R!5X5`t?X*Uo(wYSBH&VZgQBnFmP{jzAX0)68|QUA8n|$cL@v zY{-NBUR(?N`P^IT8i=gFy)y@7%NbJC;94q!Ya!RdZYr2*MLsW8?m+PQo*u3x2}dB; zmbe;hAX0KI+BR`5)&gE5KVn_!{Vv7`BiH6PxRxZywS+)6z_Rte{b~vik`P=A8MRUM z`bHU#fNLKyt|bXaARA!q>Q6NYJji^z%i7)>9wfaXe{l1I*T*WL!%UjzAXe5YfTu;o1=3+ULe> zEg`rTa&5@2woaXEbNB9Q-^jR@B*>iGhv-_^m(ZPH&Jwt`9OGJ&a0IewKww+vkGYl* zTno9jXOGrSoom|w*FIoeO9*7n>;8I;Vy{iL4mLZ-UtZetFOzI|kR;&b4cY+ue;M!@7Ye~Wp$fB7y zB}0Z~7|wDO$|UobL4x zlB7gX*=}}rdys_S-vF5!)Bd;K3;LHoImy;3Bk3HiPDvOpz5ra(t{)kM<8ESy4KREv)bqEOM+{I z2Z=jh_`SFm_NaaBgh=T@QW;zeSuG&&p{ldm^85RPD>4s~BpiXP26^rbL`qh}Hz!u3 zYh*R7E1f@ZUX*)~Btcdq1oo9!w%&_Q8M!5~3iBWd!L^Xp;7lQ-jFj`DB;g3`cd>T$ zry2~bRs!ZlKNze=Z^+*qSq*>JIcha{kZG0qA@_`(FC^gzWS`rkS~_(^O<*;(lV|4( zA-EQDm*>q>dXOaH2;?i6b8>oEjeC%U;99&Epx*@#vMJ1qb}?A(M+mAb{d=UG7bOJO zqHDc&`Tjw))Py`PH>-8K=XA5$pRcAG7L{^0AZZ~-mF=jySjFxDVRf*f^prLKj2 z3Ec_KaC=f;US6Pv?W3^Bv!p=Cz)B4U)bGM<8FFZ9LWa<26V^a4lrDho7f7byhnvxTfswo6Z!|Mqh&@ z3360Io+(aZHM)}|ZH9T#nQUH^BpiW!6*Vux`QtT6LU1i)gXd!=J3SucX_$fRYOFz$ z1i57Qp-E2iAn8tSj&ggDBuR<*=gmZ|9lLqZ?LiU(zX53>C;GN^X)G#9?_mvc_^<7D zUNY7oNx~86>Ghs7QQIXZNp-=4yt5{MVm-ryBm~z&50YZ`VIY${SN6MfG%>&7L6U?c z&^OGyDPG%GCP@pygS^c=NJ4Ne^dLQv{rwhV21y96gVuP zITN}i5&qn3kc8k`$hG|+*o-p#VVCJ~w#$?x9D!`G@c9Dyu~W$0YH@NC`0O0dh6 z)*uPNwRkP)yG-xFF4G8uYpLEjg6itWTuTV9Mb|plmTQ_`-cl@m$g(SAE!e^#QVUMl zv_ZU=H>~y~YgUwdOIBcpj4s_P8t|dGoUn00;&%HO3zu*N_VacAzG|?+wO9To$2EW3 zLa=j=;96+^$sehVe;(A3duDHD>T*P9gAXmPYCM5G9PW3;l~Im{J-n0K$)ESMNU9bv zN|0~_W-LIo&QULK6_mg1$(vL${a69PwZx|~53MOTe3;GD)8Ht2DrB6`F<0CiMNbH; z`oZPoQLo3CE=K5UcO>BmWS>b-E*l*6Fuc7Sba`P&QzK4>5L^p6s@uIw21msK8(bX~ z7V^l5ZbJmF3uD|GUX$lV`-dSfHN>i6?+^&FYFIxcNr|Ac-K^&Jya>T-7a4AU(MuX9 zO45#5bLEJR6Ov}v*F7(iaD@0CDf_EJ?UUX86`Ycz9>wRF4Z4XcwGT^MbRoN;?p zvc?(QZXjC~i+bS_jzCt!C)E8x{^lIPwYYUbt}yUFK14PSDd#m>+ecsNTC&!YAp_tw za)wX&?64w`(WUor)kcmpC#@bI+|!sNBLvsN9?nx&XJ+0FF5LGot#9XVj;x0Dld={d zXJ(FMb9B47;2H6HWDV61NAU6>ufhIz<`-#ZhKwpMBDfavAU>7OgXa&`O)PU%UmxTW zj^MSR)otvRMiorx33IJ#ojvs=*n`r;VLVxA?Qd~=HyT%R$cN{Y9Gsmc6+7FJg zwbWz4&KI`7mP=A1D1ug!4t%?-#oQf!AECtv)4h1i?$j{74A}V%{SHTPNlFBkp(BF3 z-qFhV&^Z!%HP5~9nzS%)F_wLI?Num)Be*0b0?|4mf9?OYGM0bo3hP3rt#J#y;B3i~ z5Q%Wwy8BQ@IODgZRnBtK3g;WRn$9R~m88ZKugWE!Obp&przh-7^+-6POVVM8zQ%o6 zKLeuvw!`w_6U7t1H0moLxYpN=hv6IoFW0*OM3h=~QSQIVKk;#3FAJA&#PSyht-#LU zE0hPsr5eZO&I3jy7Ms#XKyaXCb-fZ$qqedUCgOGj*&H&mw3d@KN)c3~I2N6eA$G7WC;_8UKC7GA_SAt>xnw z3FE7+b+=og{e(f5J^1F^t^FT(x$qjY(Q3b6_pb(sZ@GRqAAA&@_(mQFJHtE@ju6Xu zd*H44*Sk@{ol3ojf&NAP#j-oq}zKjS@| z^z86dPI@QIs*At7-@^g#5p2ANTfz}g9^@1Df_XB0DtHeEyhoz(9&QBJ!fX73?)PxO zdn6j~;g)a&ya(*9^@2>E#us$J7fi%2$R!*BZ9#h~cPmjo75fs5t%M`E7PJ_rrzL`& zD_B>Cojb|4!D^jk^I*-+yq7b=SwCp!+0tE6dcU2K_sX&P&G?nZDeyvCkExAX2lT~~e^bfZR%4L#6m-4c!fdvp$0yj;4SU#@Xn0Xq*i z?A(puT42pi7*oSqH`;lgPP3HDSCqtlx}Ccv93j?K^S=GGx{B28+>PK`cHTrbyWbm_AORo;DdO^ z`bwt{P+pXmOxq@F*^}H)#mfV~*5w&xY_E0O@wMlJ;Nt^gaMb6Pa0HBrv>MSzOYPQX z4Sqd$OA=-_;s~weViClC&6*CPVo@OfN`*M*dyVHm+P~;aQvwIwd>wYcf?&O z7_ulrL~t$fd*HW@%%P9rxP&9%>_JvWg{XSUfU3_!?qwZ~V;}dof z#e>>N-$f&R7$Irj9QqVoKi9s8?oHf|Oh{ZX=1(mP6qj%WFB|VJ#r|j7lnTMMz!za< zthb*IUU+Cr2*#>$2}kgDEJ+yu7Y9&H){waf3twQTjhp_eAl>yP2pl|sW>B~7@B z@w*-gNAO-rlKM^>uB?66$yTx#W{vP5xYoM>Z^*Zv3>=B^qP_)MDXu^tn*_VIxP&9b z?@`W?MmcaOF!b}2u>yi?^?#7nH9udn6K33?i~~C>D$C9%n8s8dW8o5x5Wfe^0jSu3 zaSj0deix2}jhO=N8v9yA)4noEf_Vl7<{6T3o*^ZIv|tRcq4nHUEh?{4?sO}YGypP~ zaS6XK%xuHSz_g2QLA6!NmW}~QcTS23t_3sDu9{uX8@p@%{cVvFdS$q+X}p&6%q_w5 zqjCQrMl$oVA&04)$$@{v{(*RX_PiVc^VF^?m31_X!cWgs&Kx~xn{-~wlIDIxu7zzF zvRoPaAbT|)qoygi%$%lRL77YN8h1(!>&pv;+_*5KGY+FXx_8~f2 zk_P79s$}?Y!JeXX1_}4#T5vkFi}ra!4xt5GHH%V54zh3wUguA*?-LcDs%Yy(<;1hU zL#C|Oc7VFyoFl+Dbb&W%o!$>~p8XuYMJuEJZtt7?w`>$e!)-!@qNrSC12X8q|qmOShxiCzPe^E(lZF)d*E0?Z;AbV zMk(ihJ|2A6GD0XLC7N~_L*zh>jQ^m0T&XsxI^LHgDG`@e>-&-=X@=igrEmY(;GbF! z!9DISl5m9hRH37;D`Q7ANt!#Shk)Q(h^|tZ>gsAW{i#k=(VvRm7R$@fT7PpyxQ?u} z=(XFk;3(zyV#k9^z?*XkM_{clUDDgwM?bM|keaJ?yshf|>H>mm;kSYKL}RD7QslC7 zV~Rb=aG5n~z`M`5vT&bwxD-C!0eKgx*6l z*!z?W(d>h25$h@;kDjSceDk&Y zj^J9z3?;4-;>G6#rFyZ}HE#YH4Wn0&1pD@t1;%Nd!S4j~?H|Ue_R5!S_R8(-T*47} zjTtNSyn|oYWeYuUHPlq^tN2u03z=bzA0Z01*{X!~i%Tp!agc>euy3af8@z1TA5~(b zjD2F#iotL$vZt;%0WXV28?*}^X7ue*RaYsmdnW|@ z!I(jB?Ru||ZHd@`mkqf;Yo7?M7?(NpF~q%c1lPhcFei)NC&u;Lti4Bu>w{D-Nr~`0 zRrWQ1g!a17(w_0TSi3O$1iiD~`gjY4`KudqmELpKB-+eTLfhqt+*`a{C4ulD_awBd z8h#IyqnkRLf(J(k2(E?xzh~EP{QDKkKMM|q+)6)GLs0BFwtR{a$9L*9YrK}T3dV_s zeIyz7L0Z__SJ#f#a({{lw2wcSebBu)0$V<2gwpM!{%5l-hS>)pxE5ML+yX+NebfN^ zxMA1_NwDo6KQ>*<-6cNN39yeOu#euAbo-z(I0COR^OtTP8^fQNT8D(1vV7COAV-iE zTEWakGY!P`c3YJ^>*5mE$Lsb%5^TGa!AyLrl=eYoa0Fh%xm8-mG{{rb!=;qy8|^Vsgy39m`_TJ#v=3r6@u`~kniOh@%ozINKe7)U!L_gq z%xx*!M@GXwNRkrad8$^Y{s_~TWzBOz@K{-C{xXdkqz5#Q<5d&;r3 z80E09>WTS+&1`w?!^(<^3k`_?IZBB%@+06WLsyn zL_%;av=5jwGY~IR@CQk-g-6ZP^G1nJh4!%ro!rRE+yVRE-A?14|*!DWwc#m zyxXkmByHS&adM3sXcII_#cRrQhAiP3A*Zw`l5hlnS~%Id=cLdeu&9rQMG=B)8CGjN z6)_H36y||MTT6)mOEVBYW15EUh-zw&c&l3!y*b(kWqw18qBaeuPAAV*#y?q;m@N51u?U<_fqFFbE%1S)oge<_4>p*_IAFqAmIqK6;C!@-mb9y+-#y}{Ki)Ke2l(B zMSQ9$&XvlaaeoCbyfXwMggnp75!h?fP6A0Pzi*}T?!aHcA8(5Yu7!Q@)Ud8b4CKN5 z3zUtID({*5K%Xfh3HG~%+DACCcjje7M5F7dsk5!2sW0@@T*499Q?Fm9qb2EP?kUQ% zyS?_*ULP$WxEA)ijjL0p+qp-^sSU3`vIkszWakpR_HEnSiM=x~8}b3Xe-b*sQK8Vo zkk^1qI0Ad>%|G?g&=8B$X@_#COCDSQcLN0k*AiRe&!1N*pX`G}-fS6S;olZp;-;VU z9M57a+1lg}`;*9)q4{Bi%q1Lwt&%cCOVZB8^ObBtuS0Tt)^h2&eP^zP?{|Atyi;$x zDccpro?LqJebT6)j-~XRC`<;4j!#N|y zsZ9nvu@~vt*TE%tjTyhGUD2K39Fq~QP}|-Dp+{jgoIe#uV2goFp+>vvJ7R}&`E(xJ zuvdD!qI+>IY*&;6U6OA9x=8c9exET+RMtM*=$FYEq+aG4RZAr?1LkaKde>h^oaj8M@CwR@SHc%GBQCKUS^kRw-T-O z?IgjTI%L#=gv!*c} z8Kd_lB*C7+G0c7bhui)oY0>;jj&FURgnn9|x-TIF_7Ze@2+YZJT&OI|Jkey|ow_d} zEo@h>&zv{QqsI8@=c8cHz@i(qr_$G*->;vpAx4#o*2Y)ZN@$gY z5Vm zl`kpB($U(wGf6lC_D8{rBJGdzdK#cyJ+(NgK+G5c!L@wKXMq;ua2}4vOjDDh>nYh@ zJx+WMaR9t!pcT=69I=&r=rKq+kW@6d6Re+e2}g+UVehg^Ir4gM((6M*1O(SASTQs7 z68gR}NqQO6O=;X=Sq=H0krpoD2=P4<;$GUX8YKQU2W)@*Kt` z7y&`|;#wHBKzl(X=}ePp!CnFSo(3-A2>wnm%Gg{_*<7W5NKod{mgC2~UAX_lh$My? zdYcwcEw;p`U{N@0@KoQy<8Gr6Pc0xkdwwW#AkK&!pjM6dB`wiD4(7g_cq*5kMUX!g ze-e!S!J5XWm84cxzd4Q#kV1Zjr{WThz_=XTfuY9~&2RM1ak^yAkXHU8f@?MK&JJTn z+Lh9%>}^M*6H9_i4~&6qBOVDyU|bHCp~w4RJ#{xCYdC^yVN{Q&jP%VC98Crm4_R-- z`;deqFfIpo#OU!pe?q*^5}%*k@jis$TKxOtGr>OhhMssIYSkQpkvyI<{2<<^!KCLQ z_lI_f(8q5ud8`oMHI0Yfhf6pDt;8p#jB(M<=wO; zNu^?Sjv@rt!oEErPdkI7CId&M1&(@Va1^y4WE{#&Ahr_ZsQrwisQqw+_#X9_tx}Gc z+?VuO(K(6`Tnm|qGJ3#iP{2`Tr+PSwBpe~WM?Z+OY|P>;Y2P;X=a?ISIBNZ}mIg;1 zHDd0FqiE+nG7j#kCyt_h`;z49mXP=di@75SM_^y#DFfO`>?;;?M+mNkOhms+(rSpg zn+q{_FQ8p<2}kgElB5#AQR^8;;jwrJ zx{dCIXwu?VpxZ~9+2w2p;XRO}=&5*l$VBuz>^?JpbCl^Mg=8~0iX-Vt7p}Td6sUBpiY4<2usK;^8Rtqllvj!L_jWf!t(9=9W*N>M0lcO$cska1>=+ zL&kxeR~F)^&>Kxu-H+N195oa;Dx1MkB;g2TAIbuknxhE8wUDFINLGk=L|^T>!BIHs z;}T??N9G6(h=rIk%S}Ax+qT>WUWi#M^Wt}<4}3n2mke|xkcxwLEuNdW_}b&I0D&+@)5%9 z2l!Ff!H>#s_)&!5TG+QQUD8g~{iu{2MePR}hce@dtt2H!QTyQt@jV>iM_pik6d|}4 zauj8F)cvR?;73^uKZ+zAA->0y6n+%t*uwrCv$7FKr7KrgMfUN;g%d|n&NE~j%vweq zMfuj?v^4OemN7qyBpiV~hNlekfl47$nIA<6u7ylQS=k`Aw)wPRALd7qgd_Mnc{nQ2 z@S`vj8*x;Zr1~oEN)TJ(6N96k7#u}y6gi3`;++i)R!iwe5d!Z^TB3cJ^WRN8#{4LH zD*hzMQCQPI_M=F`5y&O=ZiQHMj#`kyk0J!uLXPs}hYK~`cI>LTH24|po8S_TKrX@7 z8{{8h406rD&=HL>J zK=$#JkqbDg9`mE9jdCsID6*T>eiW_!BIB@(&1~H{&nW+Q6xde!3^WXQFJHBo&+3KAbhB; zrNL1o;Rxgs$|)^LDsa?v=0_2NYoQHNrt8#x6iGM&t;!qd^2}fX$;VC1fA4LeRg&ak{ zr}m>r!V&zPV86kndP?qudLe~igp3{wK09$#t_e3`jzQ-rywi{UD3TyYaRk=LkNqe@ zU<)U$)b=5PABFh<_)~G4K#roPm87cRM+GoHiXkKc=Cm(^rJ|^ z5y&N2hKHm0TpuC07IGB*p4yKh2}dB8V6O5Xa}*)C7XSW`vnHxQ;&054q87&y$UbNV zdKsrr)l&w)c^=Xna#`?uaV_L1vYXT#MG>*cIM6>?X;qHm#Z&uHB;g2TpOKFggQI4w z7@#z??+o5y%=Hn1Yhmw`Km8=D&Qa(`U1WX~>|)iR^1#$cS^k;MG}rcE}`?P zQm;o5f@`4-dd|U0xgJFljzH_8Glo+8QB=FgI8+|?!HI<(21lh_k0J?2Ap6kCV3L$_ zJ&F)q3;Xt_GbUSgj%qanzPb5v;?XSndK9%EWE?u9N^B*_QTt%7ucNUZMeTXoq`MF_5i97SiXK~xxU6#7xL9z_z45Z_}ha8&F2Gu`V^baoB)=g`|Qj+&xz zS86|s&MQL3!SjQNqv+%#*sm5^E%En@p7kh_aD;CERK~nHl|qhB@~lS@f@>j1(TPeh zHCS|7@QtUNwKXp;;Ryatsn?_Mv?SuF8dIiNiK9F%G39y`NsyyB!gCH5`BAi!l@Qp% zNlUbkl*nQ^SuU2}dB8VC&I2YRSE?HF8eYSAO`txE69$iL36@pumqh$M&C-gd>nk@RTT> zKi0!s-#E7aoDf`#e}8?1jLeCc|&Cs znA}du_~up(6XaFHoDB3H{MnI3@pnB==GQi|a^KR{7B@wYdLao%V2_%R=dy8DW1B-| z6_dIGX702sY3_S*E$pu#=b6#(uF2h9X>qh+(%)M~LQW@-1pC3~V=h{8tS&xP`$8*~ z;GE@5(ckpFWb{-Vfju=u@)&&n;==$f-lxZNJtBn=Tno#fz0Z2P(*KXu<{1{&x@yI& z3XpXQEHy(IUSn&g-$ewMq(l%}lHNU>uC0deFtRPsy%0@W*ruygzH0Q;re6{rxdJ+d z)Oi!5wRVzl1h(BSNg2X)|HF8$Sm;j$1lPK|)(7645EPw2*%soRS2WJRzCBx7{YfyQ z0NWM%JE@}+NWu}~d%#o0zGN#ugy35GtQwUOpEaLj;vFgJ>=}LK=SK+fJu1S=PyDNT zAvCM@*Q+aTHmG;&ikl7aPCxcoNP=v@5m+NSg4kfZ5uHE?v^&z`wh5;}9ln$J&y}*; zX;A2~&{J_sMK-{i*4bdq$QO=m2`hv9%6hB;NjPHeGjH$*GeG83jAz);;hkgN&+CGJ zGT49+Tnl?}YHx53R>5zM1H03NEC9yg5{|%L8{3u62Bu;Oj%|M=Ci)BoUw^wNHJ=L&NpxFjV4(e8X?8UizzaRgqImKYBQ8O+=W zE=h?%w7&nmu(Vx6)UPh?mPIsaVI-($r}wJxypC1ji|lz0>ASW_!VyM%s#T9kotSRC zmIX?AkF}d8taWzZuz5k)mKn$D}lpAsqXAVOjIe+>BVfg0Q8@xV~8}e>t3DYy_Y%hP;=6_|bV|S}@f`lWm zY)=_0+7vO(JNuLJ=ch3Of@|eZpC_!?HhQYO?{b;4tngJFkz?UWJQ9xJ?<7h0yw8R% z&D6qv^6og{J$&2d4y)NU^*c$@f!Gc9JZ=7Tr12Riye&s0jMT$#4YMI=a%=-NqpAN1i>Q_nO~S7I^o7 zJq>x_JRhI1b<6faUZetH;1j@UP5WWy5v>3-{o_vW<&9^B_APfpd0(%OAmIqnqVfzJ z;P~rl3uRimQK=AQcZ&z)0o(9_jDq{24C&|D_P8S(l&Nb6TeyVZ7p<8rSdxO89kCAz zZm6U?Hwson^1wM!-eJif6|KCWOx|HQQ^0Lgl0F^SZ|^AmqMWWYT9Dw|5(3K(y`kGb z>_6Wz%id?pEK>v6ea7#_wRn#qNxPf(vrWH0UGXavE!6JI^?4wNC7z78SEy-8nl@vM zZ3n~&r-68CUZWg=*BM%8hrELrPhECOCdb##kxG|#BU2$ri$5*Qc)cs{NXR@`**0f{ z@E-iWc#WB%^_lH_@pgOJadi|qmsnR^i`P2L2&a2%3oofC-?#LK6O0Ok{qrU_^l(=& zf->yk=iD&r)333YTMB2UO45olvuxMLW>lA-X{TwCB(bn>*q4pD!*C9q-w87NwR>SZ zIpC?1CdaRWgd^4r@`ZZJ9rm=@B`D*kb_;BU&q-?b8l40L*Q(YzXBhS^t7=~W#Py67 zOyPEK^~J?junOjpsDI=H&hQI^ajtMD*vDCGp8X$NvXcE(4?)5aMJDEg{>l$BXP<>K z?se~CE*(}%ol&-?fZ$qj3v$6b_=VxjI_!4(w9UTi#uX()r=Aur!QQSzL@sEvCxtsf zoapEEvhzT9wd2S2GOtCBh?=LPcm947%9!^l!d#_Q4)yTs=EA+W7TyVa?t=|40-||? z9roD;4=F?D_JuF#xmV4uxuGx58D^`0OuLf@k?s7ZQ0JYyis^ZTAmIqSKHE5_b`CJ) zHJE(CZg=fh&SvhH3PD=Dmw+gX!n^DrUn|PS=>D30P>;j!i){C}lrNl2f+K*4w`=U} z-mFm)8x9c=T#Jt-pl5jZ#?~TDcGdo^rO?88O=HU+?UzTh0@y`2@wu(jg)Hi=oNY7& zNjSo%e1S0R#drSx0-kEt+?OU(fUjC8xw(MgT3E)Xm;!L>@oPYwT;*rW7F0nkwAiE} zNWu|#eK?vBVa;AA)&7=EJ(IRoDgv{8 z0N%VXoDb<`m83S^wwS(e3sSQ-tu7$A7M9VoM`1XnA`>9+-|~&WsQd1e&=4fy2)w4> zbwthdRn^rQs-{Aa7MAhz{bs#f{=t;{7Hf!Bx_Y(~VLLm6!E z?$uR`JS(D=LHFWXc&FKWi^0eb5i6V9ZHJphsKGNHXl0OuBk)U}d|v`islaTyG1?OTYkPFAh;Hm@oNtMunIdcn=bxawPT=q?eIpO46W;gKe#Sqtr_2=4%L&@cUw0Te_qquxL#{ENhr0#BWoS`s(d00l~HO zS}zIQiwOL;xWfqbajOv;;^<0U!V!2)zw3y#?MJA-k*N`+g=JLOQ8H}&f?8H+w{M!- z?q`Wn+iw42<`RD2=6i6|Ukdz^wt&EYT|;A3CH+eQ!L{&C9z^N!+s*%t{j40m)yYya zV>XRn`#-pfSv|EL2mL|tKeg-FKFMIUmloWXT`T`|oSGW}?Ml+dgNE0rU84`!?Vc2d zCx{%5z7B|s`ESU%$L)6XZWJjx1&9_qF3EwD;*|FPbQTa?>%Xt* z!Tx;1U`_xK=^tE?7j&GUWPR94Kya-u8`Ee$-0~L>0MYFG3As>(vr2_8O)Xr)5%q4R zg%*+rIO;thcBseXd(r2VrPZ3jsnQ+^N0hjlL945c2c7`pT$&p)e3ICUgL()Eu7!8P z9$YVDRnQ)}X_Fl4h}N=(APGl6U#Rt>Tk58H4=4_ZLe-kc?dnCU2ag?*xr8ICRL%;gF8GIi zXn`4&4|k~`f63EBoqhI%%q1MLzo@s?`!w(l0z{ETFU&dPqt$Q6M+gY6wXjxJ4WVag z-VyNGe0gS+YU?##<`Rx5aWxx^WJ`i)TpP+T^_*_*JvU0d+HAVaB^&|sK3ZSm6^DrQ z7uTDs{yADrf3dWH;98jFAA6tt>FWTZM&&04-gWLx=sT7P8 znp=}UW`~nDO2QmRV?b1tGnuPD&!*nx7pC23o_eCFaA6y?yQ z{u)Qo=sqQa#`7?4AGh9o`joFawsSL~U*i4M%_iA2`-ri;1sn7$IoCY3Oip!4*JeVm zcHKXF*ruQIXfs|SV$!0l^5Wb+YWs+07B1lk^o|hSA>t~O(Z5eO^NN1C)Y+$+3J9)+ z{Vt87B<7zbEa4jr@zLO-G$E7n*JNivof3K5P21z&~Zh;T95E_ey^ebRK zx-7REF}{g_;9C4^0vp^M4$lUAd2f$XaHeK{K0g@YU$Nlz{-XNz%_f<(r^1ujVa_sL zYq{{#XNsdvN2pzogd?`r%?v(*pD{xIx9Bw){*+QX8$cd&+8bLRr7xFzG7V!@u??!_kEE?N&dF_+in{<&pPXxpcAPEm zj=Q5|+uBZ$aK!X}USU4va~eM9ru$#yLrXR&P11%72(I;f44z;>h$(AU$|d_>Q~q|e}C~kHcq%950|DZCHr*{5L^o~ zI)Hs-gV|Ip2V@MZ^S2Mo-kyP{N?U%j{9xJd%KQx?f@?M3lQHbK zeL2El_ZuLBmu{2)m^nw;AVpfZ1W#GOna%&co)gNJr2J6}<)_25s9Bx$1qnygyOmKJ zQ^#1&Lm96NZ;r?Xw&wtBZ?-P?Wwf@|?_1A}RoO|H@>$?-UY*mgMrP7w&hQ^y)r zKMZ9w$*@yC)v~B^dw4&g?Q$({H{e6%X(vCB$2*p+8;Psl+I!%9E8Y`ZJjNYBgcyt} zGitNLaJpmn7V^v|!^{iQj}j!K{j!ANsT6eDCZhFK{ZiLP%XeDGIULJ|3uSNwo>6wX zxR)`L8qh9{WpshcYPD8r1O(TDoDX|9l9q%resZ=+#(Ot$RWALwB3$gBl-vkuGQ^crZ71F1Lk;f4%zQ&tX%%k5&PNA!-2^>5{SQodACeq zm|KsR4SmVa3G#-3hxW#sq6JAx#O+a;!XQ@}lo4@hrTk#@UAujsmMPu+hIn7nf^|IY zcYS?uLb2cF-x}q!N39x)d05;MjyU<=3sw^|fwzs>Lf5UhF6TWyP`SFKvw+}QyiG#{ zUC2&3sqr6W-ZqH69NV5L}DfiX?rQ|BJcn ztFh`U#|zD)C(oUa!f_1enKejvE_3LDVKHixk2i!l0FL0}dD!26ZgJ>>CL>g-y*@KR z_u^Vu2F+4P(ip!?=6SX#wcwPQ!t4h}49w}H`8dfRaZcv(#T@4NYCY6Hs@;%r#EM>A zhlqk!yf4k-h=|Dt3z|oqj8H!xy(@DGN8mNh?qIg*e?xOGjZ%9&nUxAbTH<@GT~XM4 zC`%LdKxiIV=gg-0oL!PKYfnYTl}_k**i+%J)&Ls{#a<33#PC&vTThnG_$NQ>79oMdJVFqgbmMV)-TN~*dd1eQUwpK!L5+1tGF z?{M|cy{T)Lv=Bk{29dJX3bKDf2K8>n*1`yxk7UKMXngtrIewb2I<0fKFtQX8+#j^w zFD38o?yDY{A|iN=;%Smt)73hE0;@fL&R6brBT(%#yb9z)_DDEFtgGujIpyQ`0@We| zLOCi$W`p~SR)R7f zwQg&ku_H=-SYw9FB^<$f2}$y)+Rf~MzNEElx`5zX5EZEPaJNVO0%iPTSs(g%S&Vw$ z=d_tiI6|D62%1^Te4$&6+A!NG0l~F4{ggedOHxS~r`Clst|ZKp3wC_0Sev)eJX7*g zdFvDXj|npp)X1-ldKyWQS!zM|NRXyY$uL9NR zgB2hSz%vWT5xn(4exH~9J%TP-WKNYTyJd_g&=kz#& zw;ssRnB^C__Pua*`#cfBwfKA)WR*YdE5{e>q52g#Cvyo$@YVxs+t%#z){l|u+pgyY z1lQuTUoe+3a<_Sk5~XG+m>_crNAT7I>zsg?28bsGLAXDP!Ta~*nJ>;N_x^5R;S!GEv+6p+4v6Fw2(H!Y&UtIO$$4Nl66ao}mM7)M z*>b28VroE6Wsig-FiKX=QV_=NWDRyd!aAAg?&&rF4cQ6y&z?DONn!5&M zv+~@FBlxU3%q?x(E(g2|QqA5)1q9dPv+59Q@N%>KXo0NOE|E)^*@g@^R-f|4p`U68 zal4**czA=cv*iJ2VfK7bdSSkuBmVoE4Ch)FhFQo4@aE<_+vV~LgVbidiwX#?#oteo z*8etJ-h44!z0ozj@Kk&zotFpu=y$Y|W12^*S)PMe!JeHX_^kT>kKkHAPrh$mx6B`A z@%q4f1e{7E&zv5m{xdL6<`Ry8)fnq_|5C7OIR+4quDmoKn;5Njju2<)`P{pRXdInJ zUNJvPeOE6|<`RzJ^YE~`v?UmE`~bX^8GrK0FYGneq5ftM{w z?MerjceamF7v#8P<`RzJ3k}%gUL{ z*N#zLYp3jx8kaMVYh3`C?XyewZ|x6v_vZx0JUhn4p@ zyeESAl5{iWQ?)lcUVR^?oqkH^CGrzi!@D1{LUb-fK;T)8uVz0q`wZITxCp1EK}@#$ z3{W`Z(tYCTs@jV6_9*W#uehp!=s942NQICXwaWaL=2f--21IFCyq zy0hE5ct9zb51$E$InU0AxPoI;=hl~IF2S=*34zy$8107$@0a6EMShM_k2jqsr$ms} znnAm*m9F{2tlk7blq)^Q^dKNg?Grgq<`RBiyhg;`wTN&8-ZV9A7^!aAa3>XlwAy6H z_b3jY@K8Xs$oAfp_LoR?(!M(~m+<@QL2=h{&2O)xxAK?2)r+9W!|*Q zTBULknEmJuh&cnRhgQrQuFkxl7xtHXB>cX3jR>Feh$vCLQ)uAPAazK^>ZuT<729W* z^E4E@a(q*gaqw{Qu+FJ2=8SFxeJJ_992E`cyM? z?muUgt_^!zxP;#quQ5`y!VW~ND>*H+MuODvV5`JI2MnvgAM1(&(8X9le|)dSQH&8s28M%n@Ac=g9}G-_H4mogWbgWi)7=*4(vtl-hjDTwolJ zgd@0Z!ij}{wJ`sCDMEep`i_9$TE9%&2d94(hqdj2P{x`H4a{>ZMXHN--j=z9Bes|yIO^|+J^cIg%b{%8VaY+Sg~yec6`E!m-(g-bYs+a}mxf@;p6=%fBU zwz+`dTA?>oYx6yM;G1Kd%zrBmnnzyFuI_%{+`=Us!EF=vl8w4yo;v!h;vd~pKyWSG zc{+P9#+QUZ8AW?vH|I_~s|5AyY2gx%;I=7&**-UaJr}E_e-|YnxR%H`rPDt#+y9JH zk{?7_xP&9PZNhsreP_2AoiJg=A!G?zh0Gsb)nf-ZjZRj2KoGO^dzV&p8bnT_S%+&fmv98PG)b!axQINXd$_thZ%)m} zp?h(ysbQyJG*ATiqcxOK_)8J_aF=kkQGuK=BkYlI1h-8|+P9>RJaS2(x^#A+fZ$qQ zamT^V3qlNJJt$+mQb#^IH&ER(KhVM@9Kmf+lZKvF9D}pmJ)QwW} z#!MDGFOJ~02@zZMtIK&aMXKHApB4~Yi~EU^lsK@ud@Wt1I&ArA!Gq)oZkv*nZsiF1 zS&wivF<%zQ;OFs|xfb_vA*N_gv|MUaxccltW?1L+_{bc=Z4*}YmoJtZ`39-$+m;p( zTubzwTly}M8^XD)PkWXY<^VW?+a`EL6L-kYF+OVf##RBrwfGzdoT__di#+W~4s}{? zt1$b)5!^N*d(wb^<*ZB3D0%L+5D;7oYk_8sBq{gGf90ToXOuK|TL`l_9Kmf9`rU4) zW&ey5l#S-D0)lIa-rcz3XJr4@;}yAVR|}VL1h-9yZkqtnpP4s17RL4v5L}CUmXfqT z_Pl)k+BV0A6+H!?lOwn{2^mlFEpyZvyVG&EP7HdGPF!#P?`swG2c0xx#cLY-!k8iL zU}e+FEsn02hohZ4sRw}UBN}?$vMSb%2W*hp*c(=6aJ*Zj^HHVtg})q=|Lbk}y790R zSB~EW{A}$#XTK9pcYrZfMJt|2GOmwz*w9{qfCy+B>JF(!j2h*4!^|Zd0ey+nGhbp4m6Tz?wcUrr$sED8 z?w`8tytLXM*0~2j8FMy%b&OAlQlB>%FLMb;KwskY%;Ho{ZlaU|pR@MvBLaeJLEbH= zwQBLOwSziA8J5tdN>u7-pD%;62mYR>{IsEo+PH6ei2d-~i)-O| z?%C2734478BN_*8h*uI4i(`gQjB2^}%3R{=Ww6vzFuOGu=3qSb*t5q4 zCytOMDFF~!^Lsn;m5))M?|o$E5{}@OCP^cD9C6%U9;HqyH5t~=J@?{T19P5r{va;6=8*s-;5lxo_V0IT612}f|-ggJmCrIZ=Ik!s}&=L7`Tf=DfAhSq3Ny`hZs zQ%fnG|BFx?!>SCIa0ItB@D28MQvzm%tG_q$2G7fLFRlggW6+}(fmkx!(-7I|S7oBy zM6FUJE6goNM+!vr6THFo%qD;aA&EQ|kH!sxfjo zh@kUGID*?ItYD>Ir7Ruhqpp5cOF(cfIN{t`Y+D|PJhMOzuI7<& z1h-8|njgPQDHe84G3{?HAh=fY#{+OKi!ZF>Ab(6hw^OO!{;ZOwL>q`g@<=#>+a@d< zx9n#U=peu@wfTuWpe|8GuZb&WXXtAB)rOE`ktCbYzzhm?c?+Z^Rb^$`$Ui?ba> zH0C^{Y`(U|VOrTo;6aYywh1#{6L;CW!MsoRI#11TsxkDIr6B@r5oEOS*yH?s`8(;C#8!~vo!c@<`RzJmIfJ`GDSJY!)$G0{@F4|aII>c_d0Q=Xs~%a zl(FaRXh+>iQRS#LYXdM`-UM_ZBKyWRH)PnaY4!aHqLm5X$A9K7r z8=>Yqazo}4j^LICaZAUuC^hysR@XJ~vv360!n0U#rU=dlgfcS!=dI+A3|GrE%w^#c zj^MT_NlW9(D}OW!Qj5n02?(w=+D~yVtW^+Z(wjmVkz;;V#(oP_FI^9Uos%93M{wJO zh{g@|lsaW{sI}r73ka^&CcDEK@5}=pBt{NQ-}Q@POXH(fniUQ`gGa&<+%};MYkQ@i z_gN)=Uv~k)wb}*Soj6|73zu*Nw@nyX9_g!eZ82VnA2LWl za4m71nB$M0N-MuuWrQ@?!X+HRZ47>X5HclJ3CxJa4kMYl_dF>K1$7@8yv@) zjSxn!9Kpw>5X(9Jj4c*Mmb16K1TS?vjB)f;5^j(9JFRR$B%tG%Z3?VMWg7EJLy&|c zxTV2*?eVqt4L?Px**edY`MtOn{!S|!l9VgMT6_8YQEKsM9YGR~;I=7Ar<-STlr0&l z-dcM{D}xYROD~(&KYT}dIc^k)RGXgG5wzyP5!})wDNA;fW7x&UYF}qwSk=cm(eK5z z@ON6-0FORH4ad&E8mp1tbp%N`g4-t4?#%9vArKYT>r*wY3_@@%EQ3}yB&l?CM@ROb zg4BOHRo4(C;RtS zAZVWBX2ml~35aj!_u^XkJFRR0bB>wk7+>>@61YuAkc1<+Z9?uH*K)`Hq*x{0=V+}A zLU1iC!{gm0H(Kk605A3IP8~rKj^MTlK?c8Xa;zz~&XEmr2=RMyE$&&ucsIvU$NMy! z9PQ3&v$bxYlOwn{35a>$l)rvm@A$U}?%;>k7Qw;Gw8lD|L-%(ht~}t zuFvBGKwO3kGv45CNj|>TVm}m${ooRgfG84|X9wtqcmFBrA}1=3H?a zzwRlGVV3jJLGgQVEnd@F%!EQw7To)VC<_Z!RU!#0Yl-1+Yatq&sBOr#w<=JIgd%|{Q<=Y^2P=4_X zaxMOCBTgR(u(dwkj(KrL?o`K*9 zh#YZwc7Q?*%MWXpYw>!6u}}O%J4JFpEC%H3(xVC?ssp}{|vB1K^lDQ*PU;_sxzWjI(|hI@7hVlkkX(07nSjE0N$cEVFtt>mx)J1;MJ z3um3(2(Bf*N3rbX9cv+4q^ju}&N{oFiX$L$1TtXKo>7Qlaj+N`H-c;NI)pP{n%W$D ze;cj7ycQyC5{OaKCP>(||0IRfGqwcp)4iygGH zm?O9rZMhM4y3s~B;3GoqLS_nuGE?w-aV>Go5b^zpQtSI@$J()dAf_l2oWY^*SL$oe z1Ses7X<4#h$B*b>G9C%$J*Qo5yln7$Vqn~TL=-ltYwRi?xSnaF1MM>Hq~2}+Rj%~j?+9tz7rvmU#c>4Myk}RVbLbr38JIRZBmM!?&m1DTH8ye)h^lrN5H-^ zuyfjdcIx~M~gx>Iilc6#} zZixS5>pH-rsJiyjLJz3~sY*wbCP>Nd+$=>vAdmz?LhnscTIfkAqJSbulPbL=ARmP&}M!0!9v*cSTxT`>qdKqEI`N{Y}gT)WZC#T*?Xj_Z-W@MDBvdXyLQ#W?4 z?Id97_t_D$k-uP&*065r$||&#yqvxaxLEfF`wh#`)_&7 zf}`G|cpR_}TbUvdm+D;e84fo6qBD)(%icG7W|xuo&MeFkh^hAlBJW>(yufH0bJNJz zLq%|{{jcW%FIl+ED2Vu|-Rq5N8Rv`vW7`TYL2dw_oi#wcs|Gvv8-?CEY=rh{Loq+j zU2(*-1nTo)$azMY@otycV1#{t#?W@Ccg3}MdqW)k_sfkHzU{__EnSp$#Sw_%mMZ`& z(tBt7nZ}hm`OKNI?<)wd#rq8y5gIfwauog3NL$~Y^03I~p!`tCMuD8vk+6kc#Ee=1 zD}6gN#j1(rjYbFZnWcjh6bWTxw=+c`R!Wv+<^9;ODbjelCDJ_6u({0gZfjvI_x1hC zDpy5JTu{Ntc`v_NIwqc?0iCiq;`?U#eP@PScZgg_d0QG!FaK_gx+(L1I0&wVtQc6~ z*jYS2ZuX|3&2^b2>n2ma3`c?(EaX$C%!L<~x)EPnevt9~{a=g<_0>9FMg;PgyuM?Q zG3LfE#z*y31U-|j#d`t9t6Fa|0PM3^hByf{v| z9N{}0K%?zm+pQlu6KeimT;|?!5L}D5H)Ij~XTM%)>{BCmMkl2uas)D-_$d1cwMwk1 zYX$AZZ`WIkMp2FwdGFMAxfXB7E?1R(-Skh>!_3_&>Rr*^*^bUt-&KpP+4OmZL(Q>! z-jBY0j=;JTnG!CBb@e0D$qRi~H(zkM+)&3$>?{dGOdHix~nN{AF z@N(&zo}6-~;(Of4srMTfY0g~M@)d-wg={?%K{Zkub-wH>#uA`ly{ zv)FK_ERLXSJ5F52pX+w~IWLQAsbdLZ+;tY??u-l^LDzPKJWk?1>8smu^t>#tMb~x@ zmQlY3i~9Y4R~CF;0Y`4cB*WV<#Po(VQAT|}w{v>{PE4MSH-?N4H6P_v5nPLAhCuvz zt{-|D2bR__|0z?2;?ErkM{v6aMM3m8re3dZR;sHaxEA7h<#)2;&wsgD-&l7&)?BeE zn4-xY2}f{?j>~nVW43xnrG*;97|1m3`BSKVS31c0I@T6!UD-M2aSNBpkslZ14j(x<5YFU#A~Y?GlPxE3GzT(18*eyg`C-OKzf-zCaM;*6pk!CMd3i8u)_ zSErY`=D|e;!L|69k2)fg^h>)^%!$EMC>x10e{ckE3-I#Z)>Xf~F~u}OR0P-Jvz^OT z^uU(P7k!WkVc1p5(df*a9HF+W%?-xtGoPoLDXT9l2(CqAqCIP46m8UB|KDldnDBmo zc+mJwYD&;Z`1cpPgwR>l5c1dA5D2BGk{DQ8#X=8^6rz z57V)-3`jTvIR~Ykkr+5&TN<|~V}1Mc#^W4)6a?2o=47^#`K!$ro{698#)&s&R(VIl z5!3cY;`u_oWM_hJZI8D-$I2Ws%2)nCa0#-1xSpq)V6N#fy>6y9WtxDb5|UJeDS_Oj`)xX=c0d0xNH1cQs!275L^p9 z6kwf>@aH4@bL7G3g>CN{=Pz|9h6+c5-0j$BS{d=xyP{U&33_3?)~J*6t~dgj{(L{T zv~$GI?>{5D-paeiv3%-XaV_|x`f`trL{uZ??5}p*%=n^ZCFAVy(eTnM493#}-k4ej zF&Fr3ZU*?g;>jrkBiSrVC0>=BFU#(v!|E82a0Ff3%r5j4Sj{9=G+KQ!`W1w&^=_xa zFClJ&qw@psb8-nU7hEaQ;z^mt5N~j*T=Xwp6OBO$BbDd)^6SFVCPQo${0?Dt?)@;j z%7;CTN~0yOjPtY{0pCRM6%~XZGG+dHQ1XU5?)iA5s2HY{#kHt!+PxWL;)0Hzf8OhF ztXVN!_CZ_15r`0y)+mc_;{M%r?(`og!$(mvR5)dEExN-WOBX`^U9u;ZJ6k6@@zZ&R zUTBEm67Xi=jas}S>RrW-$`^e%WxjFZ-eBdfIAUp|!oJOeLZnsI`hs)rEiueXGH1VY zS6qvaL>9vO-!TH5DAEFH@$Q65>p5K``@Zvc6~QI&GjR~x4~Bs+)#hWF@F)PtE!MYIRuNna z79-5_d9ioiL#_S#C{O-Yd5woPQU#Y_c7y$jvhHOlZyoFQoO|30wr|t>{g$RkI0Ew_ z?0z=yQpXBA+;45!s{j3mir`wj&m$@&d(AyzLB)+j*;8d7w8syQh^bW=-#owMJ4CGh zd3W@VsX<2n?^OiXQpb`7VfVK0*qOszJia}iJTO}Fxm_Kpm%Z08de8rE8wEzG<1R<2 zD}c|oG>p#s_f2E@GZn$LsE$)DEUOV1Ttfcb(v))Xd1u@!2wMwG5;*y_87d0zep;_`rBHLk<#;d%1|+;(x(@6I8?ApWs`U!O)&i4++&klZ zJqGaxH;P7ovblyi11uO^!po)Wz;1iwA4Q^1fztxNR}i)qm2~?=N#s1shdSzIe=WK+ z5aV-M2wTF-rEA+BfI0BHjT!okIP=}?;j#{nu(iM>;hQ(tW~fM1znU$A~e+6M{p?$%UUJhAk>!S`|((UMpYg5d;V8P%LUM^kR7B-h_`s!QJnFrua zRes_t2wRIv`r~9dM7UADjHyHCL_ba*ZgxfP9WLSJ(zR_Vbh$bWU6ZkBLYi4_%tgeX z2g>4FZ)~Em#IiHG-?~XP?E-c|7r~ zJ8qxh`TR(lg5X*>m%wTuAI1}!qlRSZ84q53<*A&c-W9(Sj)sqLX0qqEmQOt)`>fg7 zuAL+1&5iV#i|jgnA8@MJd1Iof^mQM&T9=a)rbt^WT# z`I=|1yWcoH$>Z&#*1-|9;=tK0Mh9ADbm}`c<6`CQ(W(A*iG5dGYwJSVvDk=0zr{uO zHmZ3R?@dRXS3vT{rZC^l71lRU-<8!)=zqt^`P{C9BMuD>12?(-1xc0kUq)~(S`FGI zA!1~T?(RLVl@|KdNI|ROLqmgo#H34mkyKF-r>@qz9lpb8@wETQerZL$Cc9^Aw^v!~ z-Q!OS`Di6f>jbb*`G`}G)}_sBhhlsu&w>39^SB=tIHr|K>ZeFJf>y|gP{6K?_Rb|r z&T!9YJzP83YPeie+jY?TI{&#szK_Gy`jET5^i+4tT_d#6XvzBK+zCg}^S!Yt7^l3n zn>bq{yJyqFo?1;X%kewmTJ-n8lRvwXQ@c4SQ=72i1BtLD96{HWKeOJU%T>GbUdi11 z!+GLM4V1;T=-Phkh`al?x4XvTGFp?3)_&0b24FA5YO)|!ev;{r-PPriU%2%lbu{+@ z$zJD3ID+XOXX?i!I6_rhnC5kr3KOblwx{qdI>&Mkm!He|*D3zh!gN5mOzI zAd3oGx}`;{zCa!4-x{>%>^B8P|N7Rd&z5jROsz1?=Y?^{#Hm-Hb0>GUcdu!Of9|Q= z71vt(Fw6(uKD1KGls@-DmwLNrztHv+>m<13SwcA37JMh3o>EG}`Jj88`@)acwP(ZC zIyj<6o(QsW_$Z@2cH1>;W_GKUO{72Xpp?b6lIupGl|7;~54h|u67`dIa9nT1 z`~)Q61;k!095d!or6iZ@SnyoWThY6;tC1fm5{}qiG7@av^ju_Hx^T?_|}JI-2ue%bH_8^eRx@`KHst} z*%FQ@b5YvMe2Cx2U9G%y%Kg#a{aTZJl7-KyoolJ%RsP)5+*M1h(B?L__UHCU#u2nX zr@gdg9UMG$n%lg+Rcp2MV`&4h5nM|hMZv-s4HiD(XW?`D2XC*`@|#?<`v+L~qQSx^ z{49Kqgd=DNaQj42pTokJ0Tw>tXW?@YT#M>J)Q!(!;fn?fpYXHrITDVbYedoc5n$n~ z2NpizXW>&2wieZa^GEo^`C0g)!NMo}EPRfHmrHwtCf7>(aN-mLpQ7~E8RmoPqFtdn zaxAgS;#yQk&*)M7RQ>4cfXdLdJ4kU zqB@=>lp+r|+|`x=Rif+u(_Hvj_#6o@m)7$GJ*9mX3*WA)@%{+48Pz2}jU+9x;FLX{ZmxiqsD> zHmpk#J1iDHr!1~Tbr2%J!bhtyJdTS?IKsXTASSmTE{-M2T=z~{T#HKjJFUFWV&NNk zy;XF9{Aps&D#^m-4X^J54kQvOFN{vbYx25kM^XVNT{Ktj3IH z>Rs_Wq4OOo$?}=-dnX9Wj!#~-xqif4M z;l`n0ebI*IVsN_J2MJ;KJ367GYs+&W$~D0-rrmYlE;>wlE3cfN$44v?g8Wh4jl>k3)9ou zUXz5dJmMdnId9ZzF-CiOQF0tQ2=cA8waBlp^^H)By|f~$GVc;HUyRpEkL;+_&b7!J zzs$u@pT%+52cG>e#3}6xJo~wXBPd27>H@6*o}!K)5k1iHi!<8K{^$W)!V%=#K=Ys# z(*TY`B|^bQa4o8X5LO)nGv3xiL()ZZ&N-CnR<>&5oTKw|YT*>MLy0({$SspIo2s-m5v~@<`VN?VCM%Oek0vX>qf@{&~d;G5QShu%89mp!5iLCO1 zvdVJ_N2q-e40$>j@&qyDaRk?*bJY>|%42q>{G#CI$OJcsAZ`vW;Rxzo_J{#qYSp&f15(9&9u{Q(#3lWLh_U?9wnXq+^^MMQsU3sC9saFB2?$!q39z zl*P69dxK}qrbn3<E(bl@RM&=RE~w9rSH*D${F-5i$CK)^9^Q>FGoW^3$bpoz7Wk z*I4FaUOIjCS#LMrh*n0GKOSizkJ~8{Fyvva9_jz)dFA)8Jbo8Ef6vILKh@R^RO{df ziXJKSc464(=&p`;eZvSUm|x5(m7v@e*COvs7^1xnafW1y5_Bt*B7aisfON;DS z_Vc-1jc%7V!X^ca$vavpb#O$9b_HZlv+Bs!wz^UI*}vNUACnaX*COv7`>liFFYCBI zdG;plgX@!Bcx8Z?QEG#>8VD}o2rz(QSJB^=SdM{cZQ!q9if&M^Pqb;g}$ z=d_HymPb6jMS4Q6#oq`ydSVXVK$DyjQR4rno)d?G3e=SEo`7# z2S<NATW-9N5!0 z8T-RZYg>Q%P#_k^w|qcB_=6vjQ5W`_p?5Fx>@SEolOJ$bSDS1$vfZwv?db9$yu1Sv zzRI9T3qn(*73@CwzBcAo-mIN+^&q}G_&|n8D+k4xWHb#y-#e*%S(j@^likL?HE~+s zlYNxCq8Jmhz2!`_Gw((F8MZysjm9qU{FlIK2O392kX*B;}jHV6g zq9C{yZ=vuce|*wda5Y`49cM*B*y|{cpm#_X&tJ-u73cid2aF;cCuub|Tk$wHf@|?N z;&KHSS#7L%v_z|ap*v2O0`JFM6ogepsI)DqcU5NFS|jP`0&PNr9?Dzf2=MvgJRuaP z<7B^jaBhX6cmGx!Q(oY28szp_@ZP(2S$FY-!@u!^W&W|mR(@tDk2mxstWFS{&`Km;XL7)){r5C3%|fw<6r^bu>p%pZ_~8AI_xq<6Xr++iooQX|`wMKuVgZ{)oLj}fw-cZ@QPzt?Q{Tu55%Yt7Y8OhAR?88)Da$+vjI2lxT~w z@Vn$e#;HB4+<&hdF5^<|Iyj0+wJ37d-ix|i*9Y&= zFNECG9u(@KtZ~30EyrM5@2ImgSf_QcP76QlwA7;25=ZbApvwhbG6TG1f_TYzSzJq< z4ZurgfR{}8dC8pFfFsDyme!>fFBur83@}a!Vw~b-aVb zoiO<54e-$m;-lwfaV>Rsce(v8kTlB$M^S@}Xb6w3m_ku)631b}n8P*Wz!%;)EG9Eq2fcTWP;E<_!w`A@d#n(3auZvcgWy`~+pYcmIHS`y zv0`<@JTk`Je(xN?ZCOr~khl&8N%{u1wT`ZByB~iGP9&1>vu!#yJ&sVVgWv|#!3`*g z8<3a9wfI|*+<-c`0fnC%(0T71LB8%}xwB$3!1`u@^-U1#8!wA%@wXsZ-wd$62|w$b ztb=TN9HCkV4|T3W8BfGCuypaVxE8k>!sGYr-zck!cyUy+K{@tBj^Nfv$uml^WP*4` z>5bA8lFgg0ZQC?|3&_w^E|0z>BSnmz_@#^mAOu-uIfC1OFft@2A!luhmG*^V3aftymqo32U3 zspnW_IfC2dT&|m6EzpmBlOm?wnXDkV7PtFicULb%Kelj`Sa! z8DPls_!;uDXpKXec`4TitsiJUSFN?Kr+*svdrZ*oBOe);P&Tt%iD9r1S4QU0AG63O zz+%Yj8UMnV-+7g1b`90`%C#uRnq9Y(B~A0Q@HzHfZinUdNxnM+40)QLA<& zou1w@mTf<&J<8Ec=2f%rooi7(vS$fVus{++-i&?6jEPf1MAZg$WIi%m!V#1uE&sV< z*j;*n07IVX!aqMlo?|QKv%ZSBg1l;7eIeq8#gONe#S!WX;OM5!M#z&IBEe$Fa}Zoh zoz({4`PQg7UlRpNNrpT*?$Yc}w5$bs5&qpTwoO%v|Yf)yi!F|fXBIReuGr*81{0w=Hgd?yD%mT9uP83Ju z$-$7PgCS4&8S>!_!;t?vN%HR=l6e~uSaegC5D!j40#TMYpMNw*|&Ey zKZH+si+3bLp0twFxdFHDt9Ny)bVYq%yEHMifMm#X>fi`&xkoEsQB4O!p7_&Z$a4@} zi*lijn^ggO!q4!oqUKlD!H_3zTMT)Qgd@1E-{nd(8|XP;)6b6xXU)?mZ7Yun^LfJ40O$fRs zgvF5e?C>WUt1736(+M-Qml3uWII*&j84r8g@wlt>3SVVhsF)&5iy_aE@N(&z5EesT zO7)*JIyLJldRh#53c}U`Cl>Ci3~U+1cbC23(~QCmdkVM3kmpEvxmJB;@ZJc~d}GE+hs; zw|)g-Yk?CB^I#NCnEVWRWAcTEh};V#L!Kky<v_% ze0N~T%bXT5R`WCDITBtjT@%7#$jbynp2yFSryy)CaBXGvzmpqh(30=2!jG9?$n*Fa z@*D{-m#zt6G33pEQ9tueo-|RgsAR}PPptrZt7OP?`eVwBgUIDw0d0&J@-7|xE2A@d z?e%=*oe+?KARs*6W=>M)}a23W95a6ASr}%ivU>81mXzd0$^%t*2;aG2}TCj!=8;(b<#q zNAJZ7bBScga}ZqXjZHX-d0Gnb8N`qmGh~YX?AI1zP5vnO?*=3s0VcbwK(C!WV1uq{ z)kV|(wG{-{0w-2hjXcFMe-K07{H=@iE{kf298Dxco+IH1-Zx#Yzak9%Nxu+LEU#q9 za}Zn$oLE_{rbOYJQ-+PXH;?KC5U1brZ^@A7NH~J`G?%M?i<|nCo+q^>OC>{|gWy`= z!~(}i7%V)*kTQOAO}`&)N%an`}&RRJ;d`>k|EELaD>`x9~~@dbbYO*I9nl9 z?pW-yxE46EFv69>{6P$P&$g8|PNc?)b!I3m!T||K@V<%JU|CZm&(GCFR#*)M!L`7N zg??TPyE09Pt7AiA~~OA$a4@}OSOKyzhh5kpvs1=v3FEX&H#_50 zCSa%H2({NfT6xhJgLBRSReC81u0=X$xmUcGvghOozMGT`c^)w2 zdHf7{F*K+88S-N66)RS+G4|RQtETJ&H8A9P{0w<9_6Pt@1Bv!C$%akNLcr_=Onq90~8iyr*U(9K(sl|}zAh;Gdv0^B8&0@$~eR`|buH7l^VQI;b=SVn0 z?X^?GecG~ktdK2+JO{zGz=_3Xoa@0^TC>7qwAN!JL!Kky2;MjGcE9{y8{Kn@XZse( zkmn${7G`H>wsW~|WjmHB&MPkUC=3VaoB1f87-b6CwITCnFItbo3B}1ME40*!Okmn${7C5nD$Wr7m zIJ9x&tyKSQ2_;9BquLfvKH*GCL_IUfGtN%8d%OG^Aq40(=( zBY00kzNMylwTSdsQNB|+G2}T2t_4mkl>IuaQN)mUtY%&<*N9kgp|@nnb0i$0_S&0; zD{C!gR2Qo|y+aIn4uWfe6Dy{BNO9Q3i6Jj+^jli@7uCgMiy_aEaD>`xe_Yi>t5GRL zw6z%W90b<_CsqtuiY$h_i`yD%C$kk2#Vv+BN5TIul5|X81ftmM}Q#@ zeZ^XrN`|~>Fysk8Lmt*8)|m(R?wtNeD`fj*Lo(z=gCS4&8S)$nN2tB_%Fz|>1;tZD z4~rqsL2xaq!#>%-$!MMxZZPDD*DQuSN5TS9>oIuc<^I702UEBlP~Bpxax z4pIj^Fv`rrGsh>9Jd(q90^CLy|zt_8J_IbPHM$0hCBzswWtpJWW(i3 zUN_xSJouzm1ZON*?dPzt>p^ zTfz})uLVP%2Ml>0KSQ2V7T1Ec7X8NBSxSaH4;b=1eug|p!V!EoDZNFsDt>Pfc!+py z4-vj%hs6=_>hMwoh85fQ>%!yO!r%~5rLSbja}ZpMzeAj# z=Wz)a{#D-CQF*%@LDzO%q04mu-(xDi$IX|%2iM|n8vYEM+h}0O6JhYO;JaUVS$OT3 zQNClviOGU#n(fKKB^&|2KCkV)2Tr}{soE0wmvmm2gRE-Ku9|D{9V;Gr$a1Y|mL^xJ3tko;+sgvp6nNV5IE#M|-c1F) zwr3T$Ik;T#*jy2|o*; zW98roc&B*n*j{+{Z)u~EpG>otz984)-$r^_cx*2VcvGOCTk+ZOvH<6f6_Lp;I?|KF z!#p`0TMtLTtHT?J8HKlq$MzQCWpOR_?ZP9)!#q-)cf}F#PVoj}U*YxQw!L0>SzL>M zSuhF2j`gHJPZhPS9KZ6LQ`9(r4*o`9>s#91gZ$LOpPyPHD2|rj6-Q$>2rA+EE;wDZ z4O&PsyiU8~cU9=^VEK28g|AAd<(@sw)5P(Mt36!85xhS5uzWa9Yw&lh*al`f_}0q4 zM&7vmJHyA*8;JBqY`A8}hI0u=@D?K(@-*fz>XgN`;OQt^yTiigvHeB4gd=!6#(H<+A>wVY*TJ)2j^Suu#AN5p2Jlw*k~h8;6^}T2&5onz5{}>_5n^S=F4ma0 zx>FX{;$K#Jt82EmI+t(+A0J&VunB5l6BNWI$PrwNe}CkQNIS2Ai%ughIz;NoS(sw- z`OK-#AKw?fs5QYU+4?tB1lRiW-<)1MVhkhPf5)`qRZeNqh@@+ za!Rh2u-`;nKw#y>-y1U5*ITEZTJx6YD=_3ye%A%x<$>p!4{tY*mtt^;6%R2#vLq|! zXVvU#V#k24S_6wA&yjEhxaj2Htw^N!kY-x>|5|61{zXPRITDU&UOTThwwoP^w6wwi zEjjamJNclDU2+gyi}JqG?vAo4gF{+tqy>w``X9X|L!Ki+6qVPGqEhc_z>{`b)zT^M z%@%K;QwK*NF4;@5Wmc}ReFKuUjXCaSejcno2iF2ao-E1A1pVuMpY~*Qre}ZF2Lkb9 zUOO%goN?ZQ+4CcoUh>3A1brvTqo}dW6waEHw|xF(;dypomlip9yJ!4biUtgPbB_4( z>mcuBZvj|2DW_AN#}~9!uBV>jGkYosuEp;bJ}k9nXx@GE-3et=i0{t1_w1E(W2Ki5 zXa6rhNAE>h+LXWdc}6~vEPT!ras;9QG2__LfX|Oy)9w#^Q!9L+t8!Od3sH@-Br6Vb z#zz~pd|QWm4p$jK@qnHucHl<${J!;+p7S!oj_pLIkUwTF4wVl*kAPN_2Lo04~X`>)7#Rd>>8~^?;*XH1*TDe2#wd}FU z6iqJ2K8hv>Tb>s=^kwuaANgQayS`n^mgfs?3K)*Kgd@yF#2#ry&Vp6fwOV`9Z z=Wc>a5Nqpw5NE|A^O9VyJ3*#4D(7_VPL6I$9UOspUT+}kxAWlB+U*%*wHbRlD+sQo zK6$53KWf?bq-!O%%2;G){@@5i8KX@Xf^RKFFMEd_(mu=_tR0H)uDnIA#os#4qZ)0} zZk#Ep{hPl(Je>mH05RNNJGxuFD@1y0cBD6#a0JbXfk^MEp_{df!PT`Vg=Lhf^Bi1@ z_h6Un@42(JqVw)*?HWqHJ1=GZ{_pg8WiCO4yw{GYce!f5J44IVkTv2`qTmuf`yf&i zS#Iq(`iVofX?17g4G&Q3pdMxCIN)g5cNc8Xntgso`*NL()pu4h90Be-j4NTtU`81P zJ`Ucb)x3CGJNUVZ;96?Cs{PtJ?f2vRv^Af0P+AE`AS(j;jg?0MHvLkowYTqW(B^#H zRY7nq-fzH$(7b_`QuI&l_BzR_C!fQglfz4y5&fKc*nil$Ec6BCv=y28#j~OD@Ww6R zHId1YGEn?FIzRF_JGm@mNmho9O=FIGo*%9(y5@9K%%~&b2yowdk$+p(=W=z;IZzvS z2ezg5T@?h^qWLP2lVd|gxAwHXOH>}1gt0v!K};k@(MUu&Qm&Uk-7c3N`iYjm)?ICK z51IGFxhsx9ZV_)Fx5%S>qqYCe{h}S6sUo-*<%bWns~&^PduFGGi`&mzAtzuk_JB^- z8M!W{tTSM0lXYA9OThZ(0qdI});BJJH-dwpeUzO827BkN**su<6Moh=r!1~T*RIpxm zo^$V93o*(PLG6w1$>nmlI#iqf%OxCv>@cj3JHMXuL=O5@%l2y*ig$L(;#$aG;y_3HN+_wz41>B+f-BNi9sb&w{@K__dcZ_BlK>ygpR9y@v&v9EGp>yMW8 z_VnjWiVT`oTlN9y_Dl%LrSG5cV_4NLCLx^<*R~;vnVT z*&jLSWwEc$-O6ZL4>__Ugd_L}E}7*tFv|&Img5MnrOxMw-|0a!*h(-?wN&n=M{)| z<`RzJZ2>0@!?$~81F`-k1lQsgJ;_+8fw52!Vevb2V{4VFrd;9A^jD48%dFkuQ}!sHT;P}>#QHZ`zq3S!&j2(G2>4St$9 zLj$9y789?o^f^LZC(>*Vrg1?`;r=ZDv?u`!pGGWvyezIo*MU0T_9SR)O{_&fZ>roC*6bM1o$TMv9lBhzAD7ZU zK73H~HU2=kca8v)o!qxse0Tk``WXGQ?s_KA8Kxk(7BZT9?HuSX*K>D#X5UIzw8D#e zVILKcfW6LZXJF@V0om~@L}fl7bX^-iTjphE1TkMY z&jD@*X=frv41R|gcbj~jd7=4mZ5eVRa|uV#^_O1<`!h{}y)F~%b(){O&Usp{rMC8! zw}SN|UB_tE*9@Z^@lHSB2=H4-OA#?*;0q2iqD%j>L`%IW^Uym8u0_}1{AO`>xLn$v zvY88;&D473NFk;TM}pk*UYo&#*G;TFUwQtVR#VjYvZiuZ96{IM&XhJ%>wC<GH9M}ScWdn%iQ=U9`g?%3h0+<#geJPv|uVHCnz)JA~4P6vCP=4Y>S zB*e}7Nmf4aCrb7@rzEs1Pc^;f(_5Y)W!1al2(Sd<3)&1v-!15t`SZ!Gp4tDX2(HEZ zJl@sA2GNhtR?=P!9xZ#VJ$`TmMmI0kx_%xLYE#8-M`@4VlsqQRn8CHwvE*^9I?>a! z7ZkJCw80z|7%lm{tB%ySM|m@Pb$q1dj_#_A)EuGC&S0<00DGP0XRmYS53WUZEG`=5 zx3GY{t{&LygrB|6k#GcE2kbs;&KHYb9ULaMXp);lLD*VgU|{=mdROjBp<=qlUgt=7 zxpW=a(G%jMxvz-_N179}oFm~{F|~?fuC;S6uWw!}1MGET8`$f(gd^zMwjdy{L5_LR zwR6`JYqQmQ1z~G}fdOY;rLZm`_B!pCIT>kSkB9<$1efq~={jHoIQ83{j6q1_tko(`9{DRLaeRQ!>C_C;aSnj)a#>*8v+q zr6pfvblBQQROv0*>lB2oMeK;hs+NQ2vY)*!1MGFe&tB(9c)4_K+Z-f&T{PJ1grB`m zLD*VgV1SjoJXojdb+}>YDn~2)XOI!-xkHf|T(b+xBELLZn*1c_<-f?OWatr2% z_5Tpoj6uYG<)!^j{VutDbiQ4z>SZ9Bzdan;LQ}LsHR5zG;fRTabNSNy74V)Neh-Mh zf`%Kue8bK9ZDV!H36a5lRe zXHy}_g_{%io&#Ai0)NL^bU}!=zi_JfdQpPTB^&|fW8aI@78*0!w@b9a69$QYdnGFf zuEkk{!HLvf*LJ1%7yr3qbuQruFpkUbVST~sKNb{=ceWO1P9!J@uEn{9>AB(cyDeR~ zvl{AL!V$fD@jBl5wzR0>ZX~v4i_?i^ioSFHb7efl!e{ddyRU|0PZH!s?k#+eH#$X% zq#-RuNK$*9OE`iv6T4iWPRt=%)M_tcni0OZucn}1$5P2U_RBudqVmTof@|@w2`ARn8seYqwZ)@? z>Rl0k7;?CKYvg%LxkHSi}PB?-X->^6Ej_Kz{yZR%wj>sESU0e~( z6a?4e?9j-$e59tJUvg4Yol7`^*uaph*k8w_A6khw#)XUIV{H`#*W!HA$cFe8#u5B` zyX;3gt=Vvf-(MY;?0vG;*Hp>- ze$4rcb}&>$a4r7U;Vn|4hZu9>w$>`7t@1rMg0pqQ4`6S1q1V2vwY`$4Ah;I)Hpp^A za|iylaJ5pNoZcIGXTJR2T&`}5M~J^>b5;cE%nJ)fM2Fpku{z*3)<^k!V$#J8a}=NY;oiXn0joK$Z@Eew!3X>1;Mq{ zcU2K14}RwAJ@4x@;v@!#dWo5k`W@zUd0q3JDELK&Vgz4?Z{98IV^L(#W~0EKB%Mn* z0u~+QpbznqZyfIG$Y9a;kGV$ZoOlJnwQjD6LhjuV@68qSQO74$M~ZKLh%?4s?ud0_ zK*ABkfI=s8)h>UBIu8FkOw@fc%((V>Ck4T^A~!@KX4|e~Ld zCfOS3JM*@H`QvbRG4a1UM(=$cl~%$L)UKLb3&SkB9(Pr;QGZc>p<#SJxv_%aS~ySe z!T(OKuQB6nZY?4Y6f`I8>7sK9M;sbj7(TCd9jB&t6a$0*GiLQnQV?9LeizB;iMu+2 zFIZxGYY~2|pn1PjlFqey_bLqLh;VuDDq_*u=HmTFMa<`0ld#ecl*JLB-j76lNw|0a z>znZ8Ct}-(27eSVuYb`>L2xZv6JfOm3n=-$mM+{(WHc{k&UhX#S4*~pBXWbQXtFoL zUq{)3^~K)4<;|Z~v{MjVD|~!}uYHejulp+b^_@6uee<{eDU01iK*AApozc{;V{W?! zqRHj5W}^a~UP0JeIH!Q`Rk+vUmYvYQs_6euU9<9`RyvpPa?|?}_jaT=x$ZW+t4^^c z#hP7B%ppzMDG07rY67tZMZhj|3eWNP-kJhG^Tz0QI@c=EE&^B{q#(0->S2DXIL`k z!GMG#a6%*BuEjo7rO_#^!jFB-<-6mt6Anl?g8W~xHlcYCceQcvJ?;12-OcaXHd7E> z3nK(EXL;$|5RrQ43yQb*w>I0p8>4f{%LuHb{B@k2zEUf_X`q=av7Le-w5>Ib!ixji`cWrbo;Rs@b>lqz| zlevR<^67sp(9&BEHea99QbBMn+EZhN28#u8(QSXwQyW-xggGoJQSO~>2}jh(L!1P5 z9i>jhY7y;6ntMlCyKNi6wN@V}-xnW7VV7|lbu4W2m!6rGYF;aupkvME`@UI}w4{^^Dkz_p{6(?O zLMGk9m^q0p4gbbG*pY4y=;GD61ipig7B2}bt`nLV->n>Jz8Km?se>cnO^N-vUB}XW zd5s5gspe0=w^0yWYv-eaum{=CQR?~`Pk4hg^W!UV=pO+IN6?egb68A&PquyKDU&VT z{OxLM1;Mq{cNNvIP4qH+bNpB55{`hEo~*<2{+#Yx?#XpN&0I4mRzYwr{+(Q|f5!Zv zC-z7)E1r*6p8WdA0@xe8`fV`o_9ZRyt1rnjhg2(y7x*o6A#u> z5L}DSpD~k4Ya8+!{5swE;pZXdgM;xpmv98P$sz9UOh@D9j1lG^eaR*hu-0(|StG$9 z>9^d)d~?&Nx1xtR^HB!{!L?Ld-|&&S%)SNMnN6A|DAqx)1utggthL|O8S}Eyw_Pvu z?BPV6OE|)JI4^RS6vpiQ5Z`0+=v&5_0X@v`#w9BVu61Z=KI{M@{e5uzzmJUrMZ1_A zze`3R43w37Y+l*=rtN*~Q4wL|bC_jnwKom3J=Rx&vN(d`OK5~6G){dE-8SaG-ore; zsEvZ)TJW-gbw0x1qdK08GN-O-VK$wWq;m;J&?#(aQlz)+#lKL;@C#Aqi&ZVm0$-~L zu0`jlw5qXuo%3EPX=XKPVxHZcq;m;J5PKob&O0Cdi#py-DQX_y-po8*yRCxYTEvz& zvq%IR+cS=)oA392UFQ;xAZDll;M)!(k`K#~Qf30&BBW8LLW1_&?%$y zokRt}wbZx!X3%i+!`#D-KeG~)x4WTnF5-ifErY)|oPS&$Vt#vfx-ocL8=XryqFB{j zzT-~|`P+Gs_`dAwJoi&ZVyN}g3gMz zE-d8lwJ%cB%-|n<`s_YQ3W949|0LP6tleGWjWly%^>6go@3q#sgd-3oBrRdqc$Il@ zxY=^e2gc+vaSDQK5&vXNtzeum(+=|T^;GkS`MVzVC{E`Rjvxj}u;2K{-G57_o7WzX z_dI;kT0wBFrHz85jm(MwT|PhEq@~}=ue<47!Vz?hwU@sRu`$hTJ9UfaofGk|AZ)F~ z;9Sz4*!QddVWcirDa|ZX_ME57_I_CD2hQmD*&QLs%4s38^&DlMSzK3p@p%$P>VSkJ z5Q*kneIP&1Q;C`M{3q6Poc(8{g5X-}UA5de+%&iJ*J8eVUn0n|NwsqXy^#gq6@c#v z*)qOZoNnI!?sNBvm2nD!Yw>sJa{X4SkNLoRRx4MnjqD${PvoOf--DAyoW$mpJ0bY0 zBHv*?Z;r~}qmJ?!{mhX&4{IMCYOb7&@-t5r@%_+YX3;|FTFvo;v9Ar3#Su7Jf)&TA z8{8aUj5HT~&_wGeQWXT(qOsDxTjc(17-JrtP*E&t*GeZ&M$F3a9@s0ng{S{75A0X_ zDB{?Mm}~pM(hax2 ze&K^ZG$RM>)!JhPz$F}k7-jU@e2B>++w1RD2AXN}*J$;>Q4w5=ezs3SH#YONr+ z7Jr9W!*#xH^o!{!RxNL-5AKr}yPsV0wCC>gyzn*1<*nbPC*lwS{u79Bd-|Ud-?F1< zm)b_>5{`hs67m$=UKaP(+%)d3=^>(UR~*5$RKJXDzdbS5UhOP0Dv&iQ;FrO_4KE37 z9@|TpQTdvR5tT!Y#C9tHdty%Xw6{=4?`7GI*e_GXzYp3d2(C46F2%a% z#9pl!5MQ^P0_NTnky*T*j#Da}T3Po_2=W}cx#H#Ln6&DqF=J^^al3n@@&`v*mg#2(G1mb6P2dY)Tgo-0$mL!V!obVs*?|o5MIhAyp** z(ndjWEw$}Z9Y+?Vi%LzqDo;-KB-=}idvd|^bAE^x9+4`3zLu!?cyUClDTQR)g$)I5 zcg5DuT8H5yMDKy|3W95K4=C&n$~4mwri>KdW^b$bMsY-i>LKt=v^|~Xt&G>!zBy8y zb0;bYuEqVNkR5+>Q}cR6331D8t2g{S0_%c2$f36zzGIPyG02Nu{2n?bm4CNu+PUgj|#3%zq8W3E<5$G%OjaYTm8DG}8 z6+c=We%e|=a4ocBM3UHV_eGj8E|wf67FS8qxr8J5m&GZ-H~;F%D^ta1r8+4Hu0<#M zG)GzODr!!MF+Dg{wCdGS=Ms)kzd5zU@TKV@qjr1cd*mJ)_?o;ezz;d1ly-C6XmS2Q zN2Qf;1Vw|=JV^d!$X`<7y5~3KZ~vfZ2L-{kR?aVgo}3HzM4A&H^z~|H@F?*^@0L23 za0KRg+1jlYz}?pmXvf!lAf~QMRuEhZv#9Jh*1StkzHocGNPevW>v*v`f5(Y?o2y#L-#x|Uf^BGyawHr<<49mu`{Bw~tx4;T#rGxUtmYuNR{nGO;RBu1 z??*;=^~3OVaeDN4tSkc(j<9lP*fZzgTW+mt#Zh9yR|6D;s>R>c--DNG>v|0mf2~TS zHG}gOIl}VXu~!*%R~6Q$i`%caR1jQCeY?-*lolhJHxgUA$I%KIzDd?Dg<>BO=Yzan zS%}BW<0bD;%j0*bUQB$G@xIss+Y*;>1nr2)kCgm{F{}MhP>kE%T1>v(MnP~b+C9=r z#;RlZokC(dL6+vw1O>shczXlW#-$o!@Y;99k%w}x=CnkPpv-TygWTAd zc922sBE@XvV(pRezS4HN7H^d}S1a+c_~iLEt!jw$J(sHq@=E`GRgk=HUi*+}eIpk} zILo4g{osk-qS&x=+U9m0buQru%4`;}`@FfNhbX!6rj~G|gM#2%wDKT7QtSNu_nRMx zAD(}w{aK;|Stc9_MUSyeAvBdI7>9@1Jg5X+I5_z6lb!?1pBbwbUAnLV{ zwhTwY5xl*@pW#YdG4Ej>@!z{ivJSf}u0=V$$S2imiBFdH5I-9?wPoKW>RiGRyoF*d z>KQ3^d=jUnUXyk>iszJ;I~tkKEi2&7XnI8WS7!d`fkW_O-Int+ZB}YWol7_ZwncBi zo>=h^y!7yk%+X3ga4lHaoVI|hr+r6>q!D$s2Xos{gn}dC2*f(bbt%O+AVL9|FDkd5 zqCGv9s35o&Z!y@b?d~B??!Kwr=&6wf(s}Z7LAkKfd-W~gByO?w&5K2L#8?t|KO8~V zw)NBH%KSZ5)R=qUvwA?%D+pT)R!+HEwrt3Su)=8lcV+j_e6+Hp*2(CB z`)OjHnCF?*^;LweMRUj9=LHeVLbmYSozq3o%m$vS_oXe|k??Y1r$$Q;#wvi$2P@o4 z6T4^6*9)|5N49VW!L=x_TEM2SuSpY8O}FT68nhz|y(8g>T!}$Ay(;AQzxr&p^>(kl z-v{2f0SQOYH8SS;@9N6M(PB!OD5GhCxK|Li7S1d%2im6@&9a7yy&r#S6#c9=olQ6r zUM{U@?9(jFoYPXpJNL60Kh14NXDp6{BYr8Liz1F`Wr=gOR@h0j!j7Wy<~RkxwQzdk zC0|YpF>1p|vFXz$#?5(cbuOV@(36VJ$&XVv@(v>E<;>b8#`)LPyW$9(0%0by>zFlY zn0PZ~jPdYtqHz$j*13cuD0f@nTrC(jpJ3R2dKpRZBo9b9g7#44 z32L2lrtat`qURhoS`JTE5L}DqS;`Nv{UG_OU7FBC)PC!had}RXY*!=hS?97iSA{p5 zb>hm=E?1>Pt;N8L18K7wPX9_nAe9+HXDB&2Or2hUh=1; zQHHD%`(L;G3o3QUjhV+rWbbc{4Cf0^(z%2q=sMtQuyZ^ZfyUo7Cd?lD3c}V>?`mG1 zcg1fl-!Y?pl%505{qk}tPA1@Mumll8`SCsa;q;Z4#kKewL99%lW7^Ccea*>;wD{v> zUbH34yM@-G$XO!&Z(#Ss3fcDe!DeF2%=_u)^N~j~xg>H!K#P|IZrMJEw5S*T%p6nV zl{z?rA_VJ9vg_!+w5Gt%{Cdm;1;Mr8KjRJf;(QH$pP2nU&6^#iXN@D_2>8#y&Sd-I z48KxH^l6n~?haKETq|Wp9`E2jw%1ER_iznAb6LC#cAtQRBXT9?!&qY1k?mwbku9UO zIqz&s1;Mpw1s?FMNqZbB5^u$sUlwkya|uUK{u=V%wLEJ|d4JNX*6wXaPK;F$T#L79 z>`=d}DeyB_4Q;Emc8)-%SlQaGI*wnA6cb9cG*>NE5nPM6e3z>wqUc(#>R~>_i92sg z*(>LV&*T5su8OQ#qK*A*kh$c1Tk`R8S{z5f-b0xY{a#^}dr#0lIXu*?-9klhEy_Dc z{%{l-id>Vy=d~)+dzR=j}sXX=DM+{3?2H#d7*kw>G~_9Ufi^4}!i%KBX>qhypk!6uu5 z%aw0qx>S-aXP2(HEN5NE>k{>!KepVYbA1bIz5 z?}Q`B`yt?AdTGhOo_T9h&4=;r6a?2&pS<|SCC0>QgUqiQHJ4U3+e3MG$xv7}Estng zlaQCDwJTcq*-WFv@*yVnAUc1Nh_h(0$ zH-^Pxl@X9|#MoJdykuFo`g!HI#(1`brJId2K2Q)`i?<%cE=BIqJ1$H!E0QqAh_1@0ikk^dJsoG^d|;?VLU#T zVm>LCq;m;J+TE~P2CCoZsH8m@YOQ0ANr*@8@JEFPP zij}GJ>{sLUyxq+Ae~ncTTx;ws%2Q;=%FNnc$~?Zjv00)|oX#a2k#~KB{DM{;o7U7c zxBOhw?0FzjL2xZP3k$>*CFQPV_8MQ)tkyeGo|4%Tj^OVNeij1B!_`XZ8fzZK1RYX7(2tC;eAU)Ld zgih!^v;ZmeUKQyep?6RarM#VlCcPIyI*}@(SOJm$y)$!pcaz`W@8k1*x%ces%H_>gzBvkR|Ju7^VScBRvjY$17+sReH~l~ zo`v_rZB3{SvY6j!nZ1EEHbdjryhRA;k_JO3)GsGyXU7T1j$O>OUSLmaoxvz?Q#UF;YT=B zuh!+~B>B2wb^q!?JN9t|&%(bW!>Ft1Hm?ho>$~NbKCt%6BOHOFfbUeg8t~kjE;2U! zSLgHuH-cy3ef?4S0`Nr>JV9U z+hS*L@p>)<&qBVIDqJnA#fYAAneP>+2x#begd^PZVeL@5zA<+b8@muZ3y(yVv%ss> z7$TQUSmsoD9KRqpF%MYwALE zR#yLlJv=;;9)V9CSO*PSgZC0;vCB6DI*w?m5ES7Es2_!^-N*1BRli7(!E;}E8kQaG zLhvl8OI0ggx{bfTOp;gbw(bVB%7OKVi@|pzFRU}5XHEBw{p959srE0qYXZmOjc^3isKWOgJ!`Nk zK2y&`x$eY)fNP!mx)3~zUvE&un?Fv@$N~OAFPplqM2^5~*Z(H9;rV!!EV!VZ-LOVm z7lLPD&q?P1b3UxM{5ATFeQ|G?y6>XOv-jNo0r=KME){t1TGDya+MsqsH=hpEBag&h8!_Nj+9XwTDbT zZyQuj>ubpym#d0fw?bTPaKwQ>3oA}oYYjfDR7p-x3lR4!hq@3v3!mq3e;%#mc;VAP zE)6X&(vJ3m)j@BBBbFX4tTOY46OO>=VAP9h7pSq9a?9;&!o>E^qFe}`KcQi{hbWWZ4hHfbGMbzAgu5vQh-@ z0QO7#=h6nCB-jh9JJBRxC|_9}w)MdZM>=o&`H7VLhBVtJ$C`p1mPPmhKy>=3{=tAJwtYz4cM~D=G_F zhIgZ`nKnvx{L%U1VGsG?XOY10dLta+ZezrbWXCftRz~=#s`xj@;#tUjtXo2DoNPAF z$+Bgr>{X~8)}OzLa0Ks@W&MBsz;{ER%o+Ox9uSkV$RjnP<#!DtK?&&XEl0rh1iv^} zP0we(lSjTgB1~piwU%B4&q`ZU2%eTMo6*f7-jbajw2)n2w}ht*@3C5SEv&93ykgvl z8E1Xu6Hkcz7__xK!V&nUS3dg{b-lqZ7&oiRol9%UHnSpJ2%d$Xk0)O=XH|NtpWL0f zhP(tieIDTmbj#~r?{eVUC7*(8bU^n{Wyg)>Wbg0X2%d#+OU15SGKjttgXHcmCFF?k zaP=NV5&R{awLoX_wk>P*oYt~Ly&|#;c#H4|M}T8?{VHEy3kDU87QTzjD}KSW@z2^A z`480g-MbRu;Sr8-w-GcgNQU$*Az#6IJ4f&=d@fzLgxYwzE=+!$mRr{8(i)U!-UvtV zKEbLQR2=SEpXQt#=k5ot85fAi*eA=H-oC$l@UzERUN;nK`@Fs72wdAOpIxIto!~cA z{#|*2)BY!{AM_%47CLs{c<+=M-P}VG<>1AKJ(-RLt2Y+U(Bpwn=i!gfTK@zeS6TSv z9AEg9`aA4EoHic30{5!7^QuMfUcV zBk*&X?Z-^dOLa_=BfhB<@C)2a(jzDf-**a)KVn9A{K}E?>#^01({XWgrSn)CIWFd=vr zcyFOcm1(2%y+nEW%F=+9HdfboBOKxG`KfKAWXj~G_W4(#u6}^$8E9AzX6O^Fq)v#H zUu;aaN6w3aHA`=AIRf7kppHzT;rG1k06FaXZhKve4lV@GD)(cN`sd0XFr&LHzPHTN z^o%_`HrT_na0M9NW7XZjji~&hgFL+Hl`ZE5L-m07SR8?CyNeg4ntMsW2N5#+gq-5- z*C8$h&%$?+x;sB!jo1OJQT=*{ol9HXxPmocc9F2u!49EP9Yq@v+%in)&j?zRjIfbxoT*dovZ2;^%PDKjzCt+ z&P{D3S8psg_ACPmo~|wg&%!YTRi^n>`$v|P2fH^F*)9jessa9T?t$JOtcSe+$rJn3 znf!O5FzAr?!JKD-UxU-*%gQgp8i{_DB0N085$=8j9rly(PfUawUXI{dxDNiwb!x-D zQ&;{zq>3oLHUvF0-$XdV-H(UoeB@2=z^c?Q#MKY{Z7;JuW1lRmDXbw6SlvVvc-~EUNeK=!&Be)P)jzmR4BY{dm|i? zEvz7{M(tLk4w~(h%}(%_gG7Ao5Ep`H;S*&!QCJh#T)A zn?Y@y0~e1Qb7I8SKivqPg-?Ai5~+>oualiB(__Wfhq?-aA{>Dg5g%0j&h#UETbdKT zEk<0N+)~v|5Q1m*-RBGIyo4CB(AQp7C_!YJ7_Ri~&GYUF#7Y$0Q}4mc-Rd(|N940RLN=LRdCJ|TFPog3HSzBO0- znTbB0MI%9Xx2ruow|gTTf%Oe|w)#yt*XK>0<)GTC4eLG}!LzVB0^8Q#D167MxF$*b z{du^mf}jXTV10wPjlkgr9V*U_Hy~n;@LprALdSo5R#w z5k)w{J?Cc=-ge?Q#0#-0%7x%r_&JkBGuUO=3}?sLVd8xmqUM|;9O0hxvwP~<+uM#7 zKZENKYUuI$!6zA1m%`i!;<{LY2p{;Zr8&QKxS)OFzj*PmYXrPOdLtZxl@@gMSk|n? zzV@Ah31SrZ4sry~!kwY$TEN+W@q+o-*CZU?io6kya9@c}5>DH7*Yp-MYM}dGZu&m^ zPo%MA8fJP&Ke{h&2HyH@GM+?oK|0th-FoOxzH~8WQZ8K zClcHcy%CPUN&@`;>)MQ!zHxRUcym5YL|u>Ka{I+84l6ORT7VcN|Zq&X&U2hPxCX2ZDQM8!*swF&Adm|iy+Wn@t_uzLh z04k*aDkeN%v=oyr<8!+g!LzWw0cJ0-dp!y33@>^Hix0oaFFqdJ0iKX^z#SGqzNlAET+CKQ1i$PA&%53TN1(F5L{=wIyU^k{s@l&>H&&ygnK?#rj!*q%QO;01EW>F0U>x6R>W-jjpqFGUCH(@ zGh)TvyscH`3q?4B&p$Y^!FOZ8iZnaNyHT$BKn?7SetMqywE#>*K&0sG$u9bAk9J)p z9D%g}lOC(5SGY=YErmTZ+LsW=N;h&Lcou33@o8B*Is~kZ5dmw{>^Wy6mD-RZ9D$Yc z=J^`hC^EIb@XhD3r zH6K;Fc{kB0$0PgKiEaeXa*tJ+z9U7aa-Ho~Q=?$cv%;^}kwD}iQHPH2fanf_BLaO0 z)Rp)Dy%R4U*Q#iDd=R9@iXt3=s@ZGL4+m;ziWgFA)p};B;X?2%KI)bw9}W{G)=szk zzld_p2S;Fi!?SaT0(Ff^;e4^e>a*I8%ogoJ@GL$zph8-!&+1NTtPJz^96#fGX8KWp z`VV-DsWG*weHc~^cyBoZweC1>+BLg-_XN?fYz}+#)<_qEXXXDWJLm?Fs*wg|+u|gV zCP!=B%s5dB{D7tjK=?S~t!PRr{`hm)aQ}9bRK7jWLc(n86_D%S>zoN3#gR#5Z9kHxdFM&MH1Sy(l|HacYMAac#JXQJgd}1+=Jpg{5qqT_rMh~B644vbM9^Q`1M9O0&8f%-`BLU z5Z?cD9V{uw|Jufd;92Nb(|hj4K)Clp8xM9j7wh-GE#HMFeIDTmya#8gq*k_JKI&}D zEm|B5lPeZSD6KOwJe>JKR&-kvwcZYhc^xW=@>>JsM~yn5m)@HQN1zYVx4&OBPfJ<; zsV>UTt0BjX3vnTMmiw$~K;6U?P+&(M5ApB_N8pnaDD+Glb*5Jn@4c)kV_LWoJd2MA z?4p#piUp0xR^n;1^P{Bmwx8fh-WXMf!uc47{IT3DjQqyOXksg1%95ef}U9 zOU9WoH)8aceqzi3+7 zOwD2o3+W|&B5>Wui{M#&ZeUM~8JWbSnD)}=co?{`duNR!uueFr7R}(M z@BIY)lNIK*b0K&ZA9c&}`!3lYGci`iz&mH%op}|{iEqNF#$Ge%x_T@A<5gb8vB6sl z^rO$Wq4x8fqvV(02f5~hkDD8TPvNb9NRpvb^1%~;_gEZ(3ViPjPR%#YE(3c=?f5s` zh2UBIY~jhW(+&UqbCcxh%g71m12*isTCKs)c(vm?48Dc;o{L=Sc(_XXW(yD4)*?yn z?1r4MH^LF9)&kXXpzis)?;iwo{x3mxS`^|!@GL%Vpcc7%J78e3MEPI05T#b95#?J8gEQHQ$JMXlt4O2s73 zJ&$k%eb(z&bGFP4Sdo?}dwx3<>h8S9;#quVE$h<>UwW1S;&ywjB&Rv&2vnfq{Feit z8Os{>%Ln@gffMwx2kxtcXYu)m{kDCoi=o46%C=P_)jhFik4bNo12^8A^hP<*&36iX zS8yLJ)nm94PgWbC#;RJ*8EP#I*Tv{FuA~--m}*9M=9j+C$9WUvzB8TFiWg;}4&e0P z(-qo{$o^HbGh$Y(d?&1xnsYi9N8svLp352yYnGrK*a*sj3iGvcfDk+j+4lo~PBCXy zVb>L>QoEk=+U*df9GLWYsyY+o(yyMHtg=uau<^HP>hG|YaptVky>xFmRA6nmH^LF_ zHogSqz+a#ocmV6W9Ko|tAK+J=`tcN$0|B5M$aJ)=QVvjrBY2_-uJ!nKtb>)sV z(TWYH2uHZvn0dxWHrf*+uYA_lh2UAJJHTsKxAAekx8xu9TFBw`+Ie_{BY2;{#|wUK z<{kBuPE~h5P%V&SY{ouWmNO|xcJ5q4R?8Ev&Wd`=5vU1B5E^Y+DRyfawxx(%55ESy zw>%5E^an4;n$bcio)#Le8 z>j35zAF7@ckmo-0_YjqZ+&v=jSpqy#eo2rutN-jN`is^9P=q7gZB)+@C*v~5ISU?W z9RMMC7P9>>6Gxdgx>pz>pC20Mpl>aYa0Ks@W#!p9N_Lph)YIuFejuOi89F3G zpWuDa=m1I^9RT%~Bk*(C?Xl*pN`np{40Hfbj1GV!C=0ptY+(b;=uQ9~z*Nuy#2X#J z_bqy=nL=i~?#^B+3v~dWPVZ;hI5}jLZ1*t84m=a7bO03L2zMK6izdpomu?5FpQ?2L zgy31o_E-73zi9(?01H6}u*T>BD8do$p6AFKCxsPfFW9g34Ac)~^}jjNBSW9?z8foN zZc4U8zQ=b1Z*MsQx%(1XH5zsRdvAbTd1JRdwhpcuco94c`C8A=Zf11%CH9s#o1C$q zmu{XMYM<+C`V7(VV?|ZqgZ{4a0KpD(7J19b1&%-7bfj@^N8U^ zaLv-&I?qBTKKQ$i=BxrlxZHU!m#Eq)T5EyeRK@9AvZ+Iw3FGy$i+!61kVb&i@T0T8CJiC z&)YJhQ45j3xMssQ{nk!B8z9d;>S35V6J+97HVT9J$6vLuv1R44;6~!XwDyXlrwB*5 z`!S=(r*i0~a$^3LU>AaCA$M=*4mWKK+FncMJ5yDhJgZrKig1LxA9IiU$-JN(sIsh$ zV)dyXMYbZVpRrGt_2)?+nRQo)criO%arD$%jzIQ3|3?}P?@`rWI(yc36qE95R-X_& z3%UDw#Y4;tZeIV3GqreE@fNs$^9V;E`#!>HZJx-8)n98^eL5D;!Y9iME!&wk&dp78 z-k%pEn*FLddWvuavhNk!QyciayX{7j_;z7t^(;yVo`p|j^mo|V0aoo^PXhh}-Z^=M zBapA}J-4OlhxjVZ`4pTZpc2A`;91D^q*Vek*D?xv5iq+bFTku=<4HS;)lW+3If;&Ihvk<>qNtpCTNA z?7O#(kP!u)zp}$Fd4|;|1kb{^m+*Wvy0yU#u?n#I*A1&r5su(93$9$?>i9$c1Tpn& zw0g%O1kb|i+*_@wjrWUHaDD(k%eN~=LluNK!V&H{AGk5i9tc-SQ5)Cc^1|v+JGD}T zoG@N}#<3TE)moZgwX_A>?aPk_iXzRmmmWnp0$JoqkJ~Cu0PN0HsFf`Tj1srEw}iL5 zyl;-hv#?s8t{za z=%T~Lj=1J71kXYh!MHDijXD9(s??Drarm7Nfl>5EI0E^4ZyTe2`_(=)tc!RnrM(Nm zvyi)gH!4J_A>b`_Viu9fFIsfm7Ohx)if{z-^@mEg!MhLiqwb(vb}7&SEbXFo0EFOK z$laI78fJ6=LFN9lCtmF!o}{%=tUg6J0vTTXU4d^ctQxFrkXQV)FI4ocjC*rp(Ms%?P*i-=GfMHb#jbRGqXP)f*Wry0p#S~o|3?Qv5!3+?0@suGHfwKI zojZUI;9bxGxqhGku0r-4aUHlbRQ%o3$J2iPD9D#hj{4y#~>j3r+ zswU#{)DoegZUoOl?jC!tbpVAM*ApFIew{`V8XdrP@WsJ@Gh5>m zviDd#3v~ea8?4(%uLGb6N4V!>FX#Y%FWpFdVRQh5;8~~x7+5ga%=y=_SA3sYvEsWt zS_eQ8j^OhTm8`J7`#cr6gPGy3`9Q|CTF&-ro>2!t*MepBmr>$SS-rD6E8daIwDYy5d>LZEtJR#pnP|!fxqp*QD9KqQaF9fFc}$yt;Y5 zhBk6n9w55@u-oqbELgF~Gy3&ZtT&!j<;gwX=m1iW(OE6X)JqKAc*&l0E?Tkr6yXTu z>l^RtW^@2qc7%vn@ZWj-S-1xT`}y zk+yEX?c>`?DF+C_v)p40y)b}};0JSo_ zb)LmXIwPx3{XmW$-wlb?$M;&zJBMVA6Vu^+@Qau*#nDrQBanR$tT8~L!A~Y8LDT?N z|0lS^@!s+*WU8mtA7t8ix+qEfar`U)?Bg`6PZ5ql&KuwKwQ?YasT26IcozO0-`jOR zIOEPE=@E%ym_cyH9T61a2>cvbhtBHQ?or~eIZd6#-I^(N0!L65DhEREQX93(CW;|V zZhCwswNuIgityti`@ZF!QE>Ocdgqa|M~hP%YBu>!|E6T; za5mg8)f?dmWZ!X3#<2STj27i5R(1*w(>ee;7SBRf-zW2E!|H!OGgj0D)$hdztrV+I z5spCiz3FXgV{^_}@zb%@&guSc1kb|Xu3}db4XfYm*${F2?}g51!$QhCtboX?K5SFAooI08T8ik?;uB!CX!!c0ph z8yx^gP!@9clOB&XI)D*T-9@g$x1FEcv{yO+%EIr8Pv-H8Wp^X`E^H^(KJt~>;v*HS zPsid2&aj|UB82wWx4|L!K9McsFfEOjGz7P9>d2aJPXPSgQx03EJX zn+5I|a=K3?(RN*cYRCg5y@bb|H9{`>Z;Ha$pDa zW8he=1EBNe2z=H`>O*ZDzSUeDp8mG%bT(XR4G6)r_!#0dIIL&vOOhAs)B#0&F7Q2` z3Rm?{{`g!%pOHl#;Y>Elfvyd5i!`5b8UJHj7fXgZ02((pB5<*v2-@x^JyW!DfR4ox zs0lb!ns~KCvwX#}i|yp&5M1l?w$8Ko*#hUaage<8U*{8ly z4j_jFJG8@-{(qnx_z#o=`Qa%X*=dS!1Ty?xeww1tmi6uGW%j90hR7F_!d(cSW#`TV zPkqw^!4U}B_-DicyIHM%vU44*koHD60vUeGnqrg#506~2zk%KUUaiBuu)PSL#pec! z$w%(Czy5T9Z1iK0(gDz{aRh1t@Cw!1k6S1FL^N_T9k5e{sO+b?qiPGn*D5V3S2uC2p-)Rr^{O+nMcFwfXGOkj%3&FGaxPc#X+UAp2VW}bkmdvAmz zkW0rg)H;BG()paxl@sKeC2j=ILhkSVL)H;B|J3iRA9drPn zEqBj3&*HObSy!4i7Ist_`Cd1z1Bk41L6C#Mrr*v9auC4%ytNKs^sZzlZ9%O3>r{O; zx)V|!33zL$hbw6>6ZL0Yd(C9s0%Z!S6aHT2>y$2#Aol_j$Fq>t9~|(%5Pnd_TC`%k zJOnBvek_i_^|A*qAF8&!I)K7P2SBa!EacLU4!>c}YV(#fX9UzE|B}Cr;?JAsy`zqW z%y_vUuc$2K?tAvQXWHlhI)Gr%0jxGU0E%#gyNy3V&tOlFmFJBPfDk+jbpSK^-8XHJ z4&dh_S_eQ8j^KR)jvo3k4*KylT|bc3FQ5H(hCW$V`zcvuVx4Gt_3aSF>Qiqy0@?6s z^>3N8T2U&${Oeq>Ecznch2UAprK2mJp7UlG4^CcOT@rL1C=2 z@H6futiMs2ZdH{pzz<+(eO*US5sq-TQS;AtJPX-=ymoaPKI@9e zj?G%j4F$ChfFc~h`vm?zhy3JLsJF~f!`%;L^?z7(CPSa#YWItlhhTT>KgMc31ND|8 zkPWZc{ZsY3%Eo8%iZ^~0ND8O78dSlAC__0h>FeV;6AH`G+0 z0^gdfz@_qY=LlrOp*mBcaXyC1tRF3KqKzK_jStU4F1`2MEoOAl4`2cK0lYq{bpZFy ztb}`?_5(QIa;3^b9l)*DTh!kz>mm37)_>HvB%KLC!PEacMd+zZU;rq=-k)tahi3VCjLdQn-Z18BVKbJIq8 zKLCnwgu9K&;0LgM{i}d_#t(oHJPUOIbM}8>+UNp0fDNDnSZj0u6yXSW&x?W%AnNr9 z`wOE3pnf2$|3TF$8TtfDC(r>r0UbbRqXVGcas)E`zpk5PuHE!H07CFAWa4X29cxCn z;N(6sc;<2YNgM44K*vH>zh=i#>TcjhEPT~R{@nAZoheZJ0no8H0{QxHjtn>VlJCn! z$j4)Iibkg*l@5RqJPUOIJC+SJXO+J}xP0$^F0rLmgwg>}gd>oze>W<|+)J|M43ryE zD~Mj*!d(cSg*-PX3QQYco$-?+Px^_NLE0;fA{>DX|GhI^O&bZ@Bjto6*~F&x@k$3k z2%d%f`Lz0-&7C?&N=dl~)cqw*b$_Apf$A3$S^ZXB+o&@^9YD6QRxpF=IT5P+*FklE zCsW-|5sq;8BL#kOt^!BD$W-?ef@h%)pvzBfOdGAX)Rmuat|I1b&^iE$aD=-bQ;zw` zBCzJ(#pnR2AIR$e5-&3J$+G&wJ`fhHhu2-FbpX^`jzGTt+us8e8uaZwpE;F_brC5? z!<2G>5IhUn{^QAw%nVKf9l+ozFSEUxX=C{OH0SweF`~vZtsEc(&%!4%?79ARz70=(4WPRJYk2D8 z5spBHztqG^rj0URr#Y*)#)w&_x}T24vyc<^$xMh{uy^JpsP3;~bO1Fw))iFUU+%{` z>N7H%=Lhvlq0pQ>D z)qd&|AJ3A+B+(~-I6PB(BOHNzJ)W(8E5i5f%D@SK46Hs!@GR5;c-ue(>Hw-39RNi* z0{MDx8^@u#|6kAnugE0_k#PyJ*Xx}4jZpTT=zj% zAFmkF0pN9|^@*$Z4z_>p87G2FH918%0{QySp7;yxnK^VqE&IFiqeae{nm;E5&qD5g zM!yftbv5xsLA&{D*liQs7I=gskgumJ)UuYpQ^9UlFV+d5s6AGLHLc$Q}S8$tyo?k9}@p$=fp8y$d-a0K%8-ZreH)AqlsdkYCp z2>e()3v~cD-fJw-6~VH;x$UuAx9=~u{-gZ>D8doQ*8>NIS0ePIB$jnIAoi|k_$t{*12ow1R;BFq?WAQBH?y={ZqyMu}J(0w802JW}HzS`K(|o=`~aGRA3!(b2S5>yaL>nj&;euyKY*D= z2S5m(g*t%Ru-~Gd^Bi9#+t+~is$%>AD8dnZ{`L2x+N~t9waV*#$ZgSlAV*7|Q3pWR z0(g1vjS{b$WEEwHc2ulBMK}Wa`T`Ln@#z(=#G2p-a4NclC{RZG0T6;`A-9cB%i0g% z?&w&NcTKV#FS;uo07WaQ}*``K2v7{ZJmx_KL~+*ed@8XLi+)f19zrt=Y7P7lhBpRi{M%Avl@7}n^^Si zeY?`|FvaTA`EmrZGT64(0esQAn`r&=p}i}!8^N>OWA(K6NOAK0&i0gj(W)x`k5}^q zapvE*SRlv`0BTIsh`{@Kw|G%_UOBsILG1@X5#;U&fvjwopXLa+A3!hT2S5m(g={~K zIq=VU4H14!w;+8$v100_ae_}pYv4p7fg2T&sG z{0yT2?}O9eyRkLdE^;AQ=>Vv=9Dxk~H%BxY_C5pU!0j?Q?4o6&T?n3qO#IohOVmhP z)Vb2%d#H zfFD+E7rMItKHLxE;K}cq(E(6|Bap9e)pfgR<3feOV$+WsoHk#!RLTKD@GQJncJuvC z=<5DtsP4C+x_`T=?xzSxAV-g@hMJ>)@ky+Bxnq^nYqYNJCj`$z9l+VLd*ObC)&2AS zjuM^nW|b#4H&QwPif{yS^!PigtNTCf*+azlz3$xI(^0Yd)H;68acro%--`&F)lSU& z!&f$2(q6Ip6yXSD-*KH$v-;I?^%fJLx_@*IUENOzo`u{!-q$s&9}WB09^ReitTn7Y zMK}W4_vKHijW)pQj|EnLkzw@-!LyKep5O4e(AE7NfYna`R{yAB^(n#;$k)Hxa8_uJ z{viCGXFlL3I~!J?5IhU-!MLif5r2Z(uww37@^ZZniq$8+2041-agf#D^3GLwPe4}x zi;k7Vf-M2^>EG=YM^6!sK=%FQi4*2&DSPRvqF;DzxvECE3&FG8XSD$OacRGwyxBiO zarAV)9Dz?xxLXL$6+DY3RTHZ-*OGnV>6IUgXSqkWX>yt^H^sRPbRVdCCNN5Z|~(1jzGSi(6AT8 zd|%NRSpD(ehACE`TIX5F>eJa;*8TN^?C*iquWVR-WUz?U5B}~3%(M0bKt@Qj`c>e4 z@EPnyccGGI^(lg^J|U2O53HflmKDEpnOza~uld!m`h?(F$i$C&cu#0nzc;Y@J%H68 zV_1EPa0IgNxX+gM19)Uu{fCCtCj`&JCqSATaE_drNd$u*z_BFl2S5>yK)xP%Dm{a- zo73z+K+jOBMW|x+3Bj}YJXzNIoym4{VB#V|HLH(IC9(R*VB%-wowqf)Ei|iNea{3t z6R`Rp8CIX>137vcH#Y*WlI1@p$#;h3RIEN7izAS)C$wd4d3T&0B}T}`Pusc>JPTQU zI$Q9p`D;)>LBAw9<3X6>gf|?1j8F86)yGjFR)2oO$L8HRv}kxh$)F_p#febG(NhF@ zU_v1KUVAF_Jl~lI0TTQGhGo%y0EFOK$m-L$S=QMrw*!2OC(12`)u#wYxW}qvo;as( z?l^h2w`TQ;uSJd?Sxdc=hEDLBABG)Vw!ZR&1FL^%yyoaB!V$=f0(>wvA*z&yR zA7J$t8djeo{J6;1Uuby;Jj0MbFWtqqdweolmYW>xnsc7TXA@j0@>Lh_LUn&TQ{4}1 z-jY~-c#o3A>gO06DmAN**KRSW?*Ggv+rmGKoMA_Y$WbptTnL_p z%s8x(o3pwG4zc^8Dt@7AA!-CrH5`*)h^eu`igA>3`W zgzEl5P~D$us{0ASvyj!tYgf0?4XXQ3Ky`n2Q{7Jyj^KR)|8`J>WryniA53*W^#eKj z2QR@<+b9+23@z&(>7Yr$Q$f{>i(I)>Q6J({S@H{-Y0K$|7GLyPyIlS-Y4_)41I$AYr*CJ{$r2x zopJf6bLR-;w&DF;p}j8up@!8b1kXZd{OYL%W^^Azb$>6Y?srUeKfLk6y-&0H0}HNF zS;*>l^IfR^u37yu!0PWYtUg7M)hC3zjUc!y%*_(#giLA+t5M!#@hoKZVLiaKvFpPT zau8JauZe1_s{1Lz5xh^{>i!f{-B0~Mc6wg%)fxKaW%bLN>V7(Rj=;~ylh>HDN?+a2 z5tN0@c($;uW^}(?HB!zW)82meo_5FotHM4tQ^?W7Z=lLTR{!4(Uzs*ihK`bz9tGL+ zHffiCig1LxjfueOSK9C@z~8X?gy31o>cjiFX(N4gKSemg-SZ9GN6Gcz(RlS{h;sR- zejrB=>+2c%l(D*>ddm^W*I#??m}@sZt4|1?g&aM+H=EH-U)@i~LXIB1Ce_`*jR<_* zN6zkj)UKDgt>WnESR8@u`-7K9&Anv)C~%W2nMb_-H_V0LS;*@5o_o@qReDyRA{>G2 zyI=M5=3bJX)h7hcLe{!O)=Q?11|x#xM5x*-a=fqN=qbVx$k)R=oM|I{bw43^7P8?v z#$GgcYGU<2Fs%OWl0T_mNaW~YCu?;k$m+v;6wDy9`riPnzX@3Vj)v8z2uHa4k)G8j z1kXZN|Fb98OdH*xy8kVx?srUeKSemg-H%Uz)gJ(?eu823sUOJEA5YHMC(8;s-}#&Wo_I7{`{NAS_eP~o`npr7m*C!>a&5>FK<}=3RNFSV)eg@)t`~)ob>oN zXybpe`V>J{pAg8t<7dt4-}=Mt*-r?bg-m?@kDi$;F?F(!XCZj@`y0=Gif{z7?|8QO zz5_MY)0#RyP~E@8RQD5tXCbSPZR>9o&IhmVrwB(N`|fRnSpEM@bw43^7P9(Xe)`qS z#|2>ZYXGbN*s%H(;RyGfr?2iO1kXZNzjaq?V;-#SN?`Sm8djeo9O0hx^sN4k_wGqz z_3?@!R)5E`r{-5JeRV%YkkuyyvhP_c-ItoxkA>>~Lr~rSqp9vE1kXb5o~{MhI}@t= zTR?SxXH(rz5spCiovu)vKh4oobw43^7IOFWcQ=AZIKq9kBL=JcmzwH+>IZW4^ch)w z8bf%xh3fucP~AVyRQFSaBanSJcOSU7&w}dyu29{dV5<8G!LyKw&vW^%bhG-44XaNP zjzIR^+s0o|-M<>D`!k#BenRjpWc6VUKx$Th-h?coB~Vtf@dMCkH6ZQ zaZk_cQ-mXseINGu5AuJr`h?(F$m-*7ux_LH$G&2I#bD8TdlTi^PZ5rA&&QvGN{N5Z zG!aF^w97vscowqy>z1UMIq!eWNBF=q!}8Nu>Fteh1fPG)ItUI0nV`CV{jg}I%b@u{ z{uH;!j<@VZd0*-RY}q7LtE(p2*IPa(p)+Rv*^BC9(RzisCah zAev?CCGM@dWWOG-S$&FNKL~;B`?xO-NX_cMS{)<4*qUZ%`Axh06M|>C&&r1C{u8Xa zpU#&fkV(NKX;z=A`?HzqenRjp_gJCJ{}6Eb4>m6Uz)nhH_3<4Lj;&n&fzwkX0=MaR zozhxJOS zS$*;YsB8QH2*I=X++<|+sprVi&2y70ws`~dX$a0IgNz*H-=_5&yg zegJpg_yK4H&qBuiMdC&^(%?Io&JTbh9ATJo(?mzy@y>i{UikBfXg zXeQu(^}loggy30t#o+4B|Mml*2uC1ak83r$jrrgQ@au%iP66WwKnR|NIsnjwNUZ~? zn=MX^vErQXBSRIdPZ5qlz8?Rs+eq*7PY9lcI)GcPXG*OD$Q%pW#L`Qh95b}KjUpU@ zd_B~2O05GRKY*6T4}cIn3v~c55~sl(4s`%~j32=7#t(oZ9D$#44N$kyAN&BGGCu&0 zpe$4lKpnl*%7F@?9GC>kfm=p7Kv{S%Su-d>l5)U{xHGb_f*9}B;==m*eqRgCz3Pnt6?B3$_aP=q6p;g60P zZQ6J>tdW@1zpVW0rxq>*&q8jy@4lf@D+l^~&|18Aq=@`*f3)%gpa@5xCIHv3wGIHE zkjr0AlK(FDbs=~b-sioDuHXl775o5xHGTlZ384;vxFS>zfEy9Ki=cAgOYj321%3dL z#t(oZ9Dxk~qw<5z6Im4a0sIMm0OO4x03mpm`>e%? zegI*{4}cIn%RRdHmZ#avfj=)A6s42{St>Pwt6D1ukT)XbKsVnAsd?uFP!5~}<-jPT z93Wl|bpSMO$UA!xZI=3px?BC^A5*m-07W4q#U5n$ZaDlrQ?W6BRn^6#>GCjmMZN1!Hv(D0N2egHke55P8l0EFOKs2rd(w5(+% zANzj*egN%_A3)<>)$y%CDF<-eNI3x7bF;Qox0qHA%rnXXiXhia2xRz+Y;9tC{`;OP z_P4+sRau6989u0ESI6R6s2uQ~RnWx__Vc+T<>Ou24}c;Z;U25>$^l}LQ3nuItC?95 z^i^#jgPl=1fNfxeBanT^G1T04p)&cLA>aoPWBdRJ!Lv{~;8(pmz7@eYYTaU(z074i z@GpQzI08RcsM^%5kws*h8c^nWqKxb|L@5X8SUd|yn&t_djmCfJxeEINOfRJO1)vB= zpe6w4Mk@!(@6dh#>x>@&A$S&_&5Zj3fNO3FDF?tMFNKr?a92vv_p6=XB|DSA51_vB z0|0-)6dYY#dz)5Yf5z2XxId=o(Z#F11!yJ~PVc4s04NJ}0QA|7u)$Bi1ndjogle@2 z9g8Dyg{{zd-5%_`_+pWB81@BdWc&aK!LyKk2VY)uR#ZoS)zs1785N@XfjR)-pj8$s z2f%St{T+5VGJXK@#t(oZ9N}(bJm>(*gATxB`~V2Svrr$9daSK!gZuz48$SSwa0Ks@ zWfh&d!9Ki~)O5BLE*H+}#VLFE7; z+--bq`~V&pKLA4TEL0AFgCSfcN;xn$ROXqTSDpeNNFLz`-Y2ggKsna`+K!vKk zGV}=?fxr)-$~Vc*%fs3afX%5k_XjUWn$evC`vP=@eE}w!eF46T9iwInbpXG_C#ft{4y3Ia zZQ4lh2S5>yaJNwr9EWaRz8w(rAVlpXKnR|N$^mfrGi^xl12_+U0BcT#z)Gq&!V$br z;A~{}1z2VL0H`0R1E{+*W1qarfv(06fO^Xjs0o--bga2{>%TWZPJn#@W}AHh2*I;Z z2T-%)I5WEW!4IGZ_yOEAegJeV)B$*gj#qaBH)0m}0ld}gsQp}MKL9!wM8tdZ?2 zDQ|acD%LiRP=5Mlwog;Pkf;N=ylRR%6I2es?n5wx_?)=yMM+t)T2s+zW`uJ2rwB*5 z`w;|hEAUTDG=2bt;8~~~!1i<-O~DUfA@~7IFn$0O;Rts>a_>lUimr(fSE0IpgoAql z=zgFMU_wg9K6&>AC^<*_0Z?x_0vUeLd#Mre?hCNlC zZqS(!v@bv=qa5%Iot{F`8yJM6PAEPIr_gFj& zl>^VtQ5yrmUF>nO1hKf5uI{G@Mu(-mr2w~AM=2UUz0 znT;O+A$S(*1L%6gerv74KH~>K5sq+Q?JIrb?8HoQ;;HcipnjkZAVKKy;bREebMOQB z75o4qjUNDw4@c;_eRXGmZ&VEU0UQKB0FUtlAOz1s<-qxt(^9<3fs)_{@S9N%P=q6N z-9EMPIN`KicTI0`&G-Qjf@h&};K9qODOx$;2YY~?jP5V47(W1ta0D{^^NLd&3xa;J zvsLOUe#x$P5+DT6LbiWO(Wxm~Iq+YHETS>^0mN--r*r@m;Rxh}<(Jd&&H$^NSsLaQ zYhho2xn^GgLhvkH>l^m@loYKTNWU*YuFVrtNIB4Z?!**Q4uFSkidGIB0_8v`Cm*A*C{Dp<-i|dHAOeGF91b2 z0vY~qj!a6?$^nnr7odjO7l05v3zY-dbIs~s1V4ae@B_GP_648_Mc76rDZUMZk8wF91b20vZ0XU#O>7 zxDp$IAHW{)19)9p`vDMwXCWJoPs>_4Fbwny-B> z_1>~QjUNCZcor%LXw)sM)wSW`x8oD+m>W?_$w3j0KurMbCYYj?14Y0OAXA^!_6Xw# zKnR}2=f%j7G5#9 zQuM!-0~Fy1WcYEdMz^s5lmmO1a)1y#3zY+Jf80MsD+hAG&MJGiCp*3m^}YZU;Rs~- zVdo0d#u;!BaKJ$z$T$cPf@h&};8I-g6s;Us0S*Gq!9n0F;~+p0jzCr)*XJ~=pQ~!D zSON|L-Hd|(A$S%l2bMqW33oVD4$J@tf&Sniu-!NaP=q5;2XJRpOp4Y4d;~gxzRW>@ zTE{y>{*St*kPg6$Nbev(5spB<9@i|j4xkJ;2%H56fw9IxfDk+jbpX$UyTLCf>Hx?= zV7qY;pa@4GUtfDFwGj;t0z<$-V3TnWAOz1srrQ5zrxdLN2n7d$Dc~UB7zY80a0D{^ zxi+^?(K>+lp$=e0s-GNg90Ul#v+%Bs>(?5QabEz<@F1&C%n#}SzKV^2w+z$)Yy%xY zKhOa*Gdci@a0K%8*PeGY&sVKM2aqYOwwz;h0EFOK?z18t0PPDv=gSfJtW~jnC(}my zeE|r;v)rRwaCMrU7aRnBG7bXkmW04Hs&xR!o{$cp?#_r5%_2_%9l#0D0ZcGD0Ak6I z)hA{QS!6H5TIMIFZt;^{rfD4jMK}T({^d`@;av@N0GqDn74;%QwY+5y3Rg>>n)*9adlnww#fph?{7oB;dzcfEA;6RfkIj?`TV)ZG4%rPO5 zukX8$dY*n?07CFA)B$+Us_ymM0a?L8;D~V$pa@5}$Eso8IHw~x2<$fw0>tnls}J{b zv-akz+R!=x+85xmaS*^ZNC!X&vv8zop5Pa!)p^gK;2<#CI0#UL9~T*Z*v~IT>j26a2LT`BAV3J7 z#b;Ar?dS(E)c65hircNm3fINxGp?k9g4T@Hz4^XQ0{8*+Fn$1(g&ckDsT%D@%qddA zi32}?PmLb{9g8Dy^$SKowGArolH;7XIwNF(IeK3JLhvkP#_x=xvx2=do#jwRKiAaJ zkNaY;Iu>&DRlfG9b4OPHMdBXQMrg+$oih)6$bpR_l^*~_IKth=P4EMFls8W1*wn&> z;91D(Uua2f3;@Pb%#M`}rw1uN0E%z~?-OW*!Pnq>=*O&d{XmXB&*hALLUt*O%mjV_ zCyXBe^_C-$+pd;#pXvEm@8ppoBf@0u!O_a)pAb9?nQ>T;F>~G(T>ejIt|5;ZR)5Db zsqQ7nS_joiRayA?**TB-exzshDZ&x%Ha4I0kwZNp@+{oj`LTEwvih*vW!m`9eoJn9 z0K3@lYU$w-j^KT=tbyG>m6JD?ldbw`R-gKT96hLCGV}@V(%=Vh3;Y0%8$ST*Ek_`? z4SE@c_WA*=H+}$w;91CwmzsFMjBZt6^~-|G|7FAK!+SnH(JPn#5?K$cEM)a3r0iF} zM4&0Z;^4eL}d~7yy0%3kJU>1C1X5A$S&Y_k#oW!&QR3!8-5*SOIe+5xbWa;b^M;K@t(13CIF@0`ofC(D`x zij=+^lkIVLaBo0wZ#e?_`j?3sZCNeK4v-D6@3z-2?%+c3Ead1L?>cWrH*rRY99bZr zn3^lZ(>!lccn`m|1kdLz=y#R$Es;ka53y zDr29Z=BH_6nLE0Ss5357?F&G?=$q%gs5ncpCTN=`vmJ(yK~FJPMA3Lx4R$s zI|%iP>erH=A^fWCDkbx;Y9bnfI+{l~0@?R5eKgv#WVdHd=|Ww^tAg!a2%d$kess)_ zW^`x4lgun|F)!IS)Wahjf$V$lx!2VhT2}EH-#Vq14G^0ig}4wr3!g0Cjk;#qSg|0@ zS+O8S%&6H<`9@KMBanTcv!B|)`(eM^N#d`nFZS_c@hp5Iqa(o%#D8ycPNWPH(dEOH z%Rfap0{MDy;xqlgHdcWjK(CtZTnL_p46hgQ&kA3=5cmO1hnfcDHcy_o_J-B}O z_bqOkZ`8XBd_1|p58%&s9TZ1T5spCi9nV(3aNqM>qo6cW)cRfN@_8I)KiwM=nS3EPQ)`s%Fzh-bCSSD>YgSYZR_H zdWvv_d(P9A`Z|H&@;|44dl!OdA*)~P3boN_%^K&}jlrT{s&@IO2uHZ*eEm~@`yqJO z77lFZL5}{{b$F~*{>Y!-8FfwZ@~ExPQt76-?|za}(4O*Nycqj$gnB=x2uJX%$FhFQ zlFKe0mLU8#yAeDK8F#vR^n9Q{Lu=S|fk!xkUp{zY_Z|`OHj!I>pK&D( z+3A0Re^l%=vf+(){b<=9B7N)dxA$S(D@3R)@5dp4f%rtvZ;o)NQm`D$g za0IgYdz)QTD`B94%p7OKKk@Ie2p7VgLR}8D)2K)fFfFHmkPX|@E z4Q@SeSp93yFTG*)@d-z-(BIotO{4*Fh%A zEM)cZS6jC+V08m=_^bED$Sa*dlj@Cd1hVg4e)_?%`uHsj_$5gU_|DnK5j+c7ebz=@ zUy)osSbVaoq4EQu2uHZ*W3wzPJ_MKl99Oj;03mo5vikk+)0{Wkm24-?h!szlwN`!r z6yXRy|FHA>x)|ZNBF+B1SCnf8kw2Z$PtP;5`gARTKb(K07z?cagnu=wPZ5ql_WiRb z7gBo9?GH1!bxDx;xP1wcr@LnL3Bj|Fsm7;Q&Fc4rv+4(DRj*`}a`~qSM<9QWf7fj+ z0#?820+0O>@CL{)MsO8fc)Fi?5*{*g8(6Tmiw%B+~_7Q%lmdR)N}F(M`(`a0J6Wb?|{VWciN*z1Xe$Q)gN8EqWx8smTIgh zf=nDCkW2NydD?KPrwpqy>cFhMzAYWfT`&q;4Z}o{4 zM}XCz-^PvLS$uB1tUhwvyyy5C-zJIGr#^wl7O?u$tvGv{Vf87(5y-yx?4i*aS$#tA zEM(jpbUtbL^YnfI6yXTuxjjRV8~!}TRQD4{PY9lcZQz@}o7KNM^C%pk}Q zfZCu4N8smwg6XW1s>h3mB`Y}RA8S7Vj-V`L^^WZ!XpPP6(|uMHIU(zZJ_PU(FC2*I7q1 z?*P^Pxn@~%+UsU&UjT}51hV?zcw$)nE)8PDZ`JQP%`Zf%eF3QTm5s84r_cU3tiBg9 z99-Cizps#<-Da0IgNTi)4ko=XPzZzSS}l$8~`w{{_T7V^#oB2o>j|Hn7Y#k2!& z%X=5YRjocnI0Em%MYc-A(T~}hTUESdY7M4%+u1af2xbz^J>WNK|{}v#k1UJm7b%g^W_M9a+*>! z_5b1M3Bj}6V->jAPXulElb$K#J;;DJ=&XB=9POO_9>wY+mrf%BtIojDrvXPF4;(#@ za72NKT=iGHB~^thJoV+TE(#5;Df@wIHb?L*K5nq9)y&g&%hkQ*A^6rJ*PLUlM{(Hr z?1M}bKDQ&!{ps|5ihZ}N^EFS}+du3hNBkG;;Sr8#o)_Pc_QG{_0iN3rfgiv!@B?@Z zegM4Z$o<3nhH8WN$+C7WPO~=wtAD2ju0VRnha-4hz22SA)K z>Hxq)%B%=_S!-aCv&T6*0r3j0v6Xg`2Q#t(oHJd4jJthZnB6Bg`5b2p-ir^wbkP$iN*6~8(7O}+M9f5xlw z*|}_~T6Mpz;avMc*w&12atO;{zmzNS5UT8xNZY*jV#Al{TbI};b|sS>oShq_c$wJ`pb|yE!FxiML6QW z3%+o_(rDQ2=H()%(5WFZt50hef@dN7j{VbZe0+PJGkC#JIkHb1507vJvih+1nmSv{ zD);Xu=bJ-=WC-~0asR?O0Z*K0LFe>TguilA`k4d97sCR-qmq;Rw_R%;=XR zRaZg0v#78vk+Y4=R@RN+S$Qs_rvkOHyITpFXL6A27mano-Uvthu&OYqcc={?V1Dr5 zH({YJ1kXDCccJ=gmrxrE^7+Yy@ipYvNueGd;fUH(i-7w@PWW#6!#C=$7PaM)3RPu^ zUn5-zo`vXJt#hSn7GU~{CUPJ6K)iP_(!(Ph@$ZHrpfII2TL0TazF+o7=Svyl+5ApX znA<$?J>MrVYeGuyRNN8FuX-`qF(gl_UN3ogrkgzR_5-K!qgJp&?~QOo@3}>wVkCE} z{^k69We=IW=$ezGXs8Rpv*4ZrtX%F?y*juG=Av285^_M5wjLhgh>LMW;LU>C_~6eD z^2nA~PN`}UE(Fg)1;n#+xl9{bZnTqkCi}`)2g8-G0Yy0Clk0^+^+coFXKgzfe#KW# z8{$UrtP)v^!kbsFRQRoh89a2tPvXC%M^Xs zrWq^=_w|*afjqx5)Wahj;hyuzTf=04?+j;Wd_G+1(c$@0(b)!77Pngaq@qUD|7I~* zlgXF*Vf)!I9}{kjmWP{v;*1>I1b72)gd-|V#u@ZU)hgE8af$MN;(kx#8__NV&l=$r ztN(s>pHy9GQFx`+W$ddJ4ZO2A!V&m6EFZP;U7KO@#2>Sr!SFVb9zj`ro?vI7Rzu`Z zS(Z7&rv1H6I*-+Lm_TX*95}&_)#*u`gkNgbTs5 zu#H&@@}=sUxN-j^%A^UO2W0**N+Br15vWLkC&JXUHA`U5ua`-ZD_e{W7@J$SK?t5T zYe7+X($5dy0$erN5Sk#H70zYXoe}}>gWd>7pr$CYj&JG+=SyhgUUHIbd}P`_{}0=^ z5IhTywESuQRIGLc{ne#7*{)w7yHM$Hg`fyWc!m~(HGl%C%b%`;Hm?2>ClfCAvKNCJ zJ3kiBTG^;5JXsb<)zwZTKd2|4d|X8=8rB-Tsc^TGLaB#JZx29q+l$14se=QK!OoFI z;CoXr6?Tw-v+DIjAL-|N%)Z$#1a@TeMo^ssEPpEKm;^tPWmWrTpgeQ$E4w!6`gnvR z@T?LmdthT34aBUBdaD=<(wI;>N zm~WEpul@_mfS~a~4a>h9sErzvV`bd7WP1)Ec!VF9k2JhTosW`>DrXggGlhHji1I$Y zpS=h?GZgwix~>AcisO4P7A#ncYYWAtNU%b7f|LdbB@Q9MgS#~$1cJL1ik9|=mr^KF z9P)PZaJM3*I0UD~ij~U$-krJGyP<#1;hcAJ?>DluZ{O^E-+beJcIG%--4PuI%j~n4 znU#-@_3#KsY&}#Eerwe@f&2ITSoz8PR5R#KKNo^$p}qwC8gv~!Z-vX+vvP@zFT-GE z+lg=l{t_ki&hG_fKipRr;FP8hj^z@UU%L@JYx{GYmk^@q`dHZ%+>QHZZVgIVCo=J2 z0od=VvUuI_7MvU@GbHB_6>lol3$>RcQ1t@JQ17FV9nh-w^8@6SA{E4*I6Q&a=?Bm1 znm0cv0rSKA$7#5_WlA=XjcZjCvoc25C!bJRdtc;-H;4lGtLE=UOx{{b{@p%RP=$Z6^-1}EEG3d`oR~(OFceqRv;N2uJ+f>OGjZ7xIFh6Y6NNewEex(jZasVuY>0 zCIrv=a!qnNLs9h$dnXsX2zB7UU|`B>)WNx^Qx?xcMUqnoA}Xy-67NR^1DBc${D=y| zY~qqYZ4`axXqav6sp|QsSCaU9lTtcS?Hn=uer~&tGo>#(=xHvHeXVyc;+XjzA^X)JFVT zZC;)v299XwLh!7PyPT3hfBe((hAlyP=J`Aleg~ZhN1&z%`v<3b>hYKg<53WlH5|dS zP@Uu;LaJ6ULn|c;Z(XHZq6m7Hl&_)6JF*$saf=Lf_U{;n1@F=qGmQDp>E(FiYzRnkB$ZCbnF!JA&qDnb%m}@pmW8|IOxcX$VBQ$f4^~ongdB?Z&{xVCO#1jW8`2swP)N!7lLOgMH1Dq{AnqXZRlrWP3};by*jmXM8j)Y z?b#VHeja!T*={#Q@XbL^uLfVX&&> zRdpoft0VsEUrUtw(~aO+`#;DAznt%T|2>N{fR|Qd(RM{yQLjLlhetT#&M6;zPMxuG zR_Mow`>n-&PkynbWt0oSv#^EuZty4s>v;8hh$wSCpI8U1C690fs&@)qe9vp?qNrfsd#!m2(S~b?<|M}l(MDJwE}7WjS53#%%bOgCcn9X~Pml%I%sS;fO698uOMi@l12Xn6B# zF-rV3Uo9bEo_AlMK}UKV?R_Kd3}>aj(T%F z@1GR!ASer!Zn(|}YA&cl^h^>Xu2lA~Zbo}}gqN$-Te)F95ufd5Xp-pod!Xk?u0?+=B& z;ZB4j@N<^P+}@8ay@6Kc?mbcjM)_FTvc|lFpe)o$fnr^=edmhy7iVI3SshCT!fKQc z%piTe9d{iTs6xW^WSsA8J>&x(&pEwtCKI&k+d^^TUpdywn$#V*A}7KT#XimgYXH7p zH6x2NhltxvzO`B(igF=%7I1?$H=$cOETz(Bf19>7s)N1W3o6ApZZ4}H$yy)oyPc!%h$YHOfxqF`E*G^;#&m!0KYXpp< zb4@s6ze3vd@!?4MTh?|i1kXan4f2DE1t{D|jrk3~Z(Kdc z>jo^i{nm);HoEGdh`-fUTwH$Fie9OdXB6QG)O#U^q}F{>pGJy;?Q_T@a4I`5i)Z2Q ze8*kcfwjaH;c*xI1pJrN+Sayb02JW}ymvvHqw6T-+d$0jT21b`*uaJ0S#9g1RxLX` z;joTF2SHDrwTR5Tp{<8UID%hkm|s2aEDn73*y?#8%!S}tQyXP#wBfVt-aDuEKpk6q z*AoSD*O0#tjPURXNASDHFgmUA6Yc)zCwC-`haFYf8dZ!@BZV5KG4rx|4}W0^_XvKl z>9Kk5UT0Z;Zv$90aK@G+wm;7fyAQK^;gn@)Rn5?1B076ZS-Y6hU(wybv-tf6yXY49 zir!-)qmMBVUhbxUcR$HiLF`mPK-R zgx^}Hw>%5qOZ;kO)jUr2?is|AzA>_Q#cuZd2Sqpnl^m!}Q0|ofE=e`tfKU8~>)Y99 zzL3U)Yd@A2%?a}o(tf4d(X-@n*ULJ~5&Z^WZj>dgV-XnB%x2{@d&Wax_gO>J$S-2D72wY7-T?Vr6@QwO^ z5IoDR-nrU-w7GX^oUDAIjcd#~0`+h(H-&XAtRu^;5oY3%I5_~;sd-sEiz|oJ)jgD) zB(Hr@A5ML9t_iG1GcbpQXJQ%ne&D|g8{izQ z2`yX*o`p&$XFNJgd)Cl9GfDn&Po1tpk@N`Ia{xU@|EtQqzVl@Hd6cXd>_%X@l(qTW zJn$y$qZKK&s!#Dh^LCVcP2}X#kF7X?z}41d@ zKNW#(Y<9_8{Yr8G>i1wzwRh$M%g$p0Q1 zh^R0rNW7`$M(`}~T!-JlUAm6Zrwx&EHb(4zhOE95;RxO*!)OIN$?^Zh`|f@KSBTH@ zclrd+-F<#y19-$G0fM)kBOa71fWI9!+Az9j3lRT!D+qBk(uLqz;3fcUNs7L@Kje=V zzaPjXz6rJc0KDL_8-Pk#VB)-IO7FF^kg54SQ9{pAVfxiaZABY6Nnng+pXQMk;Rtsf zanS3spkBT+A_P{voU(WpFv`FzQ5`v_!@j$#WkqzSF!UTqi*N+*lVM!07cJII%_Mq6 z+9x8UwH?> zPv;JIA$S&eTzK2$raHPG?JtUbwapwl)pox~i*N+*6X-I&S2LO2KZd#bfqeoEz&m|1 zjF^oHV&&98b99vm4{tk1fDQy^t_p1!5uJcv*do`hKR{uEw)lShQM!aA$V^- zZQl)e=l^^5l-&~0`@nk|A#!{&R1~Ye!1`gXt@lZba0G9yVU*1pFJdyqThs1^qTVMB z!Lz`#2Hqs8jwf(Z!4o*KV9`%Oz#=;lj&S#*;Wl-b%m{4k>Ie3z%%ykx1iQ8-$BL?z zZ(G|J;mWqtTaLi`V1K0jj4_PV?_)(O=u)2nyTlPZ3%T>?*q`+XK7nWA>yv)+xz)-Z z4=M|^QQp;yRk?1&NZ1)>KplDZhIx2|BMyI25PnyvRiE^3Au5(DE(d?t#)aTnxJG?+ z**V>+5qoQh;>mU858zpIGa*0x&Rz^aj|5=cVZG{todqgQ5arr5ss_3>pgK0JZ7B*S6_wv?i~^?GiEsq(6U^aG0|ak7N9=B%ADM9biWo+o4|9qYn-e zlG*EYclp=w2v4=2^1=Oc+5Wx5)nMQ|>?~YMp5Nq>{vB-6`I342QFpnmUIeNv(jpw; zt|RQLT;lGvaGBo|?n3Y^-a^Be6;a9jK6j#w=o5o8GMIl{3c%SN%)-3mQ&g*9w}KbE z0d&v(^2D3Vv&PHnLqc5rKo!{P#qahBRzH3ObrAUP908`9x1A%hj>+fEzD}WGFTk5c zX5k}4I2S@Z^9uJL*T9x3JIaiJv^$_B8si zq#<~gyXQE!yuUI@THqAojyTtcUqj%}&))D~^4=&pHn^SZ>Z0d0-`yTJ_D$ zKr?W2f(#!X;zIB&&I8_wxuJ|JM^h2l;&{DhJXc9D(Z6qsy-7Z*5YqV*XY_ zlDxkUm9h?kXW?hO6P0FrjL$6p_j4r4?ma_bhU`Q*0yGHTYCm1k&qTad;Hn~1;97A6 z&uZc8lw=q)zRG3osyb2@f%E8jgd^ZB%pO5?#z@xw_dQ+y8Y#`v=uPF6#k1gz)xM|I zTk8ACv#mq9hsmNNLh%jK{tm)SA9v1q!AU6q_nUxshJ7!nch0Ww4Yp4AiI<66T7bi! z69HEZFK9gi@az&^67)J&SSM;CB@+%E1UUME;DW$r~54Xc)s!E-ts`SwhU5NP_&%Qt zKtFBRWePLza{+kf2kd0^W~yxes==G^-72-@_c3*3<$>X#BzGbl0bUl~N--A#l&biC z&bo5zkeYJ!v?v#XX94r;-TUGfd{RRlXOHEO#kNJt(6+(I@TNsLqS(jT;oav#0BX|V zU2R7`IXZ8s9FW0gk<$=73)V8dLwa5eP%9J5-{g~1ZiUFi%y^eL5st`HH3z7*ehI)e zQc%$CDJ&Ph36hT+p#I81@GM*n#$O+`YH+CR4eR@<-DInWp}^fa5sq+=$KH~Kd5scTe=WD3*2bn znRqS$Po;r#6obO$$Nh54uEX#&C?~=Zuv%}=Ue%8E#)E^UPtN?Zx___>!Lx9+dw!Fj z1C)Qs(PGVImTM*C*j}wcx$HzZVqI|`m_UhH>e$-g5V{W7d_mk?0_hRy*z;-0<1y06b|0OL7n^ zjt-L}6Q)}MXKV+WvojSov_j@t zeaf`<@CZi$GY$;$@c^~QWF+i2fJwmU4tJtm2%d!{70FC>jA%7VZdiHB6H>FCji3le zfZK-s?yTxa-5M)Xw}P^`PE&aE@`1bLXu$No2LkAPc9^nZ5 z3@o)>5-^HMBjt;&nXSg*VecR)%iWJfg-6OWa8_J~&LJ*D=i8ZKE^^eqKD@Os9=cVF zp;bJ>5%{_NtRwpBCVrkI_pJ!=Z{8l)ubiIqEMzi%S#c;pxvJba1DwHUtNb733q$2V zT7)CODaKwqQCIgqoJEf#SaNl+3&FFnBwR01bv*7fT<-d|lX+rZh>f5KN8oCAKrwJVzoY&Gp7e(OoKA!zK&jvjo^eq3JR)U;+*q!L+2Z$(E(Fg)_6i((1JoXq zhw%Mq)OfVnr9v=r+i4Mw!1a7wjZ)viwe|YR3tKJoWn(vjXW_ z#CJ}ocAiyWeOA!hc>}=P1bV*en^-w)VX9fXNDo-WaUvWsBEJv3d3*J*!B;y%rFk^3 zIGVAw3&FGEtgP@%ObJjsk#d)ABZrsBA*KwAfOTpo!V&n~1!ofd%lUnoPi5-9(&E7P zZUoQ5xfZSns}tFN4Gol~PL~wPXWDvrgd=d@>+t(l04fS#SN!BKIeADP@$cH6$iAjU zI0AH1$nM&+0q_b7&nVkeiV@+%eO(Bih5KINth)f^_V#jckSx`%h{#fqXf?o9GFE z>jgc3p1HBS)~cLnK57{J4muHzsF^LB7u+xOTr}hP403EpjF@INbRl>a&aaR|P^a*1 zZd*kDIwMH@to|B>Woe4p3(tdK)~nUcK!int|4tBY0Nh zPT9Q+`|r|kEdR9HWNrOzfGGKG1y~VwYUi!uXu}xxt)JWsE;O^ZOo1nZQ#(h14!|py zP|w@+hpXEsMpPW%*@fU)?pC#_dBJm``6yBDY!lCrp1F`I4uJCDn)rIl*VtjdXM*D< zunxNd@N`$hIM<`Mb^ma@XtA~%I>fSwgV_IpFYpOFaFzkQ!CyV5Ta9-O6W1FLg!d08 z0*Y^M218}JOR9dfu(jksqR86AcA-tHgCjtJ537;e^;(8`_<3^1$w^|#MmK_IJz1R_ z_73h0D068i^kaUGI@Wh{Mu=*4!(Bbcw|h8&0p4qnYj*l%80&T{v-~Cw7ByodJv_n@ zyPNyMuQQ>e&U|Bi7dAu;n&n3DEW8)W`s@r)XV-ig*Vig`FkVz1+tj{GD8dmhEBM-D z4qhYB!Y1|qv{s(zAf{iAb0K&ZzmLJ?X7ymJ%)ofDb3%Ri9dzz+j=;Uu<5PA8;Qo4G z$!18aQnr4g+`R}Ff@kqN6jyx=V>#^k$pg;A{JQh;_~9uZkKm89S+xU4iY-|OdU%8* zo}I}BGl1=S1+vA@!RE%;1X1?uN-hM?f?X;0-J_W5zhS>YabSNgE{TBmsJyV=y31Z~ z#kIbM*YIB2*8m(n?D1C&@8jiN%->rL7ypBmA|Bxg*vDjlR}gI&V|JyQ)7Qm{?GszM z5IhUl8SySrbu|2_f?22v>_mcdczJ{){`fmD>=)l*w-ELZTIczJc||O+$n9JRp2b;mm@_mv)UeU) zB=O5{Z2;kn6zt%!zp2P`yT=3XlFH!m`wC9(rt*T=ZI1h@^}lZSK|*Wki{#QXJH4AXAruiId=)q;^PlzBEgBb zUv=y!O701>l>-g0wYEp9{jB%kIT&nz#_^b2I9!kU?OJcm9N%{k*FIC@K@pBX_C4W# zq(=O4t+aTZ{ZsMb*0#9I)Q*sH;O(e(b{6Ub{Az{iyQIpv(!vKk;xCs+oxT%6_MH&! zIxc^oYPN%WcjSUl7lLP@K49(BP+do@H5tV@D_V?&{hU0)5xh^ZyC1kB{Qtp2cRx@E zVCHCJa~`~fuu=rsi-9RC1qdGD2;|R0W+*hA_3INLZi55=*vai&2%d%Pd)ST+`s!{4 z<-jFS4h+!BfeoK^wpXx`OZN=zYG)y*_p(9^^cL0vwf^uLCCi{Ui5$-y+!cM30>z0{yK;y;B;#sI1IM|Zv=nTq%`k)*b zr()lD+j0_r~`oWGT-TwVf2CDj<_j-=5(zbpx$x>GW_^=#Z91c;Mq1u zIY0=Wh57(EK~!Jez6+B?H*0Idr%6f&KoO2Wh9AyL)pZnnK2p3__mOAMqflFGKnR|N z9Q674;ku5EO=E>$qg3;hRt`{vBTy3n=cwvB_Rk+FYLCibt;^QNW~vFnvygGG(;-14 z!j}yZ3E3B0S)!G40M5v@?*{x9U4AplZV9seXG)LIcS)x0L&b_$Us)4x*ab{hpb>Wp#4p3P<3-tkT`mJu&?Mr^5(|JGH<-MA=a)2Tn;qFIzl`0$HiW;;_f=2z14Y6}uU77oBxC_CvkckhdJyEx+nF28Gzu8vMwJPS1e`#&H=-ot)kIw&A6-3!MPz|tbP3vFs6mBmXk zj2roLib0?p$T=X&RR>2P+duK)6c`VjS;l{uNAy1zCI__&cOiHdGW@@un526?%Q|D; z{H>=fyEYtM($XRvff|I|Ce zrU6;+zt&7iQx4>7JH^gImb_-R>AH?XaQ@CAeX<-c7j*#ho2Zt!>wvT9OgxKjR;dVE z2SBxR1Ty@u!>Au4UoJ8;9T*}H_(!=AJPYUA)I!66Gxf}>pl2vCIn7t-knu0tC`Y$q&)SwjSIoEknQ&#QR8G7y+`@@_ZpKV2V`k$>i{Ui z5y;)|Za!VVc_r;H=AZuyIH3z1fp}Rw3qO~+G({^1?C<%iBv}wVIe3I4kl}ag7*u9JI($za3kgeM?V%g`cY-jv&Jcl zBapk#c|oCJW#a!q@GPwR>qXS6xzFcYrOlyo^Bmj5G%dmrs7~;!H4R2^1@t^qCt(fo z8zI+p6?Q+UES`l*g?dw_*xCl*Kfb?ig#a7=8sD&_6MLOC4F}L{0hN zXj_}rrwFb<69U)$ie#RsbpU>cYsoj>+OoxS#XA#%XCd1kJY$mP=;Kmz$mwe%<&j=3 zZ5;qbI0E_ls~=6&Isi|u;?j3q3u!zHaUpmX_*UD$oapLl7=0(1b^+bA6XMK}Wadb%q? ziv!-Dy=zCyVtb=(9RMMCR@O1u?eC4!e)Re0hSf5xn;cd*0%l82gd_O30M2RH=O^)h zb2$GTl^ir4$m*{gsKyy}0CX?F39HYdq<2;p8Fr_m&FWKxBXG5TXUPdTBZI5E`G0U< z{Z>b=OKj~z@GNBePre$bbpS!l!{rmu0R%USvRQqKa0IfJeznHIig!5Fu`b|q>0hvr z-05xYLhvkP`zJmeqjdngi#M0mua=M>l~y_cif{z-^>WGB0HrlpbIwmLJ>n-PB`O^N zA$S&Y_hl|o9Yd@3l9Q&LwhqFscpl*hWa1x%jJEkNICtqqR~a?qt~DCGKRJSDAw#pQ zXp+_e)CV0vE#E`QwS1KhfFkJ;g)WZLI)M3q^_HLYIBbPZ3b%CtR6DZz@m7-E60E~P zOaLG6-?l8XqORC31!)nEKvoXl0F<8LI{3pK0@c>At8N6(Lbg9KKeejgqG6!LnPD}B zb9i}#Bfww6ek)QsfPeB2mA@8UX#LbA+=bv-IGXs@r*r@(;FQWb^8&4+ei1fDPZ5ql zzP?r6;rN>f_m!n}06vSA4uB9m3+q6Hs-scFDEVWx6P|7(l@5R+9D&?Dz6mR)dLNu{ zd~?$>>+7XKHXA;6-jFmM0KOHI4!}VW<4!sNiXit-2>cvXe`q=#Ktrtq;0Vfc_anUy zfDp*I`_&p^w;j2AYVEr^0E%z~em>i3uG)Xg_#aJPSGcnG5=Bj=q5$ zCA-LV{xMfVZH}HI9D(dRu7s!&{0R0Z!aQ#DmX1mXKnR|Nb>JF@(g6&zhRfhH?ac1I zln#I*9D!{A*Nb{-9l)ZAaLRb$o&Gnk^s!leif{z-^}Cz*)vfANIzhJ05N4J)+qn=t z3)%j?FM4Vnz{E;%a`b_*X5CK|t4|S*K)!x@-yWKy@4Z>|{K<Qo3-tlr8g$gkfjN_V$Ov~nP@MWC#8p`_T?D8^O}P=q5;A8@90v{nwx zhO=e+wEIw8gR^CMgd>pcpWnn@5r(U~`iqis#r{AsuW=O@f@dMyFPF5_%7OmFg5;a% zB4X0dy=>(Gm4$cA_>^|`%)*U$So0IP~H_JD(`C2NA*MVHB zgD4YGM7HY}B$^Z*X0!Sf;Rs~-+s~qY+#HcX_U#!XPHwT!YD_DOXYtk=M(ott4c;6{ z61{hqvDxq;JwwuT0M&kKWv`whM~{D3I)KyxSF9f@bQe=+PP26YPQ({}GZX^Z{*?n; z;gcHPIo}klU@a)0D9(KmZR-Fi0_R$S-dtt5>zJRrfYszdq6i+QSbeI4Bak0%IxPtA zW2htS+GucQg57$L+z6h9eDs5IAzBA8xk|WIxKM&vkuAd2b9}oy+bT3o2S9zol?-5D z!LjG(-Yq?-1E2^;AYbpGuU`JfS{6A(WU6WJyiY^$EWEEO#kAIM!U@OwTJ;aai=hRC zeV0&#Bap9O*grHt=>UFj`KNUhbO1wM4R#@T7QZWD@6x7?*8d(35CI?I@1S$Pas)E` zcTR}w{ThkFNjE*U6xoOC02 zmYa7z^m?sXZSO!)rMi8xHO!qABh3-W@W)w-!3K9@I1921>}*^GYp=X4o`pI9;=JJ- z@%muHZ1a;u#%7_m4uD1qS$+Jbk`BNb54>8}^tIw6#SzHY6WTD6gA1Cwt|yA}E5dCZ z0F}kF_{hR9=d`R1_svQYcm4Xg#+)ON;aATRTyM{Hsl9n@-f*$?z~?Rm&*I~67>g!k z5`$|;i>ALv**X9?cU9opJu={5?x}5m#%p+V*{4F?SEai@GgAt57BO4e+Eobfg;2>}w90ZPQ2LXy8mre+G z9W%i}pba<(6#7^>2oQp2A>04{xBy+p?yi5CJ=$~-7hqp1k8lL<6KEj83*Zs3`ZKhH z0QCb|eR$5l(iX}07Xy-KnQmoi`JB zvye-N)pLEVNC$9AI|yVQGf$9%fSF@~orT3JwB>b`T&0 z&qA)a*X$p49i#&|s&xPq;Rs~-Ux%&Gb&!L=THvOOu~FY8O~64Q2pj}jG*=D+6yXSWKT;dSiOMy{TI=DADqa@P zLLI>7Z>d#pH^zz-IAOKyX5}D25sq;8BfSoQ`hl!I?iWqpC*US}fJ?!Tx2?|?DpsF* z%Mn-~u9c`~NtrdV!go!obta=5!LyLn$30%^_v0m;0RHJ&KRFKeJM%1L^>MWe%XK4e zz&C2b>Qrk}oN^GLvN!^{dvrontx5t1fd=3purEsK00_aekh@1mN7bsoV6T2j*sI^9 ztI`2bS;*>7e0W-*n}8cp5}xhRd;Da6IE9Z#I0E^4^iD-IJWD_ob`}`-Oz99j3mJFM z(4TdyHh`}|Bk(mS0gMTca0D{^=sK))06RbjFmiXeTslYX?x(VN7P9^5_O1|H!5L;Y z>;`OoH`vwzPy|_h^jO9$UbkUvI-Xlh%oQ$g?{K$@Bap8@+v+Ba2hK~{fev6C=l~{Z z9RRh8XCd2!}}gz(F7!90cSqVYUu{5IhTK;nYII_;>0= zbKaUSm4MDzhR{QhUkLBJ`?DHnABbPWxo zO0LRg?(B*3pGQgu;6xk*^7W-AJk)n$ZE(2B4XgCuz!^NeES{yfdo@nr=`_~IzdPsv znxyFfRD>gtuU~WEq5cl8JXp;CJXcT+2Hwi+bDVHW2FP2vXIpuozzxVdOjmCZ( zMq5~Uo{+1V3&FFng-)xg{kYPqm1(f_@7K;|^(n#;$cE!LP3ZuhKiy%?YSCXl{-Uc3 z!LyLtet+C`fgT6o$#LbOweLh1c@*lv^)|e^qyqq_7=b(Ck*^022%&b~cl_XrwQpQ^ zd1efrW8xrq7P9?UKe{8-E{LUB23vpij+Z0W2HC7WMK}Wa`heP31^)JeL(`V5 zrkws~9l1Ay@&lOPwXLS zpApIrV3KcL*&qA>iUlY?07CFA)B&JByxOa8j;<;HXVjGwy=`q)pCTNAd_C;K7RnER zbO5!q4uB9m3)z0THu&&6h~k1z5iKO^&zs#jK(&rPZ5qlzMk$%Xcg!Hwt)`d_dQAnKnR|NZ2vd= zsE#g;edL0vQS$rHcD4?HA{@c*X~WnCzyA0?GwdtkBZ#d2{tr&s^Tzg+y^d!#6;OdeNV20KK5Q1l+4gfu!l^+1<0P1QT07W#JYcCzK9g?CMzA zYv4Ae-+lmu;91DG4Et(_ zaQp!Nt#K&%srCb)NP0xqygP*Q19PL?b{p6|}9&78G2p589A=|(8&^Fzw^f~~Fa0K%8`0fM0 zS8!jw{BEdh5Q07CFA9L?*ke-w(N?=^9RG+xxP?t-5_k8lL?^~aB`({-fR z0Z>^y3rlK~o9bBFbd;RZ^JmYgD^YeG6yXTe0pOdk(gDod7Ap&FPPKgNDn9`4k!1q! zY-IBBt(bHG4kEoD07W0GYKP03ncZFYB|+ zZaZ@K)Y^A-02JW}{M@a<65aEW;0Lgg`2lbQWg$l&JY#`S9Q`uT0c5n+`4{b>bO03L zI7D8doQ*ZbA_O1Ek&_yN@VsD^oIneqc51kXZ_9u%fRCg5y-yd3X<{z*aUt6Tfh$>R}(jaXWc#Fht50R&_Z+l*_VdGy zcmRF?FE*bvZ)raODvKkKeGj~0uYSUlp)>dayahjiwb~DW5IhT6{XA6%3*`s!HL&_s zfYp!ItUg6J0@?Q!pMHU}aHyjxu=<%#kX2JPSGcIIE*jegNi> zAejoB2L?;mL4E*pwI2W>coxpDuvLoHUkrW#_rVWfsrCb)2uI*d zwyaMhcru_LKz{H8I0$|K9kd?+A$S(DuLahBCX^q*?JCc#uPSsFJxWH{tp4`r{_rhQ ztiI-SY*zp3N6m!t16T;V`wzkH{`=pk-ThP+uLGG;2XQOBh@1wjerC<;Q-mXsum3$! z=zgpKR(}q#`ZF}EPY9lctUk5YFltq};Q0%7_is6)cJ~L~_ykTKQoH+0O{i?I&?4hr zr^6>gvHI=%^tLv`?*8)Y)b4&K;tOjF3W2PCu8fuOxeM!Xv5S{k-Fpodj}{EKS$&Ei zM}O~aMLWw~$AxjPJ^S~K6qP;-wUq-@2S*^=JH2mtypN%djPw3To-jR0G|TBm@GNBY z%Ur52%GkU6{e0_Kb6|IWeZ9M%dXDdEcztMhKlKR|@jorILSWY+?e3=tM^Dd*Do8>)!r&vAdvR^(n#;$i82GV+x!@0!x^&8I$G<$DJ5T)M_w)xsQzcsX19dRA*e0)W? zm(=cl9DlVgTJlL9GuzY=qF6z-yPqN)f$TeutXg~h@TXLB<@#7LbCTNKPY9lc>xp=m zs5<^FQNfIY-Ti&^?tY4J1hVgNCWNk|73}Ws3A_9E>)ri?;8|`K`Er(RX7PLd#jrVw z)h8C2BapAxBMWQD_^;9SB$4zaz{NZBEM)Z^M25jjKPd)#_2U<*-TgFD$kF3xT-kPy z2iAeR`>V}WyZdRRI0D&sLc*xkQL@9rlA&q7w8M%FM&H7so2Ih-g4UQ@gK zDZ&xR*Q;jmoRlU2PBFKOf0-14e^3;fH70QWWEgOq3=PEz_Ilo<&ctw$;FOvDZorSFa>cy#c zeXt^&Zg)RLIKo{=`rZA6;91Ds?<{#+*HIdF_g`bX`zgW^yiYjda6aJaB=OJ4BCuwe zA4W>`137y7%v%U21Ml$@e}E1kuio8Hz2ykxw&5I8`-;4~yPps|3z_j=vybbmOXmV4 zXjUKiC`qjT+`{|pEMyly+;|3hj=wndV0Zr@*xethclT2SS$#sd>nM6Qmw4(87xVS* zenRjpWc6WHP1kV;cK1(*-TnLZ?tY4J1n(2Li|tY_<_AI=dXUMde&A>Dx__rn;PvuJ zw5TyXlSt_qY@Z82ZRZGN!{Ke-MjJ*zh5lmyjh$wfGLbF>&q6L8p1Jz!rq=;9ytYA- z4j@b9dOHibd-%fYuOc9Pih9A~WbR8WQj1)7$55Ut}?LQ|3&qDru`KR+WqAlnEI)e@% zht>g{i=SeT6n^g_bI-6_g4}(d2&~v;8~~x09A)>)pF1Q zl>IT)`bVE6KoO2`_anUyfck-~KCBeH(cS$+`2Z6sI=m#)d z`vFjdBap8rH0-+rKY%UZ2T)D>0T6;`A>$63Dc!12@B?I$!#@#heSv(6l`Ye&3>pGTi2{m_vA3%)u1NdZ1pd=l@{3b2!EM&t$52@=Y znK9nXm^of%{4UZyNq}lcR=?lf26mRajyda6%>v*D&{6vVQ0*Lne0_ARLK{YF@B_#J zegK!X9{?eE7S7wLg`kcGKY&lb51_2}1E2^;;5^ak2j06}2S5m(z?Lx9_L##LwX*foX7(fcbHVtpgwg z&q5tQrplk$;{+#XNBa2tO-zy#+pCiVD8doQ*Eic%%l-z#ETcy;|G(iRfinr}BmqM3 zEc}f3uF?UF$Tiu2a*-tYu#!4SfFc}$e7#>Ss>6OVfM(*J)&UTLXCZe_e>aSxS_d#% z>i{Ui5y+q8RabrhQStXZ+nEl45IhUHd%T892QdA+4W6vqN6Ecf2S8;ZtH1oyIxq%G z2jC#S{b8B)$>_l{>*9_!t4|S*K)(K9%epoi-ss^(ww~Z1FkU+d5Q1l63xn!5*R9$J zegLJw4`6}z1E2^;Am@$WG^GRR2B#G?1V4Z;W0fBOA$S&Y+Z}f`mr4gv75o4qzz^W1 z_5;8%Cmlf7ylo}v06-Tml@4Gu_yJ@AKY*XK9{?ec)u$}f0l@pNR62m-7YA7%^@^8` zv>yOPI0E_lLKi=mN(T@bKi7%@KY-uC<)4?uvv{8jWAE9O)?fb&lBtW89{^nwj=(jb z@hL5&S~VJ6Y_PQrbO0B$4uB9m%YChWyWT=Bm{v?io>qPUeUgVt(gBp-I9QSn0CpTg zKhO_gI_Ln}gASmt)&Woi*QN=9D}|sPmP!ZE2>bwk13!RR?FT>zo`pJquNOr@Kkzic z@LM@#%XN`*n)U;r2uC1a53A===>TSAswn%N6!KNsNLx8T2%d#HfUIy9snP*_{4}2& zbtgo2fVCwa;RxjGu_UDfxSg?pEV-k#3^J7;0F}kFl>2`RsdNCf!4F^|_yKsd9{`RN zMmPfbdb%qeKY)DN4}cIn3v~eaEl_pr2S0$?;0N%9_5+{@N8ma&-P4Bg8T|T>h5wDT z9{`O9vifgF^|r?ubpUiPz>f9jQL-ZV0kqM602JW}T&-`DyC3{A{Fe@Z5IhTY0KlohX$KU?1oJN`Vd_S?d4@!Lv{Y@H%XqR62mK!4IGv z_yLsCegG8V2;}SEA2(Vm9l)g1escc_KUpwA`2i4uXQ2*Y%si@t`~bFUKLCnw1oHKu zFozMu^J}YtA3zQ80~n|M00_aekfAw#Y`RoB0Q3X!lsq%0G6zc zl`}zG+sPAV`vFkx$m)Z$k=+uk!$I7Da{-pZxd1+s)VTl@;Rs~q@C`uq<1L&EaPOwa z+Pg%Z3qS~-g*t$l3ufw8`7RzNzr8WT>NlW`&FWKxBap8Lh8KTv;941LhRU~*bFB@% zlpg>gcovRk*SrhmfBOMYgd>ozANJKkT}OHy03mo5)`19BM*#Q%c)<@KR{H@^gd?;L zKq?);*E?cmd(Z(~-q%R)ieDi~2Y_$IOb4J5|Dgl0Bbxh9L*tt;qTyN6Wuy!$oy~fY z7;3xx1FN5gpe%Pk&R0*AUzVs~#hSq`1TyX=+O4$FY7f6^;k!D3vGY`fBk*&N?%(Nt z1b_~p=f>qVuj`WpD8dn_3E2MJqgxdZCkgC@lLY4IlLQFCvrq>B{JgFs7W@G6{CpE6g{^E=#!2 z;3*mdegLPz4?FSI8{QwBTvv5X+ty0Q?Zr}&71pEO0r~LpZ z!Vx(4MV3P;2gnb=_W_+fpvvM|$ne&iaziTRz?N+HtZgT|iZh8yIq+ol9rzY0<$&gm zY~=v#=8#G`P_X_c>t|37+`L@XW_YPAUI((T4q`%R5qYIokeKxKaN7@nA{>F5fQVbv zkBj{>$YXtC#IAAbTmVAwEL09qYr&69`vD|rKY$7~?nzP(Z2tDKy?TnQKK@-P2MYJQ zVzsTX2<86&1fSHfs`e5T@m;_V;ANch1K9t8il7dlbr<#7T}N&3 z184?*03q5Bfa>4~WRxd9e2Vum)REo~fDk+jl>?t_xhIu!paA#*lmtJ3rP>bw5!iEl zyX(>Yj(!Jp`h@EeZa)C(Ek_{3@1WEB0T6;`;eEBc`6K-%+~w=5R&ws{Vr=K{*hjl>>y}S?+sx=aU=$oxu;F@p9z{K-Zm*2Py|P4W{w9R3V$y5Bvb)wI2XQ zI07{Rd8$6st5F}H4>qU5xd1H|sdE7c!LyL5#*tOZf%JX=us_0Hr^J=J&bObyy`+=_ zID(4dHCJ{qFT%M1-|BM#D8doQ@Z-p;)mpquz5va{%f>;r9{?eE7Op_j-woqHsS4)P z>WN~m5p4SbP=q6p;ja`!b%cN)z_;KBuu}U05Q1m9nd-;j2T%e00E%co0Ai{+0yP1! zuSoYpuf2+|wI2W>cor%L^t}L#d&(z=8a9D*0W$oqlmj$Ur~|-nDk%q?@xW_^%7Krx za)2Tnfeb&P;e?73&-_oEA1N*rQ0D>=f@h&}fJPQhYtC8N9C9jAJo_B?W;hX!Kuy5k zYw%fu>+OqSJbHbcC>~u7aUpmXAAiHR0Db^L;0G`~%@1ID3YLYOIqajhKjV0`@SU2X z?khSMAWNV+7l0xhf$aN*6_Yh00sH_?f*(Mj_5;8ODF=q%pJ->Ha^TNVQ|>4K)j{V1tkLHJP=q6RpFsO@ zz)#@+iNKhka)A1QI)Gtcz1t^XWk5B6JWCQFc-uJw`SV913hnp-2<-}}w4rA>s4J4AHW^$ z2S5m(g~|c&t=4srA3(JB1E2^;@ID#FWjGgL&D2byzV-v4mY@!R`h;vac#_-wGmP)x zT!1ccF2Et}2S5m(g*2M`H5fH~R^fFc~h`{d{V=v)Bm z2eSHd$-8}eR|i0C=LqEM@$Zfw0M`K!f@h%)037)B)lKgQKoO2Wz8>5KbsZT&2jBxb zfWN<0CkYUOXCb2;wxhqUgZuyvXg>goa0D{^IWG*+b&wyxeC-E72%d%f`S$1iG~#H{ zA>wq<|E%~pbuK{2j2`w#;kPJX+g^4{PzM03oO%R*uQycm{&;~k`?We3fFc~>?nnA_ z0SLjfPzM0shPqYhbpRCM2zNisZAyzXGq5r21}p?DtLg`ShO;N$=@Xp$3Vr|?*Wb2U zFI2nxska<~^}*R3`g>jy`~XTaKLA4TEM)bg>c{F4tPeVX?~+rkGFk^fWg)Bo_Zn5M z8S#XzDhpZt zrqg0lNC)5`EZE%-|K;tv$`61d9D#iO=-vu#7+(V7As}w0L+~tQ+}9k4)~(tJegL(> z51_L41E2^;AjAJCq%Djl>Hx?O;3w?|KnR|NY(Fp!8j%J30Gb^1lX2P)fFj81|9Ya0 zo#k$o1V4b0%nyL-;0WaFOSB7z@xXaW`f~vY!LyLNANEyS-Sc0-58!9;1E`?=04Tx{ z$nb+Nmac>R0O(u*Lhvl)=)tc~*YRaUCG%tE2LNnB3h4lV)3LLV4TtlQbRB;I8=mk( zteg)sOI|Lr`hTxcEpgX@^F)|a%fm3I<`IrSzMjxvUix~GIS~8+7HB^JYA?^iSva-O zFv?GxXwC#bfOXmrfFc}$vp%OE*eZT503mpmyXT1DegIBcPPwQ9plfIt8FE)PGlL(% z-`Wqri8u)4>%qrM---3%+?t}`2XI{b0T6;`p$=g6Vl__ho(n(`jzGSC^S7<-Z?It; zI$X>@ANT=e(|!Pi;92@pld}s04j@Tp$>rl{;m#yA{>GI z`P4?idhRno`vI)gegK5vS*QcRYp8Spa>WKu9?$`NuXO-a7P9)_-34QybN~)wJTL%T z!4IIX_5+{@M<8GS{y2rke!Kku2*ISeloNJv2KY;Dp4}cIni}wl6+Xg>?```x< zsQm!wns5ZJ=-{_m=>V3F-)NP4FhC}~X=nQZ5Q1m9uNCHsDejDmik4uJds;xU`vDMwXQ2)N z9F$Z3+Yf*u9D%GQ>?=%xX8_cZ-VcBfJPUOIph8ShI)L=&0#Jk_kgs3+v`dQ80lZ!n zEC2mpsul51wCx8#2%d#HfYpnsjv1AD$u~1kTl;+4+b;hU;RxjGadxiw^U_bdO24^x zt&u%YtLBu&vyg9TRkv4)(gDa$NpjWk=AKapo7+3@DUu$6vvZ{bpmPBN^tk|3JF@zl z2KTgEf^|3uIu{^Fp9??{jzEqL>sI|RCJmOpa4x_YeJ%hYcoymaek|Tww<^6K07W9egH$k58$l!1E2^;=yL&5ln&st?XfawW2!Y-`vI(8JT!%L0Qgo+IsklkRtP#5 zV9|vLo1>=)>Hr9VpW%!E8}0Z3G3E?taj@0NJ!30M&tv`yYR+mY@!RTI=Wl zzJXTp2uI-Oh+D&SKN3L)u#foxP+2?+Ir@&fhNrmw07_^-0E%z~vhQ$mo*qHc0noVs zgy30N$48f_4%7jRWI6zfa0KcAW-b_&;^+YW20wsbv>yOPI0E^4(7NkZ?F2u7ePwEx zkF*~EA$S&Y^p#?gQj`v0U*$OYX8%~Tta2;}SkIZJh<_X8jV&q9v==T=n5An*gI1%3b#+7E!r!tZ%d z-7)s_!;N?begMb858yBD2S8dR=?oTAi1<{ z5fNUspY8HbW#Qe2Sy-+c@iKp1xdQwECTTwaDvKkKeaE$T#p*xFUskpNm;dqF<)08d z3t9coyG%||tbTfze~NGfvhQ%7pRS{1I+uS!@GP8PVXNG%{zlE}Q-mXMCW{P&V)ZwH z%YOp6{Fm0OJ|TD(vag`tOHr)8Z`Ehk6z~HWtNj3SWt;}zBE{;%$$PkdVYB-8-cCtT ztbTfze=3XDfy}6bNbmAb5spB<{>~}tM|zikLhvkP^{KUnkz>aE28|CUi7p)~+Ftk3 zvC~qB)dwA+y%vg$JN{j<`gij7ww{9@z|Eq4?7jL<1U>r|0$KgWoo3*37uLwOE?Q<4 zi5)DOz8q!y0Z;@v`mAGAmb;FycCS4@rHmAprSj~jIyeH^UT}25`xxqIa%HsV9Ju`7 zdFV#)EM)adO_-LVSp5Ou2QUl#02*jN0O~outKs!gZ*)$d;2f+S%PhZ%jvoN^mLrgT zhtumcdNKF`Gz33@q1q3C5IhU-tEAr3^?Pl4mw$?I1hVgeH>Rd2R=?gq`>oiueZ&V| zwa3#|S>S^J6MX?_5zES}}Q zcX4F`{Q!DuKLEP!d_0iVr@tGphEB8B84Y9Zv_Nwq_yJ7NegJUFslDom>u|kh@aSwe~t@SE@N3PE6lEQTYK7f@k4+BHkscj)osqFbjbnz!vQXKoO2W z_I;$FI?8|_z##AgIIaBv2*I=5EOI#b0n`RRfc4rBfLLUXP&@q<2aV&W`~bY#4}cIn zOZfp1;+H|n51^6u1E7&YjvhY~tM7~l)`6`4tFM$F07W^%4M)jzI=6B!+FhctQ z5Q1kRcTXd07Hu@hoKZYi7Hw>)1Qh5Iz5n5zoel+b;hU;RxO*SZ@bEeF=X0 z$AE)GzOGWt1$&JZdFSKD&f32j$c#7J_OtH!2cQA4|JRPNbpX_JWc4RLeD^BAezn52 z#FYVcMC*zXwhn-<4@V#y{`Z=T`u8JCwg9olTS5HQwT%nGvye-_`q6pas{U4EQFBFE z5xga{d?XXbN~ZE2k`fp zwk`zELN0xB!PEL$wNFhD(a}xKUqd53$bg6KIAPB$)b9Qhb{6UYz^T;!yJ2*z3(xh0 z5Yr21fbs}Oxa)}eAy!2FkZMkDr_KeSvUnEi0K7-2jt@ZxPzrPaxh_Q7egG8V2;L`0 z2e4b83qbwAK0R6eZl6HK3g-f>1mES+uqTeUogMff zp$-7N)%DdS9Y8{7#p+XpBap9;jyxlg{R`fGd&5>FM zKoO2Wh97)Kbsgz-0EFOK$e%w7IiwL!Hh??+hE!`2?7Zg@nsR?`SEadC|_m1#1VuQ4_sK9iR1OuplJd!%?K|4f-3R5oc0Z7p531|6`?0V;s%+HX zfhC&|Bs^V<$b5d09vM&m3Wi^d&3kr{G_cs32r4{s12xF*U&AKw4$ejr~TJVWZ=DAECBgmVFS z1X*@M@IJx3=dhosZ~4h4=@2{%bqAZjr8++F4-y~9BJxE_4~x?$Xe^9{Nm z^^;ld1-trzYJtX`-t7}O(45XC&h8AC_qEo5+RhQXo9FkA5(;e?H9pKKnvRN;#}=y7 z3JAfoPzM0-wEAw(4t@a3!4IHYHO07}ZMDmu*CLmGGa=c|LQTNfdAseugVvz)C9}kD z-KF1e;cynLdX|@GSSWs-#a6IHQ#VuJW{0vrUcYX<>}a0D{^bXOWir%pv=WMYtfxV@8o_5dMx z*134pOzhTmbgzEg@>J?ASHpTck8lL8Y|}mMCY zfm5@{B^lb=Ckar5BXE8H^DZek8-%O7cx_FYaJ!BiJEf%y!Lv{~a6aCvl>^lZhs%At zbIal%M0$9HBar9BIhi)ql1kXa{0PsXwIq>8}bGiIYaoJ?7I!S;c z9Dxjf;0>yy+$lea{|5|L4g!SWS;+RMuA(}~LExZv5TFQ0Aj4nmc2IqNc2Z06J zL4XiE3pt(b&-ZKP06GZ7ymu(Mv33xkNP5JG{0B5gp9~HHg}_0eopunQ+EEA4Yqn|$ z*5M#}H5(?A^M7fz?yejJD8doQo4M99?I1u1o`s|N&AvlgIdB5BiFM`$ zTJQNOH|_&Q5~dbz*g}HN8slY?T+c5lY>A`?I1v9@hoKZ z18Sep%7Kdqz_b7S8vmRn!#q5~5y;o$+KEzG-2Xw1V9C|$TmUMIXJJXWUZT_qK z0@2_gP*pn!P=q7!84RbvX|2I`;2=|9gBvYYlpS)=UnG{!r{X z9%Y{-KoO2W6+y#m7qkN60yqe?00)5&lB&26JPVZr6>9vVwFWi8LEsoT2vjWF+twOT zS$OyD|KJz9TsPu=WLrhwNr)bYRECFS1V1I3E9cxsP>;8{2$ zLk0rYQeZsVE-fNU!~}``7enlm%qYSUIQKpE>XM$lj$fQXF6a~k=VrJOJPSGDGo>$S zt-%jI_pJ5cAW#*&Kass3-TQLN{3bd5k-J5v3Ez;AeSIBvNox&W1#YrJejgyN(<*gUkCtu=5WzVMr& z5U4c>+o3)i#)=-(t<^h+iD$nJw6z8lDbdasJmLPcj)8@&o4~Z+9iyT~5;)P@fFr z|L8gouqux3|F76UlqR4c(gbTXie1?quDzj%*n3p4#12>idyk2|2V;v3OVrrd9j`U^ z-i@&)mc*DCHP-yk*>=zP{{HXt_&mHh=d*L~-9582XU=?Q)z7m=V|_+0&c;b-W5EqEDqH%~(B5%L}yzJ>2W)wFcJnN(rnYm|y&sS!>X6 zCf9;NLEu4VJAt5H; zSQbiP6~VFQ);;h2AVC?^Yc%`ItAapKFRV4dr^iqbIQ4w4vf;N>7F(s6zBjzV%v*Y% z5;J$V-r$p1Dz@lEAN~FtbpZ8$@2x1GjbcZBGR_{rCj%{%z?uMj7L46z_|5A=l|>3v zS*v*B{hVHldeL=tP(5vOA@=?3 zEY@4;0EFvKVdVfuD*n3S{lMSq$(k&7;EqviVBIN7U`>EUOVU@MATR?I1lsm86a*}S zdSR`Bb!R1M?J^(blz#?8&1Gt#1XdB`tzdjYNoocP0)JK-%SsJP6bR}??>}gAEO*m> z`U+}6PP79Z;li*MUmWJ*k7ayz%Ty^2S=zgD_kBg6w+n<(*-ePr8-5vl`cfYyw_^v$ zOyq|eb6=-hg@Ou>;V`?6fAj1DFt7E6_tZro1(Jf&olFLGgdOdt0Cu0%UUN2F~{xqD=n2&ExmxU7etvCK_*NKh~Q&Hlb%wej}aBlYZm8GO&eM16l0j#_avF~;8uw283VejiJ^Uj!%ztCn14odUW5;I~bL^Pew(gT8U3^hUd(aT>AoL8>LJ7Q2ct4C9$k8!j zTCquE`5)Cf3k3C|cT*=sPdHy=O1~(TMbo0bKz7jPdMkt_3jc()^pPUzU$jQ0ztj#uK;<`L%X!de+Ka^ z$Q02wF!LXjuK@0Ulc`N=VDh3`R$l;lE?%fLAy)}G`4f=*}g4=E7> zLA|hT>lcxvtgr5AMPmB!@*fiQPi0vsfwKvWL1PC`_3vM3yVM^1M;~MVphZwG%npxV zP!ReS4Sp->?MrQRi5|R1G2`rI>spvk#*7{27DVDd$R+*uHTXs=QWB`_C`_ru6c zeAu>M>vw4&|Fv{WfuLT^3KxQkt%5KT$4@onv*O$_D4w?rZwseqyDXI0KFl5JmI^w{ zb@>{8-J>r9_;l}9eE6$GfuLTP-!Iw89rnK-fHv9|3+35QoABA?@EmuSg%Y?nfwO%$ zj}qGO=u?%S4z0m!dv_8D>J?qU1Fr85Sx9VSX?RWkeb*SirA8-}S}1X>IVu(6jQe|N zqv!nqo)+7RU#?R{AgEW*hwiYtmbK-UEcK*p4;b% zC%Uj|RSQDRej&`i0@3Sm3Lky&s-Rl0-iLu@A4K8rq+h zg%X{67la`BB&Q;KTF-j-|GOh5#VUchrKDxpTWt! z)Iy1~uM5K1!2|R9(8iDz3B2-&qWqeB8-bu+McfKPw$>f82-~2I*M}7zQN28ugA!G0 zp#<)n#OG|VAGVR6(}3?!D#ar%BnbrdqW1>g1}Rnes(@NBt51SBcime`%y8h{!^(Ti z$xNtOlLxnm;hP&_U*aODmw3;&IQsKDIj6KJ81K@1jy?0Uv7UGelyLu{!W33__ig`9 z^E=X2rB4PWaLg63#h~G|g6Apxx%XAgdvIHUpkDaqiE&4r_~Y-H~shG}(Y!FP%Pz(jfVXoInA@HLqQ-Zld9BQ*TF%lAdM4Gn7HuOX z8S|n=uqF-&pTui}E? z^%6hTv<+Dt-v}StC92dy3H&>QS#6Abox#gzKT)eXlm3UWyy!0i^(E$N#*T-bh2ILj zc=rGPZlJ4{oy8{?eYd~*md+~uMJa)Q7rXA|j4qHzfs?cT&EWekJyC-yw)r2z^1^rE zuAjX0)5c+M_^C`db$XrrF@HNf-)>pxb#e6sM`bWeg*FcSlF2V*WXl0?!YaKM^}^rK z^Qt%8lk;!~8|}*ENh`L>J5t)}zm;X71pc}uJNZCOTz+Vys!t}*YI0xp4`?qC)C+qh zoJ&DvD70~R^$0%bUWW4b?)G}`V_7IcXG@aw=gqPFW0B%YH}6IQLA`2p!}ElFpi1Zh zw6P>7owuq#MQKvE9lSYR7D|XSuhpm0c*d|<%JR3J^z6AcKf}?g^Thu(SDOHO;r&O? zQd+(1q|$3q0>{8X`TX>m9-Mx9d?de~G(b7`qMblcuf#nC^tmCZ$HMpcZBz=+$#Y#v zQrkn`$7P|!qj&k?eCMKO8>RO3;iVhjQF{03BoNdK^T|o~eW70MGPE&kXMf&l#|b67 zYFj;rY*{FAJ`%@0exNCH1&F#pT-kX-*#HE!P-0>Ig0PRp51wBnio#mz?VJb7rtCzO zS}1{U&T?I>?~yjIJ6|mQtNguRB&Zj@;cmV0gKW`NXyfU@c%E{s7+ce@Im~ff7D~ka zQV?nfi$d+|H6U(pZqHjY4|eMN4p67>vQXmJa|K|RkiYrsdPlV6_o^0SXFH-^pNpVg zugbbZ4Ep0(0@|3kBZ-HdD8g1{c7ghKmxU5I3tqR(YGdi)>U_yhP)A>*jX+SZ)2H0w z>}7u#1z;PG<~8P78^hTCoW77Fb6pE-4VE7^t}7BJ57gxS_EljI_jiDKlO)arnoIXD6xDLJ1ttVYR%`#{0_w zd|{bZZ2t4s0ztj-?T=OeJR94%yEc$F`=%w!RuWZep#+YUu&Nbu@j!ULEWww3(~RAX zZVxr|E(<00hThs)r#pIj^MZHVu;l$BLA~;~!~AZ5nfJ*)TZA{*mBb2E#~#jQ zp~Rh59uOZ^8*wv!)_xqE%;M(73k3BV?B@x+8=fJC-$Q{jixY>mVDBmnSE<*UAs%`L z3g7J_k<;(K_QRXr?8@IsDz#9e!qh@ABgRwNUc%csb=Fa>$D#r3fp1%Zpk8XKCyX5e zQ9l|!RrJV${KnA^tkK%8Dz#7o$J4m8z-VK4&*NJ0SN++on_UEgdg0g#>!6J`R*#&e z^|_G7^7m~6>w_)}C2$P^@1Q}17nrX7*D9S2krH7<=4!o&naae+F+EsKqDG!+QyC1xP!K@~^Fs?jXFRd1DAD1qP4%4OonN|IvW zY|C6p76|G^v!9YQynSSB>&;oL){|y10`LOe(m>pA#coF#e`79`=18GpYuyJmbV~+% z`+E|s_PMgBl)#Kmle<4u;L)jx;wU%mx7Yid1#pk9Ohy!Ci&au@rn zVYhF^YSXh=y~CX$qFfeAU|zgq?c(}eRFdlSn<5`7n#G!y>mm@;Yy1Llm=OhL<4wgN%DwhzPg4?JrW-#8KLeH8AQ z!J7Wl9ddmx3nj2^%!?as^tjMf89HwiyHp`jAgEXDFWxZUw21m0+A8zhN3#u`+rYcl zWw|%nM~{h~56{W8ZAn^lsjISd?kJX+w~f#SC6*ud(W4e=$bJ+UslY$>Hb5k(7v`+7 zFEPG(`!RpWU;dH7iWfFkWh@IN+Iit=dBtG9b_&{PJ8YejyK5+0)wYwqj$;wjtNT8z zj>mIAaU|n-QH|k0x%0hTYN5n82Yn$&W)Z!j_9*K!2eGB)+retM%R-5Z@qUo^DTaHC z;8O+ud{)^$ryna1X9H4#dc{@ugVAfCIfjeg{y-@Yd%c?NNrc&i%R-68gZy9&XSK1U zgBR0MlGvr2O$35^ZT!m*@}dFwl)yJ%@Wmr#)}~&pf8}1V^Tl;7o{cKj#q_L-NIX21 zkL5}2#D=eF3u7PGwI~s=1@({v&3p+D&Bvzw+==}K8G1@kuYcPVg`9qXxhivT%LgUz ztFFv5e|xBicUdSg@Hf#uK68o+o%o#LA^TG_J3@7k!fif_kAsH2xBQUpNhI)EHlbdA*8ZtxtcU?-{i$ zlqm6e0IW2i8uT9c9%mkQX0yL=WA#GYLobTw?H1LuPTltfz%S|tad86PC4tcI`a{3C z6Nmz=3;Xs|9@b!rq26j)D6uy*05UpwukceXThW-6`!S4F(Aw*Juq=Xl;ggOdeWQ(G z``fWbybvo6>Xg(%3GtifPHVw#_Y7hKKo6M`)N9I9R9Us!Xmhh38=V!+e)VXpQVS)- z-)cplp)f{VuhhTXR9)3D5b{U9dTtW;rQ-T6?ABxSHA%b&wKr zS|q5~>_vgF3&I;Jcra&~U3oa`+hBz*%$p zSH!kJSat9+v*!^|lM%6adF~5dA5Ix_Sw1ByO!b1O!hHMZ+l}4J!!9<_3CltW{CnVU zxVH#DRYj8+`hO6X7q*SLMWc-`dKzuCNSO`ec066WF#IMH<;@|1u!7->0= zg@wv07y1o>9d52~i2H;a{b+n!@q6T1mc_gmt&iOt+(!SctZzsO+yRUkR%4B&*^3PJ ztmyA*t!5%Yy>PE3W+PFfQ<8emHGZq#PJOBGeYGre4Q3XG zOggqv2Z&Z@$7!?fb%Z>e%R&k9e)OC%jKy#HPV2cGXLqir1m|gZ-A|tw*xPq?7<0Rt zt!0i*Qt6XHiE34GhqNd34p_zN`%N+%^5~Y_RX<*azQZO6!5_2-NA{ z&g=#A50`}!=Oc?jrq*hs>hUD@_*M~q_N!U~LA@@I41js9)yD6vH{`B<)hOP|1L@S;N+9P z_z)feb$!%AiId;?>MOiP4a+n6g0j-9AD_1jxXxm<(RILa<;3g$ywO>#j&@ln z5wXn=dQhv4g0101U9UTVdF^_CwxnbNKf5MIAgGs-4Yu02KCKEH^Jh(- z3F<}fChVfSP=r;7-Q-)xw}$tJ>uI2bc+X!3 z!g=1ymdeQug5=?S%Q^nOP!dKhc^whkN;_5tmq2abxX-4Rc9i(MB#eF9$4OGtx9NP& zUz3z6zgLnUy^D5Ks~UzH_i-7loMYf`VLB}mm3%>+^7V50*JeR7wNQe#4VnWbQuy2# zHb0T`)6M>IVCyLvA=1Q-o}&C(T*Z+W%M?9c4CyV%|9^sd(N7C?ug?;B^{GCr z7v{-q%|8dy|GL`3H^k#zAWYFSi7wGscKjEJ0`6=%vyE?F+|WKRk;3x9J*O5*{CY0b zQKMUF{dt3RgZh&-xbF<_IVGr9*yJ$B$?vQ-a-L7p`o2wPGvL{!7D}8x72+86xU~7S zAJ_J(+YV;38WAM~f_f$H35HlHqsNLQO%2Ia=kCvB(S1dNdaW5!5+cp|RO`!e6?3Ea z?`E=Qg@YZ|7zh9M*ldh<<`)lf;9g;)7H7l7jq2F)vy^+y(%2aPV7QhsKf}M9752kC z3_B7Y;&}AV7xp#a%xiM^CN;j|1LbiAm>q=#J1|#_Ybp5m%HU%9>cXqCA&y&b0zqjL z^X+p_Zc>XpF2<_<*@Agj40arAUIH|n|7LO3gB<}|O27)=OE$w1;s~2u0x~(TfN=kP zh5BZ6HHQCIH4N5!hB`alda&c;r6?G+RE&F@ z84P_?G-M+x$2FK4;`rx7l>WCQHQc^c9h{TK+`XqOvlj*H&rig*dT~5E7_M6y#?p=A zpdH8JL8W00vJtegb>2Spe%}nX=HzXedeJ*55|e7~R67sNV0D(=1^rFewJ1TK7#PMR zPu3o0rm>389|#2Xir5zFNV*@be?q9n-8fN;-;%~|HGCvf3nl1t45y`qUC~w^NoL#5 zKM)A&l{PQj5nUh}X2bZ+U!+~v&Icv49!r0gsf7|W9^rhoIA1<7v^AUZ{Uw2*UKisd z9q>kjwbaJYMzaCFJUw4)=63F)Of8h4{R*ta1y$c!|FHY)B;;L*eV*nug# zWon^>`2LtzD21gEvlunZ966_)Itdwn=lhzIbLgeQf1RfpQ;vQq5Y+2-RHWl=W+dbtszMv1R(taYLCsm{iJ=O$P=dy$ zBwb3a%14Z<#Bw~^3k3Co@8H-Q8UZWf)uD}!QC0cHJ(bvsyd4y3p#+Uhs6#%{iXUfQ ztZ=7V0zti?zlS$Y7>qJ%LK~xRwdQ^Ada|tS&lPH+1dUC2t94J|sm`;?bJO2Yi34zv;5Et&U@x~N?2g_fSp2N(8le~SG8#Yt=aC_6AHCZg2pDSYhUx@fpM|y z;GsPNLA`?VMLOVW(3eC&8=W(KcrKIKOy69ES|~wd6IR2!RpnJ*MzZ&L*9rvndN#I{ zLn#ynHE~hUMw@mq{FmjW*pK(tDbzv<8k>-HuF#54s-2%5nKn}(s8^kxQI39dO2YfR z475?|SxbIoXnvL_Y?eYTl%TN*b7%JyK0eZ^#J@}u2{?WocY7|p^OdL?LM(D&)weQjSQ-pdzy zIG2SIG&UuvcArc>VCV+<*2^U_C8!t7Rvg=h6^D^C_U+SaWb%Ob+vFO_Yh-Gn1dUD5 z%q;o6x?^PmTYT3~A5&Z7XgXRJ$JCAdcdF|B1h%_@AEOpZ(Ab2Y`tB4h|F)*A%|tJO zpkDdgg~E(A46^i|@I49_oS^mD*_7>E?#-x$5;QiU>b}q=ZFFau?Kn|TAgEWFD&Y>e z8sw0RKpP!)oY#s>mf53`?yxV-WuXL(O~_e7HT=>U<=NR~|0O9K-vTgw+fm zXk*onzI;@Cc~-IaYlT`UL1Pn?jcZinXX5?Xv?o^uf_jzfQpy1{BdBEchc*V)jp28c zqO8n=n+mm1g2pDyYlpPtLw7t^?&a(j26}FP-CxGhs*O zO0~<^Raw_A)rC1SC1`BI{*S-LYbUQ(WcAxt76|G!R1S6Itxy80cizBkyO-Z&?d_|I z?DWdd8MRP?#wM)FG{367zZc0~%!?KX>NUT3xC2Jau=?{6+Hlmmpsjir$tss913Q*o z7D~`agBkrBU;e1PAHzykN>DH7i3o@DL=fn=xxxK-z1^FCnCQp;_6TCsLJ1n1P|2ZH z=C#z1%5Oso2n6+7Sicm^af-vdwg9y8wpmp^toa9J!=Aj1S|~wd6J}lwTk(n0&M2qq z-%uz)ykDnvXq?Qy{(7NOYQS39>Fcskg2pDySPrJ~pB#0RVgGg%2-vNW<_Jz4j3F_rtv9#mt>!RiuE+#&c*N#h- z3x9E3rWQ(wQR_W)yXxD>mqmTsTF6<^%#@hFYV}~Zx*@}tCG>2~sD%<@)E3E^q^(Ua z%(BNc7YOQgJ1P`%<5)3r2jZjug>l-03Wb>x5A_+uLJ1n1@LN4NqqV%1m(7^y5D4lO z;~wt7mCLsGA3__Iq$}Dks4JS^n=xvk1dTMv&~NkM_1*qdnub>r2lmy=W{u-s`kk<+&s8`D`q8z<^{GqNW4?MdoFSO*3Mz2*o zTfSGQg%UJ2C29Q8-h9vVbmihb`ZQRPDyB4xOA`p{1$jQ0wx zKVLMEkDWd)Q-XTsgp`K)IqsMbfHvm#fU#&uBRSvYvof_%g2pE7A57h(dUd?2%&woJ zXPvD~GtDQ9S?4*=e^9qRzp6~|ND=bUl%TN*T1bIYv^#sxDYu_@7YOP##y`}7b%jP% zSe^M3wKC<-Da(iUfKTPJP=dxL>}gndUXzv|Qh0EZKv1u-~lZ2Ubze{R8vu@7;a4N8v5XnkR9L zS|~wdQcg!3X;G-=K|6VO98FMHeYGwwHx-MqCz3 z&`6V{Tae{E=sQ>`x~`x=P_N%xM?v524HbPaaH>Krl%SCYQJXiDk33vl-v8&{G9{?j z82{1^nBRahmp8QW1Qa7eB1+219WR5ngv&w+8k>?d^U(oy2%D&EYLKq46`#chS_@X;)lo2~yz`g>Pg%UK< zV5fJT7~bpa>Pk}47=fT(gZ)au%D5+teSU^EO4g{%2gX!YuGOr@sD%v?AwVFg7pyEp4%rjVYkUH2NYfH{iyDDSo=x72S|~vy4aN-nd-LJHK9CFSysc1z zdbOw-4Xc2j=KSOLX}$Ot?2i2O>V1V;C_!UWk}@Zx@n3sPl9S}G1cG`wPeeO5{#D4? zp>{rqkI~s_yh`cG^6ZpZ3bjyzMj9yTvy3XKp5>V=VpZQpu>b$zKNCTYu_ZIS=!f<1%F z@+skp+V2mX)6$dH%HCJ|3IvT{sOZq!Fcbtz?>(p0Xu4LueXTE}7D~|Al%$6vym_^+ z=E-wwwh;*Gg^`BequBK``0RT6@PUIC$i8P1^@y@8pAxR9ZN4FfUr8Ge0!K9jf<`b# z8eYlRKe(uH41XDtDOYmT)FaBWd`h^Y_EMgBKJ8o^xz>T=0zo4f;|8x}yldy=wBWyH zC&&Y<2k9|rSw1CPQG056AHIEIkes;St{#IHLE{nDxb-%S75C@sdh_Cmh2@Z5UHS4GY#TVcin0XM1dpf?A38aaf(Qzp2p%ZQHJ4 z;aI~$EtH_`LDt!*#o<_sgX`It}SiKLqunpVqEI;#h@5EtH_2(5`giSm{JR z3H^jv=~Tq6l=-R58ZVADUerPfTua~IG+e(=c8!;YHC~jUURdJ=>zrmAP_x9bb_wg2 zsD%=^mY$Lvsy}abt(u1SoD$RvYt>c;hna1dHFp|5yVODnTuX2HMTq&dyBEviU6(Ia zi$(7gp&-7bt0ds&S1nIrP`kjKn`=j9Ta~ z;J5dT!@oiMLH`>gxXE2N7`x|%IKMFna<7k@)lDpC^bD5eQvzwD_t|kEmDkG~k-Nfe zq26Di1^d3OjiVtC;|5RVYsdjc>+KoDnzkeOv_Fc*mdG+!;P zM`7#uzQgRtl&5)M40=<46Iq`Y_Md;B#PhbO8Cz?dmtdj93`Z2K%D6#%;0&^5WJBI2 zf1TLhnwJs?>P5d3tnlWm%DeWTxc}`(SiAO`$^Islc0v}AoM9A5=&`7aJfdN}S{w@h-hy^oqsJKjIks*PS#H)c1S zYy|fmI6|)-Joz2w-0Q_*O%bk1 zH7i_CuN@rTHyR?$0X^L6Ci~S%+XwMH9y|2<#9ls8j!HjNjKd0uv#(3Rv-O!?pNJJI zr%!zbM1!Pd>VL(0@T?L4=v5Xczl(tEuTcZJW=I5#O^U~5^$&pzeT3uCOI+{d!&6UH}7+B~Dq5@*Apx31S&PI+1q=4+UFBhAr=i7I`2!Luyqh8dvLjYX&s-7cn zQ8NJAM#Ynq#4YOY!%u6&M)uRQgP;rx)p9@Se}k^5{=DK`e+KW zw3@$;gy$;Q0m^9bTdDfrlMWPu{%XJeEP%o>)OO;YAL+?zwPL~?wa7H1Qr7aKcM$Kt zi%2@CsrPqGQV(}9W?q)%Q^NJ^I@gcWdgU3fuBm6tyext~SNP;V|Gp616I6R^+9^v5 zdNn}}?{370W%-n_q6_+xix;#eqtex<$BdbmMbL=C2nKBnc%S2MRmuIl_D_dQ^-5_Y zYAwsB1h!$!)i%!X;a;!%sKKWb^*FZ(+Dl+h1XZ~(mP7TVF^j$V!5xFuSt)Jw{>rj^ zN?;qtTy2&Ubk(}Rc`t`*3Itx)^1?nCl;$9+Fw6Phxfp)_O+D3dqK4kLTb54=q>Z_n z+wpjQ+&5C){k)h!;B_r8obOC|>H+oVkD-mqLGgTha78uyd~y99Wm!HYkT&LO)q|7y z>aRZJhF|?fpLtmXUf1%%_wM@*9stAUJ31 z?vQ=ClbrLVXj6kpAy)HF;{!makH8~skb&Gd!!KC zG=jxA&mFTto$#Tzwr#~oJ)dax0F)4;_DJ(At?*Y}wO;Fn3Iz4Sp6=T}z2Q@#KHSH8 zUA2izhUz(e%R&h;YPUYSsCgY~soksFOCYEh<{TcZ^@fTOJw6Iv)DHg8N^|So zOV1}-7D|Xw>$As;?}>`l(j2V>f_hjkqGTv2@Rv-N^5@OVr%iofBU7J^H-pOAe zs2Aq_K%>P0iZ{^4m|gKa&+~j*>mU8~RRGIE2{CH-ruN}gFFRH5(?9F002V>LFy{bT zEHLN&58C)CD48EAuut`wepAmUS{6!(QCqS}Dqq%XxVqVYp+HbC%=_&PE#d$LZHSKr z4O4mXBg54-kqh-z0LwxNF>1>W&*ZU(rsnQXzpbwVSOoRLT*kf8g<-6Pt9?NuGI^)4 zNx7#-{;X#pEej=RY)VpC@L|=l{jhe_e}MjWw%*8?)3@Hcxc=pOJFgwOMtvdwq~-gu zzwq9sgc!94D-Y8?gV$#g=sMABQ7>Ho0)+>CW&wGIV&k>R%QWrThTi(xwq>D&7`0DE zU)DZ%|3P!SZzm9k9<+s`LZS=8M998GL_CIpxI@j;+!PHE6)7g%UJ2C28M{Dm>@(Ol@F?@&ZA<@>VG2 zSXjTP!&oJoqEzKySD&t3>sO9Z3ngf5!oG>_@!YxV3vKJ4MFfI+;rbWs2sPtF9@dh# zO6jWId03cH3nj#;efYi)Z{0Io`#Io(z8Y>_i+bUl6L!)%RyD-Rn*1C4@B(`ywRMFr zD%3&=F>3n^05!z4qiUho(*%NgVcoOGW*->!VI|=0-l=@%_ST5C3}b z_{QyoaWt-NTcc%hO#Mr~J!<2Tg?L0@J7Kg<2^wjjEz)qDR;o$?{-{fWKv1u^>Y+w zJNv2@7WPJK^KX54JG(5Dps^`Qk-NNk&~JCO%du4jf_mXh7__M%mxwF8zt!^LRRZs7 zTm7r*nF5!^74tMUK?|u}6@K~RL2dYz5P_gxm;*T0ycpbbTwUs!6~nLfJD|O&AEM_- zEDI%QY=Vw(@dSRl(o*eK+<$sim_<;p?ZcuVHUc3=JfMxzP!;B#vQpdKJP)H5O3>JZ z)8(7@=4Vd~(dPbf1S;ZP*P>on2Lfs_4$#DcHeNPK=7Z<-&~^`S>NQ@Lg%UJ2LBR}a zycQ%C(%PQN5(w&rbs(|76oqFCt3?j2OyeqV;3Ibz&HppD1xGkD#*9n?Bi9_dwKmW2{x)Yh7}O#P=zB|c?f9epN@ zZvmYB;=I|)w zQlEuO(!u-_w9ikK=H36R0lLR73ngf5!m7UCMXmDm5dQVAp9uu@!g|P$mqHvc=Y`*D zYrb<@`1q2%>EEB})zOxP5;Qg?sd;a2o-fjiLrJ+nP%o^9gsOW7oY(|yoczLvSB@{j zU)C)Nr(L)#ln|r#jYm};>;FRY|H)k-s2A2l7P}q{mBm=mc(8K}@37<_t=30(MlF;O zqt-btfv#x)$|nFcWtu1i|h}e0JxIN#Gkl9?~u)`~v&DToy{uNP`vL zg1vcm|3zB2UE2hLdSN{zXwbp^2!=L%H}vKQHqF;|9NVhbI$IV>h*A3(bc8?tURLWj zs=Yu^FC6tjwITG0;n2n@&=G!cwuW}Pd>4I{%(75Ij9QOgnS6AC32M>LRDE`55!7p_ zjJx{-;Q5V)Hk@N%y{p`CRZiOn`rR%ICB&$mb7rm@T(1S6RXa%E!)xWNu-4hiOo{oc zoCW*TC##$BCEJ1_WA4gqQ9_JbDQB{_=~aCmJ3CMys28?%`cycK;ez0MRCzu@^Ir@) z@ofVcwNQe_CahxYJg2pmYx4(-yaa-J;W`m!Q(@0Dw2>8cS!;c{Cf{@fv*#`gCB&$G z2|47aouhfe{5%3dy&k=bfH`d#j3G)w8bHuIh~@7G*~S|~wd6V9RwP39}MZP04w&J_sig=@ID;@iuoEVLn~ z_vXF(Z`VfuJx`$)N{CVW{D(B&B)gqV^9Pq6-AU8X;zDZ@fz9O=~-}Q^jKS zT|$Cd0gKmWD(R0_Zxr)A>>{wLmLIZ$mIW38m~49)bw3KmW2{D zHYF*|^G9`}+>1wa_)X6`TbX9u^Mf-A%qQdiI9Jv=tu*YG&FRGl#Qv)9eYGrVvXd3zVG4!SE4)a$dUq3}IQIqtNo2;bvX&#BrMH9GT|W$r7~LJ1n1aOy&@i`vg~ zTJlPbt_cM7%IY5uyE!6ZO$v8y9s1*<#!_1H?4H*YYM}&;O-WKK`S7%Jb@-E!M+Jg< z<%C4Snn8pkkA(N5n|o2dxmzvn6L(CZ7D~`alcW+?VtCN3a9*|7Hi4jC6Bghqbr_6I zaA)zw*)cr)mvBC$?KXv4C_!Trc7{D_#rIZp;~}Hx3Iz2!eJTpFsG%_4tp;sWNod7) zN9X0Gde2p;g%UJ2VddvVAO1)0gW86iF9d>mRr(d+Pp_Iv|AIurSF8u$BdyCsZF$ZB?zwZJLM@b_u?c(PHeS=}E$+gXbpBc(s8@rT;Sd{9 z`dS#&I(NCE&1=w=Pu?^edIpz;5;Qg;TU*16yEkaYd#;}(5Y%f`!$=q>MndI4efS>L zulewDrJC?cm8K}vLJ1n1pyZiQm4CbSGd_|J5(w%wLMsLLBLd>10k#nW>Sc#3@RfT9 zE7U>>8k?}UuTmnvT+f5oINU@asMp*-qF`>0Dl`qDjk7rkJYb(EPg~JQp%zNe*p#I1 z5QFKfFKB~XhYAGs!YoD7{gM!ajiHSdnaMoA*Ewxqn=n0RX;~;iBMnX^xtGqz|2aY% z{OG!#v$P26g-<)Kn%3Eg3L7)urSl(arfbud+|b`qmW2{DHesc@UMBDI5 zFWi5gbUz6CEL6YP)Gw2J%-XF^JUB&Hjj$|~ps@+N;{W+U?c|ohJ@|hztpvmMT5Ii= zR&v3tXxI_;>E^Ng)ZDz#!@26kC_!UWl4`b^sGXHzSJixxpkB0+OOlTKJWg8*^XJ+> z=257H5;QiU3hA$Fn%9m#d|2L(LRA>`qLo~5zS`hh+Bb)L^L-ccz@BPXP4%!V7F7X5&Xr5EN$$pdAhEeRsBT?F=~%J&*WdMS)xXTCkh1h zqV-;qlmsh^Z4%C_ful-+hLEdXj1pqhehX?@v7nlD04g(~Ud(CLivjJls?A`JyVLr# zaAw$>bk_FIN!pGXl`&J~w0qMTl5NiCG1pAdFheDr5;PPgK>Z#Rxy#D822cL zsVE2iN)(KxbwxSY6=wV`NpE6v+$y@T+{2vOXzQ$CZH!OtY#L47#eLJ9iBz$$XT$;#SUX}m?au0dxL z)C)AEVE2yk31Ovyk5?A|na2OBuPe&g7D~|P80y=@t|;qZhF9dAt|(^{)C)AEVAo_6 z%-2v+?rqw2_EYJT zTPQ*MN;n}exixD&-Wc09vgw|evJAgC8;NI9|6 z#!!@-buOJ1&7Gtba_bN8D3^s2^sNobM%Odhl7m~-0`5@)LA_v<0TGNUkMVFn{`n!3 z&5GZv)_YbB=LWWg5_H6&E6T~BD924jIXkv#1jDSD#5pL+$)G65O+`7|LJ1n1x}ux{ zigMgkl(Pxy1sYP$fGs%h#Jta~+Y^-bs|WE%hN7Hpp@bNt6bF;TPQ(e6O^WYYQ>^wd2#JXHG!aBpdkfs9IUU#{8ja332gB4B7A;_ zDyR=IKg&s56B-Y(+T*6y><7C}&$JAx14I$}v!sVXs{n0)q8tN7In7j*vn`YmqZSn97%0kVrlOopP%p?1JBRlTg1jZJ zO@N{t14TK_RFtzVl%SCYv!Xtk3>4*5Q&G+)s29wz!<0nD1^BH%QBDCx zIXHVjSCq3Yln|p96y+Ew%5hUs&L*fAtdhdn1|hJP>H}?nq8tN7Ic_S-*%nHOQ45N4 z3>4+KsVHX?)C*Qg;T(t(W~~}1$}v!sYl- ziZkJaeJkZ-qpI@zL#hjNWJ-up8=G&Ol6|NmuY9PoKu|B}*PXcHYb(krpeV;pMLF9- z2{CFxQBDCxIc_S-*#z~1Y9ToDCCFhb$|;~I$4x~!+d>I3YC%zsfufv#HZr{y^@3_4 zIJc%atVg-QJqJZO28wdrRFtzVln|p96y+Ew%4w#eoJ~+KSl5O*PH~vmnu>A^6y-Ei zQO>qdLX6tu4O_9?X=k*24Rl30o1k8xAq8pTWz603*xIhN7Hpp@bN<$9$7n zr|S!~oe8?4oJ~+Ks1`y6C>S%CigFATIHK_$bR}m?hF;>jz08Z*Ju8rof}>WE8=YnCB&$Gx4R0feDbum>_%~cpk7cl3(Cg+ z`mS#n!@c{QoeMjobytJ2BHp%8LX6tqXS8I$&0ni6C!OMD%+q8tN7In7j*vn`YmqZSn97%0lA zrlOopP%p@8!z|nfYRODRITjw$NR3pl-6B6}eN`(h>xy!ArWtd{Rz6wG zIy(b*$RBoG)oK?@!JT1tKAI9@)NFMZ(I#S=Y2QBDCx zIn7j*vn`aMkp>l}3(qUQ%MWSi3`IGcpk7ex1FIQ6P!ETSa+BJhQ)c!&q@7AnLVY;f zLJ1n1lJw(JKQ_p@MU&4e0zti?)(ey=eH@0O++lYg*h{-bI{=DuqQw>SG&bRsq*VOhJigLDv5;W4_oa)>(*0Jbyb)KOpXA{&5Rs)=+ZWeJEigMkCrLix!T~og? z6yKO|4DJyE+`S67s?*N0$dn7s-lt`&9g`9b#FIbPekHXZk8+p9>Fps@+P=<%`9f?cDu-ph1FIom=BF={8*yQDau^wTh+(03Rp(s~rbxT$> ze~^|O4*5Q&G+)s2A+uf-?>anez`&lw+VMr<#g#wuKU6)Q+8y z#$NZBr1mlt=brPt ziF>MTf_gz^F3e;+9EPIYs6mrdRqs(af)2^yQaqMQtha;m8)2V+q~vjW#^ zt+iW>H2m*|UPf5tUb)8S+tj`PW#|!QSw1CPE4)9x%~Z;^+oaBP({&B(YtabCxWOwK zigJrfPEfjm0!FQsx}uzI`IK-)Ehx$KCFb z%Gs7r30Ksv8RX4=oUl~=r<|_bViPoiG19ONL#3c`1uu3oaiLn*N7rGoEuRvus0Bqi z28wd3sVHX?G=eeGunk*Lj)9__YAVXvmQM*+)PkZM14TL2RFty`8o?N8*oLhr$3Rg| zH5KJ-%cq1ZYC%zsfufvhD$3aejbPZxtp9ggQI3J4oVwFcl(Q|A5To|knKZ^qFUU>a zHyd>gY=U}$l9K-44V8j`9%)SeZCvg(K1ZPzO3+Ayo!&1qSe4p-`%^wj0zthn((t!3 zR0=x7n{#BJQn_bW8Zl^DJ|$dH3pGnJ)-D-!OC@35z&M`|SKO`B{cvU6bv9s>G*QWk z9l^Yy_5;_mF~?&n%0UjuphdzRDmnf+l*v}cm4K5}UDv`o5R1S{4y$clQBDCxIc6%# zffi~stoZ4QaEBr#s?$Hr{JfQ4Ut$jWZx|E!}F{ zuEo($ccd1q%drUBo+RD98pA^N*I+;H)D`96ZD5?Xi8V#m>6chJWSxpBN$26*O#D}K zR#U9&vkB@&KdoJb#IOp9S|~w3pj>WXp<6y;=7QO<6idSR8Gb>=MS zk(J|Ew8hwml)om?7QAM@T(CN(>;|3c+t!2)%PPWZ4;34cJi5^14?qi*L?P z3DorJQ|lUu^!Ky5_Ro z1|^^l$7w01!Jg_X-YofrPi(t`x=xWzP%pfL*oHC6xSL*#DL)-ajZ0_qRXB29|-0SglH^+vfPxY}jwrz|6vPX7g?MGFMz3E<1uuuY&_@He| zukU(;c($)k&Db)3>smw`H>LyG_VV=zB^ZQ(G3S zXBhR^&9QD-zBoFTkfc9{j?&v0AtwuChEEC9pftuMHLs`XMEZf*0)f}Hyy)GOqzm;& zFxISP?%iRNgs~4LFvB+SfE$bx-@?@!?d{Ka2|2cHqf-Jwz38(bN#8@Ccn|tR{JpU9 z2+yfCHR$xeQG*WlU%|S1X?TaA2HmcjntZ2H6CMS*CS8LL zdblX~|G2AhbNglTn_@lKC_{q|u4|ve3)hW=?5(ar=fs#aH0YdNSIeJ!G+@4l2A%#c zGBxPnw+e;o5nY20t6`kB2AvEVbj;MCgS@D5o-k(5@uW~wgAU>&*lBCf$)G{UObt5c zqyvFYQ-cm>VS#X6U4ssu#6YNyKn=Q)t2W7L9;cLZXLSuan0fg+QG*U=MWBlVC#oFL zbAWUI@PQQq)K&X^=w|uzn<+~E0lEgA6VF~YHRzn<7kI)uOV^--$_P(*7orAT`-jUy{}vZi0H_mK_{CUboRAAC6G2|cc4M1fCimxYS7sP_Rf|U z_Va40r_=oHYPlT8geo51T@Uf9F^+s4yrYtSj6K_{CU zbhhPF0%@b)-8RFAdA#l;2O1i5Hi6f*yl{r(R>2c8Ri*|V`><4jkJ(A1z~pg|{_8g#bhQvzvQgN}g)U971=XA^i`%L`{n-W3Z${wy!_3}ddQ zv2%4^)J^5H^?ZqCp#=7JNE^Lqg8`YWR8WuoT}t2A^CcERy|8ajy6@pMH0Z{@$Y5uZ zykg}Ky85?m`INvmjM-hA?HlCizj`Z|Kv9myHvVlzFlGu}aSj@EGHB2#rUspDp#;W? zD{4W5P5})%#nhm)3F?KJ!U4HH@Tp7VLIoo%6n7`4#}uo}L;j`HgtEm4EcCa4!?3g;L1 zfn5ryLDw?Phkbdxrt*)WL1$YiAx14|(6KqkODa7L4LX~kUYIEa#XP7IG&ShhhgK2F zF++pSwopQhTF{_l_1ETA;tdTto1k8pDO?%s?fjr?(2d;{&mR1lPibRl(AgGBh*7Ji z_F=CsIpq(hbPYP2pkA1zfOP}dM}r!4hvBsJdP8U8yQyttoJUin7Q80pg|{t2AyJR(AgGBh*1j~ zbP8zDDW(RUO;9gfF@yC-cvIhm`vDqs3TV(NrUspDp@bN_*|=^Au4LA`LEfG2<%8gw^$r?RKp zZ_9y(2Ayr8gc!9K|H@$R$CsDq8X9yqLA`J#uINM`r>#N9K!YyU)S$C1ln|p9H0Wf| zpkt;6ojs1m)o^RHjFlsGx?2HlfAsY>-l1=%t~gU+^4LX6rUzdf%6-TI)UdFvWzjiSu;v+rSsmziot1sqfpzzk z}CF=|)c@58Dc>Zeqie*iV; zY=V0EcEER30IX=@y7RYj$!y=zWaVd-E7U>>F>1FBO=Y8N6jv5+nJ5s{3$u|PoBf>y zbq%_sE7RDs=LMD1h6bH&p@bNOV$|MRuuOi|r4pN9XwccSU!2!kb6l*Tbj^f^g&mcn+kD0<7#ei8g%V=a z`umJmJPMX!6$}kJo1k8xIt6hP3Nv9my{>zK2}<~>(k#KyptCKM5To`Nzbi^=kr3wJ zU)P|s3F`H#Y&gv9NV*}Q19C&0h6detDL$;$^1|%)WnF{LwopQh&0DWyScg%6DWw*8paz{yP%o^Ye6Y3z ztUO>1@>+ztC zk_%e0GHZ`1UM25?;)%;b2^wjVGV_V~atbh+1KHccn6ZE>5|4G8r}KYzrmC zs09r=1{!p-sX=EG)C+T0Q11nCZfejm(4dn|4LaLG2{CFnou4cF)oa0~85(qU&I+re zag_vP0#v6+{%Zf?ee%84&Da7%gU+^4LX6tRnV^4zoWa-{bjpWC z4cIk9gU+^4LX6sSyUrIKRLkdF(8nJQ|~&HwD0GUR3rcI}?7L1$Yi zL1PoNKcOOJ*3~FBvxOUK(Afm_0@W#4^9pks8g#{$d9jGsW!RCnZm24LX~kUZ6S!dCX84r%N9LlU zoNb{5jZHXDcv%|TKQmETx~HK)P%qp`@b`rvSeeCa?Xu@-?7CaL^25Cbs6l62C_!Tr z&i}}PoXm%0Icv{m)S$Bo>XpA8?$j*~xmVOY`w41TivRwVoK|O>Of8fUqjv1RUGlW0 zU$7F_9_v|WE7Od9pp{R?9d)j(bNwglH%o4N)a0}VQ6YS7sP^#au?j6WF1nHqEqH0YSAL1$YiAx14|&@s@UW2Oe3 zO;9g<4)RtAgYmAZLB~LYj+q*CwuKU6)DC*lhke)kpwivYptA|;1*%g}-(C_{9dHlt zo+HU@OkYjeVrbCW7D~|A1QidcaOzgRkFv$kptA|;g-`p01tp+wH#O)OXwWI92Ayr8 z1dTLM{_2#;+P~W_Z#FdOY=U~>PQCXVf}Dm1T^j7jEnauLd||^veSfWGp#+UhN!n5` zN1n7|IC~*4(v>N2tq9kL=qix7R#a&97CC+XaQ3X*B86HgK_d;c!ir2%roJA)mNuL( z5Y!9wIA9e8^T_q!e*D*ehO&Eqf0n1Iu0dy8C?Q5|&rR2qz{OqI1Ve+)Ca4#vPU)*z zxE2Ph;a#pMD;soWH4F_p+d>I3YPZ$!VvQO!W7P}|I-8(gpgIL(yHc=jQ6Jg>4LSxI zbj;MCvn`YmqZTyi7--NjQ-jVXs28YCIpeBFLVTDSbPP1;n5jW$TPPt$Eojg&(4b?c z2Axe%FHoI=YyytZO$|B*8g$ImptCKM5Th0}=oo0wDW(RUO;9h)QlO%Mp+UDZGnp+b za!y%bXwcafN{CTA_Fg(0{^tngo}oc!6VwYUtm^Cxfq$sUG4WkGi>j5b95*!RYzrmC zsLkD!$+FI_m&a$M>bqsFYf-PRUzdiqgJJBB8g$b~WU{!~ntXUqf0#5y?xE^4H9gkd)zwuVo7Eh2)H+wJ8WQHW zmU7U^l!K0GIp}^9sD-g#)YlHWCAT@;orPaibI|>c7em7Q*6z!dlg(V)j0G)MbI|=J zPzxiuB&mN`b{793l=X4tp!*%GhJ^X8rTlYD{AoGpeiNvL(OYT`Iz~C@n3jX?cjOro z=C_t|&@swE$Fv-DzX{aB=q)t|9itp{Ov^#{J6;S4^ILl)PjB{X^F=x6)fP2_nI2(= zT4IJ|M1{3k(31sjxFJ6)lc45R(Kw8*72LFnF!Rv2r-(a9v{uxRo`kI@T`W&K}V}Hg%61O zzDVG^DM@n%bm9NixvpgY)LJRmejC3~;-d4z&kt;Zb0a_Z`J!{jkSA=gbt8Xw;j&X? zogxqYu>L%y^$sP+y)p_)kcfKvH!nZ=ib~VU=-LRLQQ4rp8D8E*pw^*}|M0N=S2P<# zcMssdwk9Zd`jl5tf<)%434HnCYnqLwVT1U{fO*P_w!tO>wW?2D%`4r$Y{0nqDK=R*CRWx*u0*JKrN5$J9*CV^O}vXYg+It&C>I{-K!`lL1OWMoxDlc zbLtsVM(eXZc&F}{mA(CPnF!Rvr-$P7%Xi=zE4@R+!|maP;1^xJ5MZn+PU(zs4d$vqc6Xg!I%G_rvxQPh~NF}r!=A< z5#q0!SAc?A6p_gbS3K<$XJRJmj;_grWDg!%zM4wt$3lW4YWV3>r&OBq*53E#vx)@p zO1V5u1Zp+Na*#(nKB=A6-go)+aQUEU%lNf7uzrr^HoOP=Z9Q&PTX!2Hi%6x0(5cm!Z7gR38(8S_=vs=Hvd- zZPatiz^{&N!H@OwQBZ=!x6vnfhuJ5zmap^3$Va>k<2|RAQ&6i@(sBMJ?4)`{&BVYQ z-;|qY+VX9g{S=fSQEcK#J}{SymZWCAQWRVFE__NeGl5#-_u>KiSw*)=R*Dqs%GW&f zQ&57$&kg7KrN$?m_oBtK>y>;|iCo#654>E&M4%SMWAFnfjyqj-ktO;seFs0(cNS?7oB?To&EM0P))BVwGOn;e8X`MHk zKR#00M4;A`eAjuSYPyZf4nHOTrOlu;UlPR^MfjTt)Eb!UKK0JW zoUVwi>@U_i8Xu14SEgGOlpsO7=hT+B2@ucKiY?O|gF40VqCfmh1Zs)j*AjFaq8$;` zj>o;KDs`s!=Z<0Tomo>oXX3YSh7ZmgXFjvoJOh6Jrf++AHf49QC?5PY$V8ylk>Z1R zyY8+d(GG=`!Af|h5q#x^stQVwxRx-8Mo#Zld*Ym=G5zVR9?@ALfm(i5hJuaZdG0v6 zzKG`e#+Ox4g2ZSxoW7#EjbaJIloRWRamllii9jv!`)ioq4slvhbXL3Ro}&bbIS0|k z^~{}=KD&qWt{uz-YUK=%;(k@$J101w(H#{3U6rHxsE4ho=QJcpBm_r7J92D~S7Ij* z#r&v-MjHS`-2p^2)`vr95^wSFRcCE89MVb-H+{kG@oKx9Lmgo@1Zbk>Yb`Zt%kCiYant?82QHQxrLamEKxI zg2dth^LXoQE}CkkNCSB1b!(LBDYA(`t?WMY=pB6S?35(pmd12VQQr3G!lzR2jOUB( z_!2gr`a!)NV`v=er15B_^<`;XVn~o^aepHJK3cz41!*RcoyLiWY1D@VYPq+az#C-I zZ5*UAVoxGY5P=dTdi9*7UR~Ecmnv4^gKt;mI|8bk2-FfZMH)I?JET{7()^~hFLx$X zRZxON-mR0>vvS!u;pNNQmh|N>{~%B+xzr@Sbct?b`?5SUExUn za8Nhp7R}FbPR`?((zA5WdFZ2<#pC?kzIHedJ6KZL)*ynuiG~CT|Bg%Pd!^IU21oKw zX~UIT8+}X!YBig=h`$JZ;!FtsMD{jE597lkdMI}?)l*P{#2;UVbi??XqGOeJtIC-O z)Y@}wA)k`(sk8iKVdMMqp?u7vNlJyc9yAj+BuL+Pg;GfcB}n|+uo}&Zo~vI}sl&xwaP-{)Fze zeU(?SO#QZtugvjCnY&4E)OUMeuG)O%>(|;_8*l5vPB>E(E4_m#LE_d|nI9?cqA7+Y z#h39LzWhjUcN2kH}ykc?yW-Kc0+=M zcorI0d#l;_a-bb+SN?;Nv76`(3<9-iZAB$qQ-tKLsU7(5jN!54w7_ zcl0Fo;3>+#^dusIT9ogcJa=!kcI+PE%jl0UrLhl6kVx%akC(csx8rm!nI$##i2k_GNtdX%$n?~%!?PVt)K)6VOz|+TsD?@ zak=$BefWWwWlaQXQPu+Wti%kNYDk|wDto-^#Va)_ub>2pf@xZ*vs9Oj$A?qo`AJ>) zE_!y6K&|2P!}+GS@6=g1)jvP)#$L1w=OOug6qNi)jQdM($IOqLQSfLI93QCY@oh^b!XZp1|WA7@*t{%>&cJMV3sFgKUKl1P2*2cbb4s4dcOp9PE zx|dK;g2asez0{Vw?#HwXnOW(?Q1`P+oV{CZCQ$3Zi7tH3LEVOX zmv8d+yzN+xL**5eAkibJliChf%eC(n6W=Q&P)oeQ$)%FDtNZx!EBX1+4s211zk(7Z z#Ft2%mCHugYx!8s;vpT@8jcj-Gu|g+9*sv;96``d|kTBawc$b+`N=g<+>mNvP=#_)+$|6@~j7F68ZM4;A&qYe4L4ew~}*xIWm``g=twc8Y+pah9>?VHj{^&PDp zY5Ik*bvg30mQBnAYKf7PoJqGa^I{;oS;~v?Mzz#YpDsb-+~+WraNYCk8GKp$jJ_;2 zT`N2mYKhUH{@&2`rR-hVu3Y!z=7&p|-Y6vQMYrJ5PhD@9B%O?*yn{o1*`I-LYi@C_7bN8ICUGbmt^<$4p@0F|AE7LlsAwfdS zqiHoo^D{IE9maA`?<X-=B5_76CeIIBxDz%Gb-ou;9*Ea{KGe}*6gqZ2sLLO)~ zHqh*DQq6cdfo6AjEYw;%$V&a7Ze!Y+k<9wuPY$&dQ&570nB!L1`B1ZQ%sYnF%r@CN zJx`#CKrL~GVf!Cxp44oOhq6SsiL&fdQ9Ey>B~}o zZb*TltP5x;k>c&xSj zVue^fu6;vCu`OQzL+Dzo3T;y^l(?@wiLZmB`T1819bri!3QF*}6v;uWTl&+UX+Si; zK6|3$?G`hET1Sd+SF-!)Z`5-4Xg+hpVn>&8jmSf9NRSxQ*RF`~t}6#b$n}wYPGD{2 zr%x6Wfm&+^ITXb#4sg4c;wR+<$nR2-Kog zYI+`SXd{`{o_+b=P@A$TPbCEhx+Zq4HtrsAWnS{0NavG4s)@}*qT;>eU`Vf41DY#1Zv&-`XBWz7d0C*4|U)f zUcOZx7xknswIM;GL6%fB^U`e$t685HEm@FXEbMC{P%F7qI$B-2pxH=T8qCj2Mfj4b z0pueyBuI2h%BaS8xnl166sW~twJOd#uQwB@McJ8YHRHTy0|}HM;TV>iq7%+**D5#j z;44Pe9p{{8!<#&6HAnjLZq%Qn7Ug50v7zg@W}>{$X!W_8nCgQA zB}jp*ZS_28By(0ft6!~)@w4A+^2clHD=5L^y0Qn(z;9Vjg7>sO6iXyc+4~ike(f{-^TRtqsrYtfQa=i8cX#yhhbCnvG0v-YAo9 zw&(Y!SWN_K{n${M#tf&mc2wJtfk*g-@zIy93QCX=s~qXap4MzEsQE(iyU>YyRVrj6 zP-{VfI{aSrDb2>BXIGSf@a}x~OK*ztG9*X{KkSE}r!*T=`rDMyMt%8o`obZBT2wQW z)}3`?@2|DWwOs@F&dy~_1Zuf_Stqp-z}y*&70btgyxW)Rs*g;>q=~Ts5=)n~A@9d2 zEw(l=pG_%zy$_$)vYLrNtqMCks8MLHQHD?WdZlTe2wpaIO$8-LTx#5vZ#e22uTWgL zlv_!962+7Ic$x^*T2*KuMGolgIF(dTnRY6QC%i2|Us2;&yH*UOI0@Hr%|za7eu}(l zBo94Qg2oKSv5*ksAbk`iNsAmMl!O;iJY;=76M&nbW2 z0g4`A7aNbGb5H&Ick}(J4qPbloknD%AKtM4=RS%h{+mw`TDnAdbUe1ZwD3se5grw+ zv`LAieOLh|8%PL09FNy+oa>V8*y9_`R_6^g5vV16CzOd#JwwWiGtoooo^>Sa_o0@8 z5+sCgxmQo!#*zIElrqCdu+!0=CIYq6k8Mfg-Bh%v6r=qn+Vi;ccC%&o~+yWSjeX2xYmb7w63P01PL)CqpXhF{qScq zmEk3aupXr=nh4Yqz5zkF+R^{?Cgo?9e(Y6-ma5l4mmne5be?>7qYMTjQ{jY(acXbS zvq`iAkA+&|-`51|Hm-kMse}$3!0IN{Qf=rGB-(WkqNsb_#-6*^m6Z|QS>fRosvBdw(g728Xkf(`(YxZb*<2-%83gO1T0>J97HPEB<>1u<_k2CIYqk z%=O_uJ9Qgb>U~#^rD?;$vp1nNdP9PQ_|7L5)omOJe0ox60;5 zdv8U39LLOTe9if0?DVkW3QCX=JyE6G679SZ?I@Ax#?R$$!xq*qW+G6FavJb?FD0r@ zENoQTQkrLKX=U!MtqMwzpbD7$YJzTK1;xosB40zcvH>OnwWj1NNHLJQjiGfa@VpnQ zvflqxqgYNuf&^89q7j)y*~LUVX5FvCGi0pH(o?iL5~xMBsc3#K(H>sWj*Z!B2VwZ?;jHH_3Wh%1=d5S40K_YFdY!sLA%USB?S0e6yB_G?t zLd=rjYa&pKa*Xml$Ntl7)J+r2FD!RwzMXw&z1EN*A#5Ly{7rA`UMZ%)2BmlN}e*mE%o@Pl(-<)Q0j6MzWDF5?&Gdu4^$Y^V&x7=0(fOYiATyP>c4l&=>Eu8X;mP)=!A$JwIKrCdT;F zx7|1v5-&pclV?JY{TM{+k7Dk;f!5WKKrQj_``*9Q&g#28hWBq*&3d?{pGxQwB!ow& z%flyHgi!tD7=AWaW$WS7l}!X{q0dQ@^s`z*XN3|Z#BbrHazzM@E3|HqCYU)0rUv8QEP%`e*hXg#(c`zL<8%nr&5N{|?s>k!2heb&~t+ddw| zN;jM#7p>aRM4*;eeI?9C_!Rj?UQN@iz_DeYq>~v`d4$g z2kjC<0<{i(JVxsVZ!{Yv!!vkN4`y-d#8&Uw%>EM4;9uFFW;WKeYE;rd1i4R%JHQstihy*is~k zXTGZ2NL<&G&CYXDE>hS`pjOG7iM&?lpW62->*juJr0+iYNwwk%N{|>ne?LXN{cyUn zq-7~>Wo~K9uoAZN%0EXpspAjPr(R3gPW#}b0~8;}@?YP|!viGBH6y<5Z6bYHF3L$Z zle`8fLE^`T9rRqeXh~W()ymT5^JEwD(yk6T7HU27+(B`czqJ12xmz%6u+*L1+2%u@ zGed%e*uf{p4B`xFXL0_vto)$=gZOZ;utjp*$tblALP|JLETMcf>{H1g({&p3ba~jtj39(bJ&s_Zuc9;~(s%*{7 z_H`EXT7y6>yt5R&8DEPX+vdUM?ksG&y1p4UtK&8?dciwOnGljnvMFg8+ZJv$-E$oy z%iyMsUSr&fvXF7X?221a6M`iwS7#OSWEv7AUd6_9s-d9GSZ@dN%~$H5+ouXFH>j2u6fiBIThQRs|}mlqX3P_ z3<(nRUM{By<5b#N?aMb!{=8{0^BGyoM4;A|B1>t!n@XFjh5x%&PN+VBWxMFBpah92 z`IeB6ER{Crto3ERoavtk7Vy@T(D zu6garm-pmGSGzK|8<|W5YHfTwk4E~sjg8$V$oXwUSntwS@^TsyBt#tYx6yh#GUXU1 zAId$9RcK%)P)kI4KRTz|=$p_%_6r@(R##&RN|2~CeTM3paQS4K9lK-Aax{h=+MU}( zpq7Yr7v3G04?v82C@PGJs4$cuAtLaF_rqmFZV@Pd=sS`H?iGggoaYvi(V z`u-5>=D28<5YRwD2@>c*qC9sIr7Q!e9ap|rHxa0Xo++w`&^KPbPO&(9DHeymN3{3t zmby=no@|OXyQ%)Bc_8hTyQ%F_>^?AF&Pv25B2a<^dBDg!s1xE^)uYJb;}`3T5$f;b zQY(M|HWTjk;${1t0c--TBBKO}RfS&D&M?;*(mt~0)8zdW8&$PqfQdjYiVr3aq)xm! z^v$~eSTrk`x}=FfEs77O{ZZHs+Ly}2zEqSTLE|^D@gTLQTsKn`>)fWSi9oIFKJO?# zQMb`<{(9@E<}obe%ZjuzX-JTueYF&ysJA24iezh3ioBl@=4T>MOZ=u;l6D72?N5>a zadcr7=Xjg$AobnkNp)%Avp{}^_#`>+@V?ACw7lslL4wx5)U$H!SbyK{yqw9>gZ*9A z&qSaWt!D8y0k^dM&XHYHUdR+{c05~wA9-;365?ERD^PocP?jucab5+rC9Lw$~2EuR>Wi~ZWy zh~*q-H4&&q86MOKI@ex(Bv67x^wURb%Uw1$RB~tiqwBD9D=jqAH;#o`;e)WL84aYhdkrvTiUgHRMnFm{$ORtiPFdWWzxwFtYB4}{FU}iAc0!6`mV;qxvo{N(F0h{L+j*KrOJw!h2Ih+sCELy;@s8N zqb51}u}SB4%TFqr3Dhci^E$16+|$;)c24cXE^vo@ZoMzf?hFYMdyZY9zV^OmBj$2{ zR$<;o*}H$Bi9jtm(^ZPCeW0!Oy-zoUUE4QT4j5BHK?xG%HKMiDM_R1G*HuH={k;R_ zqB9$s2-K>(_afzbd91}VOl&ul(VyJDUn3fq7{{XeE_|f&SUs+pxYltP%ig!E++t=U z@<1BLLSom7vlIjQREuHBk}#AFEl0V+f@_%w)VkaBG_BXZ(qeJWERAISoSo&|a#8xW z8xkbUeugPcMzFhcEb`)|BIeE@Pz${jqB>J3KVi$v(vJ0~J=K}WBb(wBnf5mq$>bc` z+x2@&zKqVF8&b3k0uM?J;QJ4*l-t~^PyR$ha(QG%+P8L3)xslD?Sa1s@weUP$bLiX zm~0?X@@7V7{S+51Nz?L1@{{j7$r}!Nn+VieSUa<`*u?uzSC)eK)+70oS*2yq0Rc2u zGbBieHa%E(Uu%ccYcLPrIY(~bZzfRd!Me=OPhJl+8%25y;c*)$$vc*bwFyIl1ZA0a zo;meEvoYyo49`+`oAqRTQ4@h$5sx!FPp*Eb*{E13mJiJ`$J*+637WAO5+v%UWO9lq zJy%ADI5~!I=yKB<|IRopoI1W{~qb# zk;G`1{AAiV5hX~FPw6*JH5${lvX+R6Wq}im)3a-w6>5=xK|Nd7bM>}NJ>{F{2sV6U zWd$WjQ2j=AT;jSP!wv^4IjWAJ*l#m|S`~KwR7d(Q8$Z%fmV!)O*e2TJjuIs1z5Joh z8C*6VW~clN%LcGZ_hb`+TC|3vjxt>~CI<{tD#^px&v=n_$dDjG5lQ5~x}`l=Cu+ni z|5B9cL7E#Nfm-B`bBYnN%f^O}o0LIA`m=_#O9&-@5@Lsns~zz@QL_+-5 zwYof%oML3@veBukO}R;ZVHxTNQG!J2bZ+z%>NdKr`A;cUt1bK1uat>Et;GXU(W=ia z&4%V`?N&i((GcHo4l-Vk2cwDjd!dPK(T6 zh6D+kY16vhZOz8~T!r}HhV|Kq*CkB^YEgX#8ZYSWcodO^m)OvpUEP?U{62;R3BM|- zonlvts~zHvYCA2CWoka$fdp#Zi#Co#dzUD$!M^mq>>WjUqXY@6%H>=)NVk!kBR@ag zH-r^!Y9>(Y!_PG8S-Gy&PpJ&gc`t|!NvcV^APfl-m2RhT?t8D>@Xs5}tGsq+K3&V0 z2-IqEKP~m^x7FTGl7f%>^65u?*_)}wDHDPrK_aneT4!vYWbImoez5RfAIq>Mg*;3I zYDGOw=M+0eT(xTo&kx~?D&%MN-Kvt;z>pw8)nS}s_lWC$WIr9sTinjfPJF3kB2bI! zEm5?uZlgeFU;a;9stu4>K|u);y?Ul|iWqvAjfKry@YSO;uyWVSnF!Q+5t@Ot?`Z8P zd7u^NUTIj!Kb2_J(2yW8qknoQbwvdwNQe=R z7!$d!)rR%1C|3VprA!2B;h2c>IrST^-0VA?r^@LrR;tOb{!xwD7Jdy{H&=fP&r7eK zpR~0i+7suXJ#l;$?TJGP62d?8?t+V^+|iE*S~Jay;d@?HF%hUm5ge)wSJsHe(?T3Y z@5S)oiy{`skRU-Z&a|&cw{g7GF~?lrXnwhI4HJP{w6aUHqEA|uf@`Jxlu5fr@TN0e zo+7=4^iI>xCs%9nib&EV>Z2;i!+7>p0j6t(1ZBRY5wd=*mbA^U?5!2W%kHRbB2bIg z9{IXKAGI9{H>(a;=6Vn3r3aT3>({>}NKm#2PI2ej{on`hDW1-*JR&%oi9jte8Yo;* zxACajOl4fJ!MrG~TA~)MR;l+~WQs5o6)T-nhNbSo$KA0iC_zGu21Gktc_!-pyFtmn zGlF+0nmU*GNCB1=~4Q3_+wK~jBV+FUy=-tTY7efH$z zBf@i<2-FG>NJDW3@3gt|nqd`r;vzp@>`iqAwM4Hbaz)Wsj+u!?*K6@w{fqIs2>}X9 zkP!V9`DL{keP+slIh8VCKBh==Bv32;*tGPdztztwD4Y+ZTwZO5RHBGRLxRMc_30?e z>8)nt(u+_&?p{(~V60;#ePwTQVd_bjy*!x%}`O zEe^SRWDDMUQF`99LlqN&T2zJDDIy14G220umnx`7it>_1$S6Ug*-X(7{=W#6AR&5f zVZ#-zUTarxzVG%yWo6b1l;y@a7HSEac9m;>pJ_vT@>HKsDr1ZJsr|WrEYbH-O=Q*A zVdSJ=kPZXs73o5Nc)9mHzY_+S&oz|g=s7{Y^r!L*OwkRU-()AU`} ziE4Lxure)A$^FyIG;TMnQ%zR-I)72m#7x8obYm?)U6s#Gu!?x<-^W7YpQDB8t^KC; zYUNvXWy2TUmhUw6GZCnD`cx4ot(|MV+MKGr*ux3OgPQlE-i#y9#zns`b_uKl# z#<{3ZlBjyxXJ#hCmUytfPiwNF>&j8?Hse@GP;EmedG%D9@}#aV#=KV7W+fkbnF!Px zp=77|xf@lj6j8+i4tF;6SshlnqmP0TB&gQ5vsPy}s*xokmp2@DXXEeIVM{xh3Dny6 zJ}dc_+^8;^uraW2ZkBSmA$wkgQN*nwL4xYJJH?I>R|IypcWGF=UoF{~D^*PdYPIX0 znZCQJsG_GDLqFszRR(IyZV#(T@yvz<3DNJ0XeyVD(LHX;zpPzZ?kP1)1ZsItPUjSn zHLlpf68`Vx4q+YHo{RYv)DoVlS&LFpb&s#iOk9fGEMM@9V68`%S5Sfk?LMWxBh>*{ z)O}W(0*Afs%@R7A3DgoEy@9z>YiBk1!)|$~r621tri$vh(JATMP?cjB*Z*X*mgyj&K({jJ39mrIsDLuA2-E-quG?U|^wPNpznRw$!BWGC@#cHPwpnPP;v5>$q zktEs5U9m=5V%V)GrA!2BnMX^5Ul)~a2ckIrU!AdJ_c@`CCd5-Bep4g_6L*uxpU}o7 z+mEi3dp8-t*I0bS?&aSSB*e_}=Z51dP3t(H$H@m84B`E^hnNV|5_9z@-;ZlH_C~gq z6DW`N+id}~AIFd&@neIVQ)FfrXGnV*2F;PLJLX$m_S+OYc@3zs3lgju8cgc`Cf%*LafF;p^-jH zkf2@wY|QNW)7s*36t6$R-$bBR{_C$G<9qq0Ypu^~$MVNJ3R3*8AwfdK{+Khq$Iw}c z%=Jj1miR65>AQUT>wQz?>a_3Y8SVMOdoJeA6P&ImL(F&er-!_?buYvZS$3k8~< zDS&qj%H+5wzb22@O!9c)u~19QecE+Dsf~AU9JI-|_x0uLVm#DYkuE_( ze1pY4Sl0;oabk+xmU0Paqi7M7AR%m={j8tW@poC+$r_WSb2vpck6t~+ zli0XqDt7C78_tTRSIz#SNvg}s$C?S$61}J(TxYc>cNR9=y*Xc!--~u384@H!-!67~yNIKA3N!bIb@=jI zb!kVgAwj}pdoJ<;oYBrI>{S&uwn$~3hvNE>K&>_bc~nBo$Rw$UAuluYt0x%>n-!2<3C4KYo&`-5`Nl^-~HtJIvN?(&G;TwJtU;K~XO^ zH5-%q4q~?^ELUdEFHUciAwlAqrw4uEZjygV-180{quBe%g33{8>8r9Q5aZZ$h-;f|db$;o~b6>MjIztqD z+pV0kwRCCv!Wrl5SEVG)N>kM1n$K!lax|+wXs=`DP=8ttH_jIcv@J;uGmT*V;)9j( zRL2Af)DmYU@}9d|-df8Yy_|AKqvTJ*XvejG?poeGR_rq_$5ECt!y|+qHuSbg($8@* zmT!I`L7zXzQN19;zk`I?4`<6z+M3~?VU8^=%>-(pw?&d_H+Q#`ztTNu!H+nKC^WpX z=&dyqW4k@K%>GcpQFucfRsS(OLP(%LnRX7`yJSh6mC8o>{U6$-m+#=egmy%GS#>#ye(Ij-%*H!y|+Qu2a!&z|Dg#2S>lL$KNs&sD-{7N$PwkpMChjeD)#V z;%NV#;l)7$*VrU!-|o5As4wdr7sie@5vYY89jXP<GHd8lpuj?o)jnJZnFj~+v_;|c$A4i zE%fL}(#WFo?SVz++i#H#lpuj?w~|z9bwB%mE1ug6CC5>YL}S$(y&t%uO#24|dt2YN ziE-R16en^Q{9eOG0{t_R^k#E$>y{0nj`<_Z1ZtVj>fq(omKpWRIi@GYQ7pBwhK&UJ zndl4Gb-aD^`W5zp&&>pC;T54s4$p0tlUHjx=Bl<{^{BmyN!;1f!_ z=&rZ2&|gp*`if%Iz1WLRt8waD0mjoyQr#a@0#|G;;iyH=6-to6=y>X*9=^05>r=-u z=*uXvGwb(`3aq?|yA>oU;Y3z@^*%jqrGCayMp$Ei1QNLSfU4c3?q}QAAf0V6^=fDX zcXJ?3{YREqTVReKN!^J+2@<&9g!0L>3b9$F{I-dI5U7Q_U?_k4wmkAI>2Jrlfw7eR z&e(T@`*F~=-usLHk1?GU61W%W|0YlicP0HM#{8moVBJrwGm3k@eiQ0hp#%x6g!+FI zsD*pJ%mhl1_`k0e5~ziHzRWgIg2ey(T8aCCwJfoAChiP1+dv5tSl#pgCQu7^AJT4) zo!P9HPIk0=U5FF)iGJ^7L;|anN>cHtYt{v^A&z`CN1F)L!o7s%vqA|HSZ&oH#B+rN zYT?sk5aQ~h1PQDM`+pOth0nA~jB#=1JBarNYlE6?pacogOPF^3{=AC@ERZyzR1e!C?GEa`VkB^< zuB#o@tM{=j{DVL(^hQ%=qo6a1&(if)36vm#J5*@&n&&@D+=auokUMdd)!*=iqZaz% zsgC>Ap@C}xUf6>Fi=)*Y!*`Dat`11jo+-@(t0lj%)gb~UNZ@W28t;yqXH9%JAt_6^ znLsUE<)AuoXIIOsGiJ6{NgN_FT>M@cK?3);&^~~PKLekPeP*jdXN3f6q32$bQs?A> z$1c6HRVD%@NZ|e!8tGr|9JKHJU0bd{2-HIVz9gLqDI8S$-4mM^5hy`I%m9S{%C*wa z_`^NRucn8Sjy^IIsD<8o+T)(~pP*hDyzD!^$5CAuV?_Z8^wv`z`hgKa({uaUcM*XS zByfL=B=vIdXziTmd(gTSW&*X)TQ5mxUzM?RnHXYU_F2s6(erQY!9hxTIflaB+sT%mNxFa?U&!i(XJ`OcaH@6#U-ilgCdr*kCxlNyosY+5{4%p z3EXufNo8iaS;ZHnD!u1OpceYYsZ!I2c+1e>miG5A1!3&wz+D&URi&!z@vAMbPdBxH zB?2W#2(OW`(;}~1T5H_$H9>30Gl2wZp;uLsnB2^g*lx1@AYCgwcihcmZii@f|ISzJ zv!1$IuCFK{A!d`tzM>V`N?JSaiVd23(oCQh`uAxq^~YFCsb*X3dB_GzkieZilGL%! zPD|X;DErkXW&*X)zfW-)qt{r!wXJX6e$VA8(pMCaz@0sk)ThKI%bt)@_TH~t-YA_w zE%fhGl-})WmX^tp_EN9oD37nPqJRWG3$&*p-A2o`aTV>w$OaOqh5mi|QWsCM>?!xf z=0{HnN|3;3fqZ>eZ&3X;NSOBtJGWo3l<=Bk+fGjj5~ziq zbgJmN=&U8r(&CQk`{O9LhvAh+0(S#b)rD|3>-Az5TNZlHkw7i@3C|z-FREaw==~-2S!($-)n3tTZ4YdI@9KZ?{e_EfY63x96aeC(2s??bIruFBWctx+#G5LN{}$`;a&SrI_r!puk9n8|4Zm`&lsyp zabyQ>TFzX#7Z^%!ElTjX7||?Asa8LmoFxFTT~XKUKY;2h@L{1PP3RrWyT$yOwJ| z?DiLbtg)aLuAY!j|8`AzY2~tx>!o9;Ua67w5Pcb#?N5@9T=lW8$ezs+OJfFh zwaDRUeZ@?mmibz(d3@2n=5d&P(StaS5+ux3j`QqyCdJ?WVc&VzOrRFlNTzHH57ydC z+-zsR@IrX*j2g>Wdl_v@(%b&cES^t-96R=yZ6G1CTp5+EMSmXL;6Ho6Kh6rZux7O+ z`S+Mt>_dn^2@+VRoT}<3?6V}F?dLc<&P<>dW-yebQ(0fzPh}lwFGAl$ ztlf?^?=e55Bn@sm*Iw`J0eeg8Igvmutk6zpWhrWHl4_!3MV&axuxHeSM*=fSQjXtj zi|yI7F1Gileh>-N!Zt}#sC3j`RqADLO>-QSAb}YrDe7fPe@n+jbsPy>%miwguhp&+ z#jW!nPIY7r5HtF*)EB;2EA(P+L(HWpNmFJ{v{&B$)V`cXuSlR4UTH}hT5Y@i&R?hP zzC@q|3CsyewM?5kE$fT-acmiHCQu8nG`)j!1FTPfj&(#-7c0C*CO^#Zw{FlVinG+S z0{1_+)}A{&qvP)BINE_}NRYrBgp`N=)hzq+7ug(z&zK3+dKDW-vsB&2`qURKgNFBT zG@*8&1PRQ(Nb4Vq)>?O0+~v4aakPm*ExhXFp+9ucUgWZyV=8&iQGx_!kt9DulNa_8 z&0p9DA2$=Ih4-I4Ec3?N=X~;TOs5rhyt;VZFb5%31e%`F9{bkY@%&I6M+p+5ZYS+w z(Xs+h52$AU_j4J?5wd{jBm9%l{<*+2;rq7tguE$hmf zJ-ptkNy-r;K~sBCt=vQRHt!rTo4T?5= zRzJw&Rei@vIxCbQfpvc+Dc`uFmLSi%4!=JL)WRAXn$g{{fyZ%_Ac3_fs2|Mb zYuT6A*WsnaQBH89+65A>sy+I%>*rO_1$cl5SQIiU;QQ; z5+tyaHubfuA6Qx^q;eEHBEF&qfm&F5LXy^$PHW%WwXvfI-E)*6fpsCsb9Zcn<>S;G zj%{b-D667Ty8;QUAudVnod4R#9vtNGjxiIcg>@k$>5}IO%i_`}?D?+6QHDLE5(N@i z3!Qv@pB`E!f7xeGpji<2Xu?z?$ylv1=Y=dp#=9anxoe zPz&oqNK)6yiGT)~1)FWA!RH>gC$$2sky`M4%Sdh@dt4Cuc0P zC$F>brI95{kQi)jKx?4y)KL`82OoV2N-UVqQGnLfkU%Z0BtdaYZaXZ^_b;*!e;7wQ z1dOT_NQj!VVvoCPXIRN;1ymovRI-5tYKec>YfsSCo$iYR_2@>KP)3};$suIC zN~|KI7FNGC6P;`if`TutQrt5RrC1!J1_u&Y374XfGVBQwmVVKi7ZRu?{@tjwvEab& zpi-Mx1xUYKgf2k>E8$Agm8t7(Ge*ZbCdHWv)DkxIst zt?$CRww^?YQMg{SlyXb_u94a!s1v{2^tNW>$a^2#g2Q390Ysn#39PnC_q;-C+h6fR zZGHbBP)m%+MTS3-frz3%v;MG&w<-sHMNxu;_^sEOB`;3?*|wH>v)ihXjXw!pt6Pq6 z%95tr*j1^X?OEk|HWAl{5$-6TjGU6M^T8?ZB$B6jOAdLz3I*uPT8OBt+F%k+;@$KiXg0m9(tk=l>y4 z3+v2M#P7o>>-Rxk_93)xP(LM!M#5**{7|?Cg;AQ6^|aCBp!ZdR>;))t86`;23;=SH zm#^z@%@wrR&dD2v1ZtV1iW|KwWPkIlki8Vyz_X$eGOZo`?-^1wmD0}s>U=?aHJVM} znIN&}*j{eAb5_fLwI@vn`#fhsyXPMSYKiJoIm6FuM7ul7>=*4h?DN(55<^n1{WhA3 zUHqTxCP_1MueQuy)ZZRQYhHL(NC^M_y9<}K+&k~?PPI??eAqVSqnSXhLm&U)q8>d( zn$x$v%JNCo2em3^kEA&cN~%v?O`e-uvo5GSw{HGwfny}6R3r&nY8-4a|ExKSW)hN(<-vc)4X=OdM0nyAEj~hHZ{W=Wf!C# z=Yaa}l9b`<2wrh|B{@1vpvWTlTY`kBEP3eTew8Mle$|ou;aGop;B{{kfm)OeNcp+p zfJQ7yyEt&v$ygpU$wR^8;u#+L=tU7@+qBF^p#>%ex}S~Z-@HA@vLQi2jL1d%T=!$3 zmXA!-27K~;1MRn5rds!_vP=CfuBiC!-*Ktd@~(0$FBmq#(fY8*{}8%XOrEV&0eO+u zj&^mT`1hsv92ws=R8WG)6>~VcCz_312cvjY1I4j@xtTyMY6Bf@p=QHteH3@UanO-& zaU*f}eoK%LGk|$77iczajUK_J3k{XOs+TemsP%8deYB5%zGh={%upVDbF4CCj34D^ zFeFIirV5#7KhM`}Y`im!=NJ~DoXRG0Ko|sSy^7^jA!Dv)qvyq;e8{qq%FkIA%1vcR zka&0Dv=X>`j%K4+Vg&zGcAGM`WNs6ITH@&$K7Wp8W5UL+{Q3HO%9k8D)#pl=AVItC zRl@Z~c~$7oZ;#ol{97i!i9oGxIj&Kr#F?56w+Y?(N5@$ueSr|l31~==_!@R!C0sU& zZ0yWGZ@jAvqO4p+@5z9rJi z(@|YZlpx_fIiE_n>e1(1RF021Q=MS4^8`&C%@yClY z^2EoLOayA-ot307k>eeMDG$r@As+OFu~Xz^l$!5R{0`hbQq30GAj=wB*^1VB=W_ld z9Us%i@)TMDK?xF~*51-3QCdFB>}!LS?3qUJS+xHg3DjDCcooHk=r%rn{^}_BF^UhQ z?35@$;&|jr^=w_0Xu?7Rl$t&xc zNw*P96%lUV9l$qHMFb>JtIymBPQF0RMylJpl<52s{K<+e^bQ&lB(@Z3OVw}3Xzl2i z|EAJuN*6wEpQz(*5UBNFT{{{XjMZ$UPw&eMW%lL%YW;IVf`l09=N_-y7;xyOGAp?Y z&vGJ|s*)H4Y6b4D&j(w_X*Qa5{HBD4wdaw2b5b8+NRSY|_svDdY37^0RhtxHP1Qb@uN~)l&-Hg3-+8gy?bjvOJ15 zsWv%EkcfJElvaLRUkuvyTXvweX`@km)9Lgk0<}VCUZpjjC@p8^{0!f%N{NwtVY{{p zO8z8hhNtD_JV(_}r{D4Athc*~5VWp!qGIQG;V*F9W*3JP%V0zL5|ike+|ulM!xm62fyv*XChFmpFIs_4jrRqFgrBsl3eJo zZv0wuu!0gKL^f^lEuo%=?nf%hU)r2TOqutX3Dgo9q8n8ktJ&DH>#{uheK&sh-vBjt zv@StH#H2UKGETE`EjArX@oU9ns~0s9s5RJ{NhMr4w(2a%&6ccc#9PGssM)6VV=Y~h z(fMukI5pdsnfUL0W_I&=C~r@>yitP0lkXWR)0~T@b>}Bp+1549_$aELjRb0yZJ*uw z&(ZN(Hmjz6GBM7>xSyLl%~*_MHQ%3&YDP^^Ggz64DpW_2{`i<)?v&rhI2IDOzGkD$ zsxDfRdY(_qb{1;I-%t%VBv4D__gYY3f_7G0QhBoICl+2Zf~lFmbO{oERdQ37sYzOn ztpf?=+4IKLx#!n1CIYq2e$Gwam5Ewzs-!z**`8a0yx`q3G%GPANbEV5k5=R+Yecb_ za_oJjK>p!vSz41dBuKQlpONj-LHXJP*Q zRZbIuS`m*6J1;h#re*7ycf1+<>sDs|uUwnve1-&xtf>l9&bw(^o}y}hRc5DVSLS8! z)in{Q<(r`h)$!ms|1u8aEbM{}lEId_EIRzz1 zh}^#m3e3>Xs${zE?CJSSN>Ue#i9oGYg^E!w@0ps78V$NK|6{k6F_dEqB}j-|Hnlp> z)G|#i`==+1OMg-+P1WU*K&>AeJe;)0k|>dtYh_Y*_Rscfbe#&)+}t=;ldi=nZfv%C zTr-h%@<6t7XT0($cUc<$8^=Q8T0(JWnaXptv#NNDD!ATCQ5uAJnF!PpIg*YP*UxHa z{ekSqgm@)y*@`p=HzY_5?d?I8<>zWPqWy=llJzGj`C7}}9t>w^ z3xzB3G`m9y5+YmIm$3Pi154!bx@R56#>m5z^Hc#03Djy6P>N<43p5)Az71pZzjabN z_ODI3^b83SVz)}4xqoRkwzQ6A&IOYk#m^Qs5vV1yT)kPpP;W>3Sax7igd>5*KBy(m zP-uyBHxo0wV%e^Ia~!+7c&Hh(^kX3*_O}eSF4fNJ>i1~oQFoUkVUM4QKrN94Oc1W; zs`@Ak%et)$8~@OUdd^|=728VL#BZ_eK0}<%_it4zs!XfdnX-T-QvS9(baks=voM}e zhSdr6Qc!|K?(xlNN7VtUA@z!gjU}zD#RO00)z8mFpq6L4P}-?`z)9XbBKpi6Z=3%q zhJDRjmFkQd5+ph$1vpFIJV2Ra?h_Fnn=3Ky?^w39aA^f4NQCW|=}9~w>NXIux>DxE znM717WF}BcCtNdh78YdiZPI~^9nTd0|QYQ^A9m`6k zs;uT{)Q^S4jWg+;&pZ!kebnu53lh&2k7e8XnF-XYu+u?NNC&i0pJ#=<0f{wZ*}IVy z$TML`kZ7IlwCZ7X)%u-MvP{smPO+^29&ZzYTJ$WaHeBA1rLRT@o_`v{tQLRql>C+; zfnHoma#XGtxbi~`yVIzui9jv0x3)^B@z#X{VtCCARmp3RL@_4Y)Or86(Hkj`)HZb% zFaF(fXPfF9kfg~S$6AYZkKrF1`m2O4K|*-ag@4yoX~S*VZ0r0wG5r7I>nxzFNS?Mo zxVr=b!4oW4(B#gYCL~BUxVwiyAb23c;_i#PJ6Rl-%}u6*yStOX;Z%>>YS7%rk|6PB;|AKRV-ky;HJ#4SF+6XoO0;rkNstgR;y>L!gFukt@J(Dmpfy;sou6FLBbU@o!9AV+SvVei;^uooad!H+eo0+ zpG78+|NM93yLK&A?QN5zFQ2!!CVdH85+uaBM&I~YV;nd`Mgq0yyO}a+nKp#qGV`Qg z`IOJuRMe`|)oRyH^!fZz89%NgKPi=@S`b!SNYED%txRq;diB2u)DoUQS5T}m@=Upt zmBEzb_fN|4i;|Q?y?v&QhLjcf4iV`O+X+FNT6(1XR>Q`Ky6$XbyL!CWo&Z`|7IE{s z1c?$m2J=Vfw;1Di5-@~a*)d*S^t>|VVy#bmN!Ds@HLMv#s}bw8-y*7GUV(K65mssd z>m9yQ^_k^Em3u7-5+bg%Z@f-ZMxeI+ShD9f^|*UY8-ZHg{%T5B$$G=aLdwLxsPI8` zU>9%tuC*jcoH^Z)>U*y@M&6f*fgT6d{wWC5N*LOL@)2w>Y}EA%V;`Hj)aQ9C(=5Z1 zAR%n;J-)%PalK6^Hm&n3^=w~%8-ZF%<~Fp)-n5abUwhVP)n~OW<*h{t5?lXnPLU)V z4I4oL9oXWnAJv1K?F4Fxm9Q1BHyY#EexwCU+K`FQz3-*1k(m-C^wqCThK*sJ7?U%U z;?rAKwGpV*Vt;4excer<#$~F4`G9JA1vU1iH>V{*;$n#olnKtX(fvU~)~j7^UjJJy z8-ZHe>v!WnE}J$OeRF!!H|G}m=0ph+D_)0EeEnvlSIy%6ShaU$cn?-fMF|p>@ARZS zd0UK;yS4IT1$&m|IUCst)DlqyH9DL3M-KY(>KEz5TT=BQlprB&)BD7*(cUd1yK<`) zpPoO5jX*8?Jy@+C)q+?U$V<-mRZ)V3h$vVPVBY7uC~Nj+%9>q(-XBPy7T*7qJ8{TR z<`_CuUH-6|?H&|S-ec#l)s86M|CDK=c2(wEt17=3lEe07Kte=|6`i2d6f==6lF8>3 zHA4?C8-ZG4z4Xb~)yA>f{bwZeiY%r!*dXc^SrR0~bA^1}#yKB9B9a~6MBAoLkoTO&8_a>|XHk}$QzzlS(X8?7OmE=sUn@w?@N<%W$vcnmxA zYK$|fa7E3A*%oSvBl0U-Zj9q8)uU0gE zIov$MMp&c|%TDjY4fGyFEfK3oJ6pAO?L@=P-Pys|WW_C4E)^w6h&SAyMa*734prE& zrCzLSiNZDlwGMsgO)>mtuilg|!q)T%U}+}#lLyO^AR*q5J{jj3BhRotH*+*>#ICKX zU?WiLNU?#mr+cn3@)>2l*oB8qmhYT5#mQI_B=#O3rV;wHWND+KY;~?c_E)T@jX#t694mJ=-k_65@+2wy4)-3h&v3r0^Ssfh4b`0{i5vV1;UVmJkZHyz|`J;;4+fY`ivHo3aN|1=j zJCiaJnKsh*-l^P}){oVn@2PzWn*?f!@93%irj0TeCn|aJ3}&5I2GDA)B|(DTff}Jl zC)C>%qGbOP!D=n{vJt4oS1#eh)mg@>!M&}q%FaIFZ2Lh^diq!rB>s4}oSxLPjn$8< zJ6kFpOGUCNd(zto)T+~UJx^WUv=Mszn6prP6kB~Fi0)cTg2ed5Ej-`0S%!^VM~^wn z5aFGIKrMZJ#hFTINlA|xvYTGQs z?|i#%t%SxQzp`RAGt(NfB|(DXYPIvB`?mMq@1|zmJ(L|fQqM-9mdF+?&LCxaq>(R{ zTBt9|4P&cm#S0}!@Rc{UYgf1N@)_0T{4IiQCNBUIsP$)&zcfP6hS)q6Q%g6EWO*(Y zr7O{Di&mIvkHc)u_ircK`7TipwjRK86s}G8xz!dDRGEU}Zf6^7uRjl;Rzo}YV&lAg zYy@hFnqkWh&o(^nsf!#`>(N@nXIf`KEvmCjl^%Z6+O-o|Hyl>ih4yBzTYJ;Ww$&CA zw4zO4MLJEsRBu%s6ViuWJyX?2pq9urDxQ4$u?h@LQn&Z)#uhcJM$sac1PS5o7f))P zINr~t1~v?1O9pytaR8r8eQnslOLhXaUNrkhdq?S&%-ZxyQa{ni z_fyqrl%yntKT5A;cJxXy{jGaI&k^Nr~`mriV1pTp^M4$u-Vf+1e)5iGhK72zOAGRh1fm++@iwXm# zjT&#O^C@2{ur(R|RFoibsZnaG2f4tA%gB2ykRNSYlqunEHUhOojfg+q%{SsQj!^X6 znZqlUvJ^pw5+p?a%r$jQ8;!2`^8*u$(*vb~jX*6?5hGpe1%{0cPXqb2Nggb;e`T#! zyeUB{J;Wfm)%x(o^(~X`?Fnb%^YS8Xy3<&*Of4^Sez-sq1zEwN|{&KyflljGB6(_xkZvKAV*iOB7liv?NG~ zT6ZOK_fh8mPy@Gyu5TB4;F%~qwb zAu7M41ltwAC+A;n*hrWZ!~dLjFQH*w_y0qfT7G*nCSHkLZP@rkyHccI>Zz~ii&#!e zg6-zpmVtcRrVW3eXukZP?5fWmJD~%q*`aef6Qp8cTwNIRF2& zSZiEY9rq38t9K1mo1Us@BT!3RS7IeqR8OZW7)!fxd1;dR>O)2Cx-un5i2EjCsA;2B zy8*mm(dBB%fEqRewZw1XW6>k%N>Rnot>N?3-W9yHd(f01A+p6%)+}Qjk3t6Up^H|k zk2~23)DoGlEABFFAc0z^R;QyK33|JBVtQ~GuQAZ2R-|t=lps;3YdWe~Z1yVa=Pvx_ z+Gpy_-od4{KhGepeY zWQ?QS8<~%7?#Z{;Ye4H)mIMj$zA9a1lVPJzc#>M6M>l@6c{Lk>TGgtiNxZ(+v{CFr zJ>GtwJI|JH=uz>_hkP!1ZzH*CktVXXW!Sh$H%}3LB6cVT<>KJW0 zYT6i`%7aJy2l8{<>Zm9|LR7!)&~vL{quJP0Jn?!Pp88im8-ZG4cK)$UtTB#5Yjg9; zjT-S|6vc`XB*gq$_@MPU_4RY#)!q5q^QsTMZ3Jo!>HRmwp2r&BAAc76tol~zz)z`F zXnog`AR)dJMu){3-yfd!SE|SF_2=E=#2T_ipqBXdX_NhT!$w$TmwHAD<3IOR)uN-# zw!}A1;55BmJF%k2VYPU{-hAJus@ivy*%lJwyX{x@yV0vsEl;Zh?}hMT85A3VTH>qk z?M1U!H!sJj3#atqVZSNb*PSUrLVQ`)uD;C}$M*Cm)f!2?`1O3%v~NyRf<)yz4{3eZ z?A45amZ-y%2k^7ad~5`2iLYW2U!s4}FH1j8-L+vbe=)iad0#CF65=ad#IWdJ^y_mr zR`)g-&R320vJt2yzVxG>nl@%X>!QADJdDp?=%uXzm=Yw!3WA8<(R(#1`)HL9jNosA zGpi^;qHp|VTK6$~^{JtELZ)R=Jh^rk8-ZG49Yw?x>CsegBIhPFY8=ho_XlY!ET#kr zv34RNg!FZvgCnC87LJPM>o(X4)Dr7HW;_#B0bhC`VfC&UUhQR3Tci^bBKFISnG#+c zy?XXznk`{ls3rd0BAy&ursdx8rwjt<)f2x>9~=)rV+sPbaK|a-(=R#7abU+yKanW}B}kwzjCOA|8t#bw<&&St$$~eYv`grp zNQZypJAMOF5U7PaP9>>GU#H`*9!|$lB2a<^de0JAqobC)Q4&1PS|IGQEBA|Iu3JNON~D66kNU+rYL^%f7=6ZJ-1R^tahqoI#Wzfxa+%uaH13+=*!K6-w*``qhXTt#hNvCu{9@MDLq;qFQ?` zkw6I&xNB2y-L!!cBGxme`>lQ5#W9c0ITEOa`-iC>q+CgUJuJ-6k1~az1PR>L{O`zV zEThR|VeN!Q0zGsZLE7R!JAqob*IL)r|38}IRIDA{NT7#KlD40Am#w!V zxEoZG%GVezA0PD0_aDmjf)XU`el??Cqs2IoKrP%2`tLY20wqYGzm4h}{PnB6w)~Jd zcgiA&1Zv@KP)Yi6J=wSWIK74&N|3<4os^sE);;-L)!!Y<=(<7)66n*T3}!he%4$Rt z$6p`q1Zs&dBy%^YB<%=p<+mnRsA~>gi6}t=J#@4mr|lW}Yq#l+vs51k3Dm-!nv{di zaZ)bcVxl9O>ItI+3G~oOQnl@~-f}5KV7us*v)h=wtGXld$6c391WJ%Ve;d`>%H7B@h~IS8 zOhKR)?jMz;sc+gia=*If@+AT#NZ5UMe`Q(a{A0mac}@X4fm*nKRFYDSE$7TPrkuQ< z^1Y%23G~p>J^0TgM}@biT;-`(NT8Pe=r&y*?MQX~yjD{WB}kx$j%qLOUgo9u!5l?hOXv)u1U)opTaw0JKI(X35GX-HtPWd#oS>D-&PsciI8OH) zX&VP>q3=a|?$R??{5P7S{VcBx`c=@juB-oFR6n)O2NLKP(R4?f1WM4efi3B}CV>(p z&}Z@gCrqEm^Hf<~aVQnG8yB z?Hya9iXqWOAxF6;(XlqRd!#QWzPVIxor*On#e#@`1W7k_`} z$Ux&j39b&KZOSbYbJ|gH^WWOhMF|qPQcU^uKP+-2y!_#4oZ?ua7Owl!`c?lb&Q#fZ zyK)~KMSJ`b{ml6V(|-2@>KBXRfeP*6eO897ihp$Q@D;sAXSC z9ari%N8Rx1a%&<`f~%)!TapgWYT_(buwVT5b@pB%fvcbNqz-E8xbQYg&P=mDYzwvQ ztDJLZcXdojH(V}H1WIuI5?i9Ij^#T#+h+X|A4^#sQG$e+0h%k5RNo-$X1|bZo8@nm z+YkxV5;IwA1#(@-NcnC3{*Fx4E0o|aN^FTTp0r5k`rhEF+<$9G5;%nQ=;Re!y&$ls0xYT>>q zTBn}5+A%m=0WBLlN{|p9hh=ai#1%X<)ACIb;S6u7ZJ}tE?2N5ViLUzj=8jDf{9tu9v;soWU1F z^$06}0ls(fJx}lRm{X4DbGpj&Qp|mjK--j)BPl^XS^Y=+MXDx^iO z?>ZUxmd1e+B<$l@+v*>=SE@yh3Q41=T7iS&dbTGv>Hdb@Z<39o5w>f2IMB8v6+OPt zvHE6qM=82W@Vf@J@N0>_Kk|=}J7p;1*h(vqC_w`4k!Li27e~Faa~z*&97v#6jtaRc z{_1xlQ^?#8^_)wJ^^3num9tRd(LQgYh)&r0_gjW7QH6?SnPh)gYI!NmcTs|b$XO-! z2@9GkNPa2foHSroyq9VxQ0vf#0+fAbi;+EPdsJUJ|DXqhyWPGA~Vz<9GXVf9iMn)tTQNt*9O*QMg7IGde}Es9((^U z$6czJjs$8gJ6tTWLr;?!y1^x9-!e8nl2&U`vZ#Lviqcx8<@CXpD67h>Yx2TzuN|`< z+if5rsyI*Yx6;T6RQK1ja{DTtj(=$F7285BkxwYYnB@kM>gxqr`8>dpkqDHC976T> zE&Y#PrTKAC7W0PwREY*{Ab}Z(Bq`!{oIH8PmAE@8&LC=GmLo~xx8miW2X@9yBmyOv zNeOLB(yq#D<%?D4yP8s!7?dDEGd0K?73kjB`Q-Q(`Og)00<|#f6xCS27bZWRyUaC_ zo^U9^{8wn3@($h!lP4}%<{D1~N|3+|U6OP?si9o0;6ay*)=Q8;EzAfeNn;{LI-dnE zkq<}cSG$>a4D*p;OH|Xl)(^+C=*_Ou^hJ*nBrszdt$xH5cIK~g-f`uuoj@(j{3c1C zvyE`jpDP1BMNxt|=+HJD-4XL0$=(B8NhzL*NMQau%2M#IqVs0{9gf6TVCVcm!zqo@$pacobb4dP=X1C-ryN>vdp}OHnpcbwg zN>ay{TjaEI?)YN#EWvjM=8eL4mn7YtT*sNw`=(>z9{XDj3CvkV73}9tcFHw3I%@&+)kBFY)ozE0kdVE3{2d21j+j zozgH*f4(`)A$wC)6vH zU_>L@mZa#q_v8*A)5`;B9S$W(V3ejLO_@K|_05ql{$Pr$9knoaRFZa24Ry8tGiSW; z(TlZdv9CZx_F~*C#@W&{G1D&jMbTT1?DT{~0;BweN5I;lP$T0mxkg4Gc@tGvL5UcX zxmN;Pq6mn%wQ|MfAL0kndLl}Yz+D%TG&riN^V@H7dB7s`sd2H?;q32bEyH;nk zCwMEnjhcDrCp^lUeX?nIyj3Gv(7kJBMJjq=;+Ue3x9zy2S>)WY37lJrN}XqVTe zJn=W_4C0x>^MTg_Rcr1($CVUQAbw1WbB+Y|mYxg)M#~%Lrgfc9aYvz+y;n;zPH}#F ze@Z^uR*wTPi)lbC0036f&}ghuxz9x zPz!e}P;}mo)y`)9ANaMZD!vV@{Smlt0&Ppuk=kwL{Ju{4IemGd1PR>PK{Z+5E|$~Q zC?%I9F8~s#h5Joto;Y!y+)phl?Dq80i}8|Bmi>E*2SjzS3%xHm_V+(&Jf zSA2i!_$9@Y0k!ZcVL!UKr&e4g=H6h62iNkK==%*k|7zfzy?URcFx4hUE%A&pcP!h9 zM&DD#FB!E)&O#$c2@>L7v36m%-jg|=zgR0zq>&?mTH@X`cYRY1(@PoRr+!-_A0q-K zxKA8gq9>f+bZ5Ss#S$jaRe};Egr~{e?@qOs^C!9<{qb4uci&E+mUuIWJQI4Heks4c zvU|Est{(KRMH~41z&(*vy)Lk=Jg!b#*K2wYq6BwmVoOwe;LAAqkeVgFFkQPSK|)+_ z)=t*qi^t3Er?bbud~YXEOL%3i{k6Q(Z28NsUh(bdx;USs49-*+l5^MvE_FEj%{2@+xsXXeq6r0Ek%I*)#= zpD^uL-D_z|FeeALBuQ>v9gfw~E;)eiL6jhYIY21pu6KS%r!?{MY+9K>0<|!g3FWQj zyBsSIN64G#&507ss)8+13}pB?`E_K=_-wS6ff6Jz?+ZmCO?cqQxPQESf$k_IPz$rs zNYea)&m6bD{3;Kjs~shn7YAFCq`$g9cND8KLY_thN|3xe`4~P=c9_&^A>8zmr*RzpIEmmuAQ)K>~9x(YL|{OW#w8a z2-Ff^mR6>z_YrP#VB|u_D4H#y1aniNZTf;7kWKzOJe%y1;_D6x%!?&SJ^soi``ozc zc$0!aEzHnGc|Tg^lAm{b=(s`zN-(>ay;p8c^U4EGu5*k?G3&Dvm{%=L*SvD)8fzV+ zQxKRXP3#4+vc2U$oL`!yuZauz`g5ZDpsNTtBzmI!jnw zr~e>KEzv_OXJ(EMIpyqGTydR=KnZ5qwD&6Q$j^?@VV_-9Q(PrTh@)<14yF82TeHjb z=jxcESEz;Ag6Z42Sr)lTlSZzlM4&`mC1xgId#`p~$RtnR9pb8=!UhszkCBz*H}*+7 z`AGHIF7FfsYKbe~%GlevP&zqIov*E=qD1UHFf;4gd-Z+FM@RGKmt4nE94jO+XRahg zrj_J>lJ>d2q##fW*UqS3YR+m2-EYV6gU72-1-{gYb6(!&B4>s8O3=0itQ(ju@x-vZG@BAR2L?VTFVWE`J~Lx}8-ZHC zvV4@Y{|3dA3LE9VWmcPyisC=QYS6BDOM*n5uK6e$`!?78Q3FP0S1)ym;*EOQ3Do*= zxj^F2%hwGX{Z=1Km@+krd#)7OFDwZXD_*;42W#iP51BURzQB`aNY6VU!oG5D8 z(mb2S@w8cl8u)eyZ{1N*QG&#rmxXE7;07k1IS5&j1d$6Lq zC#g?Ey78)%1qun&+P%Ce?J72H)P9$Rw|&u!x27zcC_!SXf3d`ZX|HnKMn=k;(2w#b zG@#rGNT8PRq~xe@(HO_jcQG^?NfMbtgx2omIW&)(&=M7a#AuP0q?Ckd9SOS)FFs0H zGioSn-m@e{wiKj&|GBlQHb|gtNs4P1r4(LGwM^2Kv-RrX`a)DiC#UA;#Fprdwmhwp zVN)dg)32e;1`=K33MP7{&t>>h8*iSVoIN#&6)$clP)qn&JI%^%RH*poo>S>ib~vjL z)s-@ySP~?}*nY{H+wdYE3zC#Gvm#l#6HRRdYQ6uSm#S=tN=o9ced$@9U0GR?mpvt` zC_&=Q#{7v?=hE;Zf9}WFHp(-xI86~7fm*_+BfK=ajY%=h*}7&~`I9mp6iH!8kcfJk zpT6!)8}0n-uoL5p^Ouu7Z3JrlUf7-P!GcD;qJZ|n>_NIbyw&bssFtZEL83-ycX~1u zGR{X~%6_@yd@5dtGGHQsS_=XSQGTC7hK-}|Te0N*>G*+06=-j`B|$>8PrDHf8?BeO zXUdoF>hgQVYy@hhYh5H!T#fpDp8iuO_AS$Ebyp!D6(vZl8|XpTRdK^c`pE;>;nH-lygl4i)avt_2SxMi?b?Z-m-@2V*Y>HumM)^A1PSq! zP%@V`{O8f{`Y`v>32Jz_w~aupZhsY{+&*5$$eUH_%XU8BrxvE_5hy`IR3)N)m}Azfm+3Plt?^t+S{n8v!c*2 z_I6i@dM~^ry=yHA5~3bco9q<~8zC2lu{u@Tse`)P3DlZbpcLJ)RSg@Shla6UJ3Fdd z(iGDwHJNQ~`MX3SRe02V^mZcLc#1xurzlF05WoLvQPqe8sF8U%E5Ev~I+E(8rX)-) zkM^bLjZ@9&RjHp*>|>u52~!dpQ56hJg6#?q|5X3lhK;PjG3;Twi3uB*)wB_)_5OP) z%J!xhHWp2aVZGIU2?6fa=~-e)kPsD)gblsIQBVkFzkD#sIV)9lttL{JxFAh}T1Zpm za;1^%YO7Q7=4USxP?F_ZNh0fNeHWFQ%$8^`S=mT7tXF=e*_0rg4J4{nEt$AvagZ@` zGaLI-%HNJ{p_cgfCtvm6O49k_k<7Di4y9?~CK_Q%kPwjyyO%dHuB#r0hO<7t&6P1< z^V$g15?AP={>_bJ<!~I$J3M-XsfesZVNQk%S z!AIVdDa4W>aimx=jnHQq zoi+|-^5&t+1Fy<90=2|*YwvN>1`;Sig7Q9Sgl^;g`<`s~&MV5&8>J{4BDQ5|iCGk7 z6fnneq&+K;{)6&kd}D2XWl1bT)Tn#%wViRSr21W1c)j~dXe&E`S~C}ldUqWS8y-b^ zurwL3DzRA`P;9FuK_XLZ;lx6fIvH<+=HZmhx?EoNXm4H{fm$N6YDC`7##_yYDs6~A zQQZ+GNVtLuQ?)eH#)`ET=irxuGO>=sFkL?8%1n|7&dY~D9?JWuEEmxZlt0Fi9d_vqbnrDuyJ~RE>@p4 zW+!iywGpT#=5|WvP{YQ)R~{^;PhB=4e{srmYqgaeotHFvY3U4#Ozua_{pacmK zdAPQ&PE!nIp+aoWsrqd7-*y7E9-Ysf=(nf0Q3G`O`^?NEp&1LT>{L;LgqTn79NgQO z@8+DAg)M5@jP*HYCs6C#=3G?ou8(0uc^{{w+0};~c<)E=T1$e2_}wMWBzDyMqO|^a)ufE2OIA`t*i{{#m<(n6R1^mLKdpY+Rw1@AxEV76kamyJNJE^(O>&z?4IC`5E4;v^BMbtN)W;^w1$wZ7YlirJEt zH95PpMpV@bB}g=^nJF>+u}(`;;kh%EDrW|>h?PZb1Zs&Lu5T}f8+qSb z@^@-_QimIP?(Tm}a{g5-hJ6euZX-}D&p3(JWcq8-7qm01#SQ1Kx-l$>vQwf239(CX z{lIV|ciZ?=U!3cXN3%DnO4^E(=?u9s`!ef~FsHT>K&Xgc=^Xw<`zM3|C zjxcA)A2Do6y=pcBwS>Rk?S^Ti?T2XY>+`~yRJs)9HeAO`SE;3CsP|02nmnatwBJ+x zSJD2jibjrj@iq|a08MDVi*2C|;rX?CHQ>gYgehxc*pZ`UZ3Jq~T=;!5xs`-?5BAGU7ta^~}<5ML2 z6xK{d2@=BFEAA*gGxy>d{nX;MBiO>%6>J1*%_|_Xy@eYd-YwbcsX1m2XS)*1(oEly zAhB`jL&}n1+Gw4lgL-A)F!nOZPM}tyN_QxiMnA(w&Z`mX+|ol?oesV<1F$4Wh}FlU z6Z#st+xjnFsV0OBV0S57C=#gEAn7J$;xlaw+cZ_ZvvDx%@uobzoh=CxV)aoxMag?f zYZ?9rW7S(z!&#r6BA>oRpw>usMeCLB4fmOTRDHN6lqILBrlJIi9yza5+(DROW75F$ z>eU7z?3eJGHUhO4^}nbQx{Ys}@2j1+bY(5~)KyV}M8eQZv}d)CVWUvgTeWc7j_h1D zJAqn08BbI6X&)m8-I@_6)o0CnGtZkjsgklKL4xvokr$%3k>~D2c{d(0D~Ro&I|>QZ z5}sH==y~q^w&monwl`)Mmsg-UoFzfR6?B^7ra}!HeQxFD)prLomV!VnkM@VTm^0`$ z7T)vaD?XQDUpv-SZ+}*4ExLy$sWs(**iWeaKDGJ?AIU-t8=J!O@_qRmvT2mJ7TbNZ z@hC+r=vsCgyYpn_(>$89mu_X~E7h`&M5_l!XcwJnBX^V!Z#T?`jiP$nNTAld0*7gh z+8oC_S2rHQlazI{%F{h)NszFQ<6!yXe0iup`?1B>Mxa)wSqHUV>Bp+k)Bt{{e_>XO zx1?`1OM(O*H>wp+HN)pob(J6*2Wn9UJE~;WT|1(9)G4z6y^{yj@M1SVl(U@=BwFlO zse)`b^SW9j^A_t$vyEH5Z3Jowul_TyZpJwuM_E-%)bn6&^yWkf5}9J-d6Vv44I4cl zG~}V}a0e&gi7IYbRze?aY-y&y>X- zoHPTl+Ct)3@E_#S?5MpHB`GmeH=dX&Nohot%#c8>_uu!>v)$~K(zOe3u;Hy<gQ;S=B}qv<^J-?; z*fDYlZ`x*}5$tB}kk(9ZTN$CWejuZw7Ps zKV~SGx4PR1)Y>_C6TPE?4I9nR4dp94_EtLFmsOM?(Xi%bvE$LO(e3vrzF>%#GO9#D z8-ZFPQaaDL+J=p!h$x;jYgT1PpBgGkkf8lnlu@F#VdL$kNWQPMhqAMwoj@(B`AWW2 z#jx@Ib|n90Y9Yn@Sq*w#SrR00Zbv&5q9S?K#>JFzvcpE8)}!-ls6MBkVPhqEEITFL zb;e(>K;MIw1PS{*(Z6F`9)3O*n?Y;(O6G-B!>NEaLaIqd^-&6H?G{YCfNZ!MHpWe8 z$A3QlsZ9E(yp2GuC5snxu?C^ncRu3LlRy9MqVj%HAUzo@2@*|bETk;Wg$x^ykB{Iq zfgRnQ>L?sQn7fGX^TLLW1Fd`W1kYniv(LH> zlR&NT$4ejo~T=+okcjax*F zAYvgAC_$n_&!rTZS=<;$a`$2U($vn%jsMZ zB0N$Ms6{o&_?o(=jY|(w@xYk2?7{xBDoT)$@6F;OE<`^c7jFCT>GynC#@-$_0=3+3 z%%nNBX`@`#th_;^=4{ZK5-LiNNY{EM?W4(S*mxe2q~-|e#(q*YC?rtp%<1Xc$n`V0 zgz85nQT?c#6f=PmB;H<}K^Y}X8*S*i%0<`JUb;$PJ`n7Hiew=(cxeq1c^uIyHiZ6*{g3|l9k7u zyR&q!3eud~k{}@>53TpdzNcJqW$MFD+^DLe1PPI!T#Q^FN80H}l~=~98WN}_GB=6$ zqkgQ8j48qDr4C?im!{RK9Gem(dgN>iZ|8UPW*$X5{}z#; z!fU4YsvBwNp!%}Kh(HMv$z|cM zE84RL4{E6>LBc+c3$!Y6kUUsn<&E( z8RHQ1g~h#+6d#&1pacn#p-Fg{^<(8ra}wW>Bt=AKB7s_Esx+WnNT!XlL<}aPCJ`t> zLf8}jd;RD}Cl6x};~Oah3s+W4-ty&-&X>@vzZ$_P7QDEoH9AbD>cB;fUS-%gkhSbN zTj|jyP(=w6oo3ac$|pq(8-<% z2@(Tx2T-*e(?%E%XX`I)R?b`~Ya>vLRzA4MZJ{4so~aw-v__`T8W~EEupg@~w6

tfh@Gl~kL)ifU->3VWfwY8WHm5Z{e$h)+^uX!RT=NQk3wx0Feo z%NNN;6{)1|FIq!I2@-Q&%9L}WiZPDg4o9-V*FDwCrz+V9)DlO%)|4tnuiUzHXKr1R z)o1kPLe zC1#>IyO_!Cpm{q=kPyGu4b*AMFn%?Nt?BB<{pN{CS*tD7db6<}dHZC;#+UR>*z*=S zcw<*Bs@rEtkPz9^M4lA=3743G-Zq(i_(}57BY|3XOVy)_KBkSc^qxI9H46`>cP&bg z5Kq@^sZASKCU<5-JHJ%BZ>(e^Q0tehb@-Q&e#ST)^ki_*cc*wVpacnF`^i^dV;sv0 zwPo&GQgJV;?~DX$iKi0XxrU9X;GQhmbx{q7tgSt-ObHU=X-8M7VWY`Inz?42phop6 zZX-|&?@ikOF}Et)HM=Tr8Jx;?*CHXF+QN6QpN~%&hp@npGu2_=JZ@Nj#Ue_rmSTgH_ug(iAPn(k}4jsTb%YH?50ZNzc6ewb9xMamUivHWy#pC_$px z42zZ|^6)b9@N(hdMFO?NPDEi(Kj*L3XJqeZwd8?cN~$P9LVN*SZdAuOAGC_YXcdQd zpjjUhs6|nL^b|F1BvI7a_&Yv)Fg>qOf`r%~Rcne#^h#BPwHsTPUrAMj;zcb~o6r4Itm0S_B*d3xy?v&QGL8tvO^)D;Zx*)^ zsC9kqK&nz1V4OkzEWgqH9BkreF>XHz?C>< zELAM*O%)5V-Bs=bcst)fZM1e9zrC5EG;BDCA3ZFpB3jmwxOsLsdH8jja#@70RN9sr zz?;tUu@R_M>Q)r(iZ^>Du6A0J;bIjBB}n|d90@jtm#nAU*)g2&80=*uP|H5@ur#%m z>gghR&!e6+-?fej5+XzNsnvCjbAD`KZl(U;k^JFO*+!ri9(BqZks(HV*Uq3wa?}#P zbG7ZGwQDCXy`~tl7We%Q?yYV+gGh+8`E_KNan8m1m^fa-ABAn97M_24CU&@&@UUJC zn|Ps^y0-2xs(;o;YYPdqCrRVRr&g<#iDp&PSJcK~_I=LF5ftOqTN?+qM49nQRZ}PK z9?ojd@U_`M;#lx-Eh0sqm*lzqIibLfDE1qDZDU)gH6m|+%8je{mhwQZ2vmKKMz9WH zO;wa2K~V#|*^E%*3_86Rs2$!8Vs}R8u@R^xo?{{=RljQw_^nd&v>M2IgyzwnaHa$a z@g$z%!7+e>}utrM)x;YTyxmPt7DW4+v}`6u-^XQS3zN&* z2-FhQE9-P^WsIZ6zIMD_{%=ZRqLbc(mIMjP^+YT5Ee#tr7PsViV$!qZn|1=VzHk1U ze4s5Ty8j8iYepV!&EJej%{B)Is3<{#GAW5Uy_w59FpR&bbx6tFG`)>Lt?B(9)9j$B zVWUz|BrmwSo>J>Wep;co+M-xq+S9AIYbVyQK735y!^&@!-D#Jp)fN)9tKX&dfncLo zw>tFTQ+i%gc1?5I2-JH2{jy5WFQZq{)k1k?@1x3crcgAMB|$=D>28qpi$SDI7sgwA z{-IoNA3$rbmIMjW($2vR%wBC8%6A+dtvsD4qNywbwbl(xpnXmLhK&x-hx6#?pPZA5 zb)l!v4yp=WU6Y7B^y0f;oHdGvr1MeRurdAX5Po>e3}yDsuL;=h=A)-*H(H>kWw){K z+Hn3bLlvd?Bwvclu&g6txAC_5Kpt0hfwDTQoj@(o!&UCg7{}T1!?>8qJkL6nR#+@a zO2QlyWqh9-!)veX;%rl*x<-hRi+Tfk9fES&Z>nIR*63W$u<`z76i=VErjjN1mjrA# zSKB|uii<|zF_a`P=LqgGX`=I7|Khfzi-hQ{XGt zPl6N5RR2Xp(ETexLX1ho8i`{lNmcGgadmheHS~-YbN4TSS`>LmUb=jSjmaY-dBaVf zYDx0lp#%wvHYLw&KEp=Y4^h1AhtCPwBJBifb?E8RcK7JxxSX>;uexiT+H~?nx|b~p z5`KG*Q`BY_qgP9_M|0*`RV`WNdje{mIenTu581Sy*ojraVf^|4mzracH@yw4wvg!d z*LjK(%xa8d?dKu&Y@1Zs)9`B-oUqgOEl!g>8)R;ZoowbAZ#Q-Xv?`-f`Z z__T(N9L`YQywyqdiHC?`vD!i{%40$CM(GS2Mel_0-Ji~@5!cJ8C_&lQ*5}n65CvE#qX|5#}~#|wh^dBJ3HvB?3-cZ^yy}N$DJ&^ z?&r#MeOeMEC@Y(`$3?d>^=%`rtjxu0=c{BRP>b>$(wp-=eIJN%SsIcMhO(>;CZ z%gd4=LA#PDBL1T>4!4QT`1EaA_!RO-A%R+hoSA7B{=p!&oekuHgFSemU#r>()S?Wi z6rJ$S=v6LSyDC9{wP^hcB}gplpPP@!`^q51YM1ycLXi@iyj%h+TfFq_Z$+;doQa%t5KE&3Cc-Nl|G&sHU`i-c)6=o zb%fT!kwC4=cS?y2K8B5_ZW(#c-7R?gF%F8~wIoP<8Ci}z)sGDuBNwFN-M+Np6})QN z2-KS1ue8XbW7v4{@T__zDTFud;X^YSOM(P>OK64VfkAxAk*t2o(VcIn{evh$qTar$ zwEFRv(W~>yHFd|Xo_yF?JAqm?I{WbhCzA{tyIoh*22u#Wx2Yt(fh`FV!m}ZIrQd_A zi!V}h-5Cc-VDWG|N%(jHq5YSVN7}oD1ZoM-MvMJ-jb0@;Y^ffJjNq9^*P~c9OM*ni z+_u^{^pW>(dLkiaZ457;si=)Wt;zY@kuTM>A;w{@k)Z?$ktqC&ga(WO7(QZ1g4Fmz-zuMyIKqL3^J5@HST)rfnB4eHeBf$w7Y5{eK) z0=2|%%SMT!YyAFnr*)+Fdj2aj=N0C{q8TTBrQW0H8nH8M(WrEeLC<1%X73teF8Z$o ziOY=+(r%91w94|Bh#`O8j$8kQh$Gc(1ZrUqDNA#+GjW}09MfnVC`n0J8^a_0;$s!JyBe2$Ypwe1ieObUFq%(U5+t5^{jEiR>f_iIJ5QapXb>xtT||pm z1Zq*`a4d6mw@ukZ7_0hZg&z6R(#Q;jY{M z?C+Qs6xWw3QS6lv60z%7RQC|S*AM(oGo=DXuN2Q?YEtXo?A$-a={w5mFV&3G;;%&C z?KYawI$VEa9S$W(*lp16elB+R_okIQBv9+sh@V=-mOhT)v8nhU*W0j+BmGpAAc13& zq!_mhyxCu^SgyT=Rn!s{p$9qLwb9}c5zkP{bw`;gE>UI*lprChLz}e7D3_f-Slx_W zFPGbPbWuy>Fcwir`u_7R*UItR<7=^gj~K1iT9TB6$e$rH|IzN~^^7+QEX97kTFyoY z+SC#?%ZpAZWZH;!~8;swhE1RG=1nOmrJnDQbZJ*mUy5A%R*2)264YRvw0p zN-=?aYb6iXV5Ll}YL)~EkrjF-MF{COTE#Wv*Ux8RUq1TS2-I3rH@%i)OFvd0X|H8( z+H2XLqD4@Ggs539cE0FD@@0SSH>o%)P^JRK)>;xILK1KJ*ZFX3|3o6h&tRfvo(6>bWq_uZ)cc1w6It+r5Wd}3xT zADM1rRnaKExP5k|6@7D}1c_{^v(PGP8KYO{iwxretOoR1mHlvKghRg@qhdYdWM+pw{OcG2y8aM#)A zdIcMSTH%kgXc^4(kq@C+jJOgT(M$%l#P33tDroK6iNGZ^f2$wl+*hUsMQmAZAt8>) zrA9tRuWE0m)u^yQXL6P5HUhOimdUCSdap8!iQ(_>4@r2Qw>tR^EC~|Aw)dnO#<6<8 zbp)T8{zLrPy~Q;frUZ!wNtrc5ze>a`UyNMr9>BIxOZ>Z7_tSgzIeiS@GGkvt!c!lO zFeONcqp;$2O=IMfztLW@i;dKA`zqK7)DpQ<8YKA{Hj0;wBnlV zTR8V_zgcx(CiZPx1ZoLdMAqoX>cE|W{P5HTYW-T?6m4j=CGxOP{GisZouGXg;(SQL ztB(>S#P694b(;L=r~C5s-S?_5`g;B!!qgJ?BSlmhy}It-gFEhDSMP7BMcyS#g6-}c zoKed`r$0savu?ahK$04Ezov~qEs?)w*R-or zwlD27Heqhv5j<#Uaav!0PcyFu+H70=CU2}ZR~NIvh`9}n8QJAtWfFp0Q)T%crL}pY zDM3QibQf9c^x4v{v@2yZt-n5_T`AZWYKi%y@HFUiQE@KB(?Mi!MF|oOlD>eA^dFxk zG~5uy3tjiO5vV0r?I=FZurZ->5A_;7NfmmEq6CTB)qjZG)y6nD?Qm;ID-$_rWdaG* z62E)oG;J*YGFx4}b`an2r2>5wSrR0~Jn`q{28NAs9p#(@GH%s3j`9G@Vi3 z7{`VsE7hqX1NZ~Vc!Cln#GFx_C;fZ`E?BA7>otI1PC=lS_&vR!X`_%|y!w0NFwSG^Zf`s_SUGciUab0!wKB?~T2;~)jt!N`qtJAF1lo_v{@pgXcla&vnh^?!uOHiH( zOM=9J+-c}*)wIz!V`}cdzb#Mo*-oHVwW?_-BbRBT*P0({i8}3hT;EbEN|4yFG_BSv zeeRQrautuIT*W;oS1}T(C1#ne9@I6)v7h#Hia)Xc10_g^m2-+vFpky#B2a5yfwYOU z51KZ_{UKuR=26TYN|3002W>R&SCmh>QHQTwEAoC=ZK0N!mx@>{{pmxw`ndR;L9=j_ zAR%U%BDbY}tVGl@MKAM@6uFE9YKb|ci2c%uzcV=a{uX8U=ZUh7KrQiGM7Qa^sv6Ur zmuQ}q|D+thC_!R+zYH{I3@~gMUlZCmkU%X_8BD~g={9bctIfCUE5#qW3aThUqI8vv z8ljKld55+x5?QI~c4~eICnI-jhKT(=>vl63Th^Zb&H_77STBt%P=jd9@-%I>fTekhlR zjX*8o0TAyG-A4H1H0)yA*1QFMJEH`NJmc}E_&U@2_Pu{G|k0PpKDVhoi)T+4a4(*CJZA5oXR(R*`{2@JeQG$e62N&_8 z`l|Cx+Hsicr$0|kI}VXREm1!~5c)U@Q0zJV@#^FyLkSY1rM~fjhK=HsYiTUyTyjvp zB_vR5$>JMY^sjDX*g#KKG>?;CSR*o?SP~>eRyGlhtB)f!?F_q+d!;&!c7`E=TDjWZ z(Bg%48-LK=LHgq&Hx+7$JZ=d?^>*z0{1WJ&2(d;^(_}t&Hk&VXDC(?)CNkO32 zn!2KXf@$MX{Wh#~<}|!3?e0ej5~4P!*b$*0-T97|Y+v>aycES>A%R-OcU;t}`sznl z>{b_duK12ZNlHTOo6rfdBOVD;f`s@jzV7s1HH>SXm_TcVdGY0I1Akyt1fevvJt2?bKzMnPZ4?c=_sXpHu=LBJ`BFs*8gqkaA;Hs^9~ zdfWI?yp1J6BHy-S6!BZyAkLPFVqpXFss%dLAg{h9LE_2R6ZE9^GI}-r^>8+KoR2yq zpPfLhI$e)Yv}rNJMz8zBS!|y=YSzA96j5kNkeJ@@7-gC&X4sgREt1LS6g5K+JAqmv zW1d(;){pMkCXvj2VKp^`p42EoBDB|0ivIL4Y)D(WGigh*x-Vxg8-ZH?v^Yeu289h9 zH)zjj$4p78gH}=p`$H!EBb~`Wn zuC*jc*pF2XI=UmXCaG=d=;9F-_C%h6|9f=Dm!eFe{Z^{UGkxe?YaMqagl*wh(~ngU zW!Mbba$k+0ESpH6)*tT@G(sPTgLa>#ip-`!uIU2@)bRqVNyvy_zsGfF)in z!k?X}VIxqh%QMr;44N|-1?KR5dBsb*Nc!*bU1KJh_MN73F>%d3wB zda%jccROud*&IA}fnHhK`O>AK{Bhyc_})}E90}Ay|EDB1ToxkxR*#L(`+O9|kXW8v^m$@S zR26Y^C;7=A`{P^Fu|f$FcF$;`%Mo&t--7tXFYN?sq5qTWiVdjloLjiIGiLZGis`Yu z$mqdD+mcl2xh$W3Sv-CaRggpp66k4^q@bKWGX2HZNkO0%dKx9^)|D3W#9GGt1Tt3R0f)hOC;Y z@Vc#zXGx+eyH$}1ZPHqh0M5+uZL%WD}lHmfu6)<${DssBruTG(4!QA;!5F4~?jBr29YWG#w+;U;LKWqc}>CzOu z|8ZZ=F(gtnG9JR!6)4OOVT7Nhx-z~n`k(RD@lptaE zD2AuLByTuX#!sQ=6%we0)sCpbYV)J=hwsxIK6K7e3q5~!qTvRYTq#p}c^MHXK>~ex z^mZkfOFsQB&yBk8_m$&Z+3&zzDV&>T#Gxj`2Wn_-RyU6e|?_+`#gDjAI|yg znVH?49XWH(&twA$)ROZf?Kv`Dtv#Zn>UkAupacnAd#8-)N%p9>lI~GDkqsndEhj$` z#T=-;P)%8}n7Ws2U`8e+FxwGhhn`MX6VG+26{){M2@<&Lz}Rt5C3nW-OT8uc$pmU) zwj&~nzEF-A9i-Nz5dcb%z}*wZHYN#D#UG?&pc)Bm%WCcad-Wz2RIhbueY1Z z|N5f*FKJ5k80D!(0<|!A5#@0olT;OdzYu{EByd-b_6=%$Q);b9p&s!gPz&=IG3IHs zPx-rd9yJYR)kO&sxVuPWhJ$w$b|9VliEJQ&S|V1Togay@b6t)r1*5a6>B$C4kicD1 z8tG?9qKd!MWCID*!fZ$MJ67uiQva*3-xb*|#ApJu_TX+W#rSj8`07k}CBeXq&{YGDZ(+dZ+LGCS*Or52S6B}ia?B*u!)P3`{k z)dSBp>P3-2ExG2m&XsZn<>{gPNOLvZ`^RiIm|chBS$2G*78zH`bKy=Tc|be)e1s>5 zoj(Y#M6=*Mjg@u#e^**kJy3##=(X+KMYPs8(p@L3>#xVv@TcbZM|B;Fy$ zB~D%te zL4wwf6FgviHh9%O=2L3Dm-TJd728cw5c4K1^9cqgRw5f!TE!n^L^CtHQ)Sp1D*i zlpuk-vNZM?`Gf0fhMt}|egtY^b{)pgi6#OiNMNoU#&-4D=jwmBt7!uzNZ>v$eVu1Ea&J&8s2?^((yEq| z&j+ zWQqAOCYKTu+RiiFk3cQl$7L*Yy~awm>cO6VM4$u-I;oqG$A+<(d6Qk`=1%tX@gq(FHBLXEzV3r{I zs$EH^bU4=B6Xi#smV5^{HYum{?%LEdfC#*UxZ8{OhOy2wODeOuua+oOTejtJShK|1Ye&DrG3-=8jLVTl8f;)eB zC7D1861b=L-vnym?wL%W1b4I0wnO;W90}an`fmcYaMwvDP=fnL|9_=I0{4()8+a|$ z!aXF$BJ)*ntKlzV_8f|&vyOS=>4d)#u=6z+r%?8+$YACv7Au~mrw9*)*QN)6vnzuV zB*gDrNo}Ip{3Pxg8{cqTRi`iK=nHC^^~+oj zX>S6rizO6Mxy1-XT!|u)-oK|dJRIceK_eL?P)n>|EIPQwL~;)F@q6pNIrL`)4F4}1jJUm5=kReM}mZ0^RHiij%`+U zmeQ2oDBO3#ohIC;V(jymRce~2Wxa=Kg#{%@$TrlVM@pUxiPYLOOGN^;#EeXQxqW`f z?{Xeif1b5Jqz|o;p#%xMoAQ&0ap1&n;j1K06Js1VnLr5=7?n*XP=W-;Sd$5qAc3*_ z{+mE8jIkyYC_w^a5dJrTS{P$3frt|Q6~>puI9?bNGJ!DXYA8VhV~8aXrVS)e3!^;`u>>MY*gyic#1rboVohL(KnW5U zF)cygw1EU_;S-ub&=2|9MFL~R{WpPH_#DfGTyv-0u+?P(B^VnQWAXksfm#^NOeRo* z1jZC*tV6QB>g#9g)w{HQg#>ERdH_U83p&dF_LqqrnRY0N2lR|a@DkJ9Jb+>oSx1ZrW#G{(+9->ux~xFjT*RdYK=8xo>UPQHneXSOOi zjBc*%egtaC*(1tq+^j5$p5e+x1WH7<0z0pWT&iv_)+?zWUv*{mvw;NW1)=wR`DUf$ z+0HJ}U*WY-i`Hi#KHP~cE0uHSZo3K-A!|8VLS&+3&nb%9dx~lbe-DYRg`QXfm#^(jn-bPM=Ob|r&ekZff6L-j3Jt5j#6sgUoNqhfdpz{3tCf`o{XWalno>{MiwvL%u!o%{&Y!dP*P zZA(>LS(di5(u)X`AR)fgPX4IxgJRXU@0NR0l1B;>sD-iO%n?A8ZyXRsvu!6j9TFJ% zO&&9#1PRO%CEGv(wP-gBBFZ^d#JZ1@w+r)mVZ1fj23`wokoN`oM%ppx&<0A7z}!`` z4J1(OU!}6lL5N*o@N|2EA4?2w8x~TQyGr(M`XgA2Mcl#N3dKg?2Bg4%%gS z$CKX@N-#?$UWrz5`ju9n_u1~fMr#=;K?1XTGWI@41+~$+iQZY{bB6?KVa`#;+;_^V zZtq<0eez*J31&aVD=~ICu7n!u+2ZX(o|z~?0`s#ncDG9n^+ETep7bAO0<|z}EUks_ z4ps{mZQg%e=XXk*tlVX-GiD>)-q945|ki;8Fm@_wCSMo@aa-dLHcqc zfm)dNm$6)%4=R0#C_n^CFt;#XiLqt>JFeuaJjvtsvw;L=Jf`e*_x`7}xjD#F*N;Fg z%)?EmAd)9GcL82}iKO$bPEK#k$}N|wZpX{Y<0Vx+&HQX2fmyW~`)SB-rOD?xo^E~w zYRRj$AGh38=r6Vl5hy_dvuZQ;fe8Bh7lB%sBb%`^sqQM-Go6ZULpD%?S+3DGV|(*5 z^-JK8*bmfJC_w@VJa9L~Ying{;guNsyTbrw#;=!SBm8V2QFH$uI`7chvQc$GGo?r0tFiO^2-KoD z$+VZ--XdlV>Z1jG{j78k9YdoPM?!NET2t;|p5Ty6g*`P&khr(OLn{iMtn(lLMW7b$ zK#>3Ll^``>TMf@v+E>G9i1-%aYP8S7m~?$T!ruoaNQlzfCrQXpe^(AQ@K!I+3BQ$X z)WVY_qV0=-$5@T;qKXdIGx>-p{ZG{pfFaj#g&v|+!NuR}vm=z&`S{P-O z?qKe$%8}d)l#)cC1mnHpl_(e8r{coRcLR#|$`Lq0Ea@%Br^?*YfrxKTDJ#fu&_E-TOL94f^X$r9uL=Fs3kL zInue5a&vl{`#2~;Las-W6$RCwAHDQsrS}8RMB#}lJo8284K5W_s~x@XQHekao>#*w zk*AYhOntVolcyZLA1FZrPt-BC{d{4y^rdy4hkm6(Ej+=;Shb|TE9sL=@D89+hVX|H zXIVrX28@}B=OgKi3E7A~zs~a)*}!Yz`9Ul#eWQX(s)?IMdj9q+6%u&Dk+IYB7pP~; zHdQasE*V}6wJ@G3eQ~;XQu{qy;8Dr%6(vaENlnI{&s(k3IB>!nOSrrM%~eKrM{wC=+>~DQeK1FmH;t!gJX< zbBqMWon&nEqz7vIddb|)56c84rx*M8zPLCou60u9 zUi{{{Lp?RNE^6TuO5^t5cB)0!R#Y$1i4iOnp1enUj4kS&Nj=?nuICu_i6}t=V*`+X zyDOLKT`|_P-j6^nj95TloRMwJ*@TPcYA6xEX$9SkGk~_~e$+{>-U(ah`Rr!{35*9p zksP|6SLXIS@2NqfaJ&|3VU!9w3$XXGay3P~rvecu!FU;XB|2X__MAe0p89?^kib|S zjMa`lq*QBh!_&%-KrM_KLjDXd&nfihsZ9h*Fn)<#s)GG~Pzv--;_cvP0||_ z-0t&l0<|#i3uBd(B}$e=A3WX321f#XYUb zEp?O0`jcPFOvS$GiC=s5^c5EuNP1yvMvaXQ7PJ4Fv*+4?{K6cDQ#=7KQ z?WRBVc1gJ&s3p9=?U;x3t(}!U_RW7w9VL(WXoeq(5z^hpA96$_sWS0 z=-GC}9hmnh}wAc|)8GGZJ94su2*cN^OuZ3FTw;h+2v6QX2nyJhnB^Qmt zQG$_N@k)$sO&y`uPt{tvOW$*pAR$g|I&o`{9-g9pyWLRPP9sYsPz$5pCbU(QXk8(( z;}cKKo+|!$Jzw-^jQ5*B(2pqdq_-WD7zuGkRh;1^zk2fPn=|&LI%jNs_r)cV6gAGd zt~fJlN0`RQ(~LE_rK_FJq*gM~`Yx6iuZ#6#Z0)W`?nM0!w5^_D9){o$p)qcw!9W%flz@dr}rOv&7gEBwF08omW?m#2k`-i zSLtf8K#4#t;e{yna((w>66KmGPdO)QQ@)AHm4?yD_$+3Q8sTj^tam0F%{=5{PfujL z^0n2x=$3qco+L)l@=%IL=17qEvSkQm#7Jq<6{B@56=iBR>)|U^ z(V$Vh!{Hiwqc0V7lprBmoqUk3n)g25iyz5h0E^{QBqL z^yj%NQ~p6mf<(oX{l)r#Rr3{J`|-BlcItWBRgwtQl54KL?#0KQIi_!DP?|D&I&Ff4 z@Ytkts+Nt}`&;pK0}>h8FSsQFwXoG0E1f(GZ*j4Kk!yKAy600VzF=kZT0`8?!ehs* zZS~HRjMcfzSvH;?E6g`#=0@q03Pmt;BuJFMK9PEbVpdzNp_oo5DNsf z2l}>@vTU@WD1wrThrg+Aw%5orEf6*{FBS#joWo zZfJ4eQ63XVf`sUk^OVbH)#JsaCj2C2mTO0u<&Z!vk>#9vSj$EwiZz*-Vom0wSd+5E zxvuzk;oIqZcE4*|m+v^7!#H*^pG2S*zG;k=A>slNlZikH618uKQJmBqmW^-q+VFNi ze9`wEmET&_!uOF*J0%a)DyAM|oNZl#GURO#(Pk-r>_fgF=Xx>cFn|BkY#n*s)F;0` z5#g`=l2zKT6wCVWr;d;a0|Ywhs=+8b@lR@PC1M8mFgXqC09bw89Iw>5=wLO!Q_kVv4G*nJl} z558XXk@tgEmhz=6XrSmQLE_ZO8T2i$Y}pw8FdJ_&w4PCXMPZ3REqwnOGbrP9UCKOt zhO$nh1c{0%XOKT$CCf&d%%8MJJK7m+YaWR}E$k&IHdTvgt!uWS#?uZV(o+(Xe}f^s zWyQ0E&l|0gB`vGv`8dkRH(um}be=>c#LnNgS52*Z{%K@M?aJ#>M!HUJi9oGfN%zxv z(uUS^74r6jyUe>WMp4R_iV`H|9NI_wYRxPg%JAV@{b9om{a$5>KrJoxFLXYxk!2&@ z?Y-{mgTjsP-8|B|dpX=_d%wIypq9v-*08HRBFnJ9mHRPe z4OxAtl8zE2aBRm|-llcj5gWpduyR2Xfm-6XGwPduX%x@+TWT%-msWbdw%x@%#~d51 ze9=opy@?aOTKVhUjfyEt$I(e}>KVFp3g=08M!VUU%K9k1laaVo3iB(BV=5eL(LT=3 z2!3#KsQdWCyi_V@tR}{A^p!E~;gu-j$*wWHRHfyCeOZ+>H zmg0Qx;E(a6`N$hdv{{$xnuIMuLaw|jXNG;40%$B^58lWB=G)|Pbwc{92}5d&rjb$ zF&7{|NV6WT(*Gnf`I^@iR}yU~>c`k)E00Ne>ZwtJg!oNr-X!^4fyuUE(Yey+^F z2wO||4nO=ufGm>Y^GJV~iqbn?Ho)wlPj zh^PB0@@Y%@)}jOnaYjJ&yT141{)y-M=ctZ6dRTFZK&=dADw<;k-`MB)izm7H6(YjVW+8+C&&z;nU7b{g&N3HOz zWlc{4ab1~cb~cUiIi?98M^Tzlf`m9-BThg0N@Z-zVKj5q1@S{s)gp}4u%EgKse^)yC@ z{HD=RPDcq6;zUfR*k3Ihn?4OTmbsQ{LyzW>2-J$|>(wVLJZ#yxkYxiUkmK2au6OZ53^F0Zp}+}bzH7&&6Bwrp@!^J`#BkRWd{ z%EB?rvf-IC*tp+vj&?SrrbM8Y=&8xW(XtWsE!;TvZIip!k^*K=ZA*|?oqGr6Yn^Ek zVzwe?E!C+nK?xG#x2Mhwt5lowhZ}9`-gDpi7Vs~^))HSeI;&yXm^eMmm^ylayGP9T z^sRFwcwPKHGB(JU?~aKzG9*w-{&I>rvZ#mnpZ^y}_?(NL6Tc;lCC=X76+1n`XtJT0 z-}mDf<$!5zzD0N?TA|;a$hG|~<-DUze<%@8iA_v?a#k1id{6t%yrW$=ZiE{r%2bgE z)Dl;+OGx?XFN`ueOe(L%Hg@T#g*L=V#0+J2+dW+S(Z-&&m9)sdB3`&7L4qPeQO=pY zRu5P7=zLeDcj0^-5275>G3K>s-;N_K6EhpniQPg(XpnE*F0LhV42ad*w_o;}>$_sz zx6sbrR+NG})l<6G{S?j@v{xt>on!s$g~c=i*lW&G&^E0UEy$>(za7pGehijMg@l-8 z*tRLYXv7rH>Z}nw$FPbLfm&jhRQPuKa=ujg-mj0Yi)Tt? z)Io`V&FvL$+SRv+Miya9kib=Liq?~2tn#c#ewTI-$xlhoQ{$B>Unj>7qo)LSGw}O?UpVr= zIyX%TJU1=23lX>=s%A@*a8IH`vcB(M#s=848D;y#We0`CFZ!|$#`jI{{+8;*qd?d(S_ofxIKn=For z{67d=3%}H|4ea5tR}=qkuhmd&^+I7vsyww5`b1lT1kUv7JWjF*<#w9sAwN*5@LH%P z*JCf8qxWV{=bB6eN|3;{0mj;#3sZ7VUL7K`6(E6Ha?RH~=p8%puZK!g@~pwNH(cwJ zzk@u(`q*DjJW^^Cff6KeHIgy?Y${K=a<7zZegtaa8aZR5g1acGs@(QuA_7;oam`vD zm(=e)QLR_4f;&~L@64z@VnG7emgT+#uZ3E;w(JlhniWcLbr`SY5aRoR5+ra(=)Vcn z5?8WUDI7wS3MEM39+E@YrLwPuTDayQ6DUDK-Vt&b;V0v)i=fv3JpvH*KnW7^9unF> z0=01T!Pv=;KPm4|hL|TUaP*2JOT16yO||cLrT6wuiZ~gD5+v{~V64>qL(06aD!^6weDk<`K zKD-f8ft`^h61d7u{_VXtD#{-}Dx;_#NT3#$kj7UPYQ;?L-dkBjcMv81gtMj}IdQo= zdVeqF36}{$+gjKwQM~YeD_l=rh8rVR`o_By^@5AIV`9Wg&l&BO+$WEVyGGS%)wq~5 zkgxNQrLG@ehZ~c}1eqBNZ3z-0wkgHv;J&j|} zTjWlLAbxgrIb-lC1PM_mT2D4xfw7@}a|8QYs3opMG0#GMvD@sqfyncN5+ua$t*|s)9?WQBj8H;# zlpuk&>2yuHG{!%7n-~jPgh&Kx$)!p=yE4!GM>*qFcqNg^OrQ5GZKzn;5^F%)^QI0J zvtRLLuuDrZGbdE$$9^tn+&vsD)dPu-iA%2h24Q{R6#g{emwGQI(SE`sckg&Bx3C{(z zv+5D^b6I14rXZfSm|I5)UN=LTd17^#`&Jt$S>W&X@Yqv6$;64{mJN0mycvHoC zz}gzYny9p6R*Y(EzWXt3+-^_BjS>8M>Y@^XT7xpL zqv$kMHSsnxcCTq7@8N|JJp1@S%6I2TkPxjdYVNbq&NVNj!?SQ+`FW5Zfm#&pgKYSo ztB&RKy7Fg>;E}^C>f)~161*<9A!9SU3{fio9nKG@3X%xal3UlmhqGJMzoqT(3VjFt zdQn?~g!pZLedyfw*S+S~IT4+~!h0Zo+qC?RDpF;sNpz#rPWb&0gxzb4-%d|`|4OiW zx>ua%Je?Rp3HCU6C7Pv9VCuq{!k!Z}%0LMc`2JH~fFo(u-9;Z&S?EWgmRyfgYp*Gx z&zq|s=>!i-kdS+ZTg$E~D+{buf2NTP5~wBWXWu8C@(L>`oeG;9HCLTz>!8 zrtfUdIfb7-O8g1u*`H7W`CC1b!K?2_u#u{dsq4v(Yz+=!k zFPU)ORJrCU6Yn>ll5=!M(f_X5-z98mitjV3x>@smRCAPw-!_3?A{xC~#4mmXYT=j2 zSM33`9{v}7)rPq1(ut0Sq2hfO=aT0=s~;-9mJdHoOZZK>IkI|PX#*mR{0%Ee1Zo}p z?Vx!jUk|sfXj$dT!*D}w5KQ@<9SIU=1D2UKe7#S%)c0fWjf^lB{a8XGP)lSF2@Pr( z>hpCTQ*%R1k+BxsE^?rD?O7IC4|7(O&Iyx+Ao(oup0`B-~n^0%;T z47k#V_sr_ihqbIC5vWCR+~TTtZei6UTaPaMQjgpEb@Bv62@;fn!(0A(bIV55rs2H# zrs4XM!15A-TI7`(m#=Me%SM_FgLroG@~%!^-Y7vr+^3rRn^`t8k~eNQ^2U8iIa!fF zE#aBeCd9TeD^F`)dTS!1^QlUv&yp=cLbTza%(jg?gWL1t&)@3x+m@FI)S|UZlkh!> zr71h`CCUywnX&_;7L6C<3hiuamR%;6QdZrzsc-8QD61|?kPxlDc%YAF%+Bc@O1rN} zpqBV|@_MmaH)u~C-n3>8qgZ4GldvU7^qD&#ZdmUoR_orc-+-4|pV@f0s*FUS7QHKR zb<^55^3wfiP50vk-4B!?(Rk*fxXDkNSf#p7-qAD3J34~AqfvqcjRxW6aB$OaAN?#S1h}kygXUxH~^{Zo~AU_!-P>V)Uaan$_ZN!~T!}rBBF-DWW z4@!^_S9%`N$hw0UM_1%4M^-c*)BQjKwS)(nXgA-p8`C)le|@ozk$*uM(}T^HAR(@_ zI=5}3-;`v0>V_7^&?P|OtR+ zs5Dc?8qpupNd#(r+42f(WFQah-^l~J65S7!Ao0taFL7c7;QJ0HJ$h2>*|DcFE3l$O zpcZ)=l7EX`kDE(ZY2(O~{0e!JqXdbV{K<4NlJVK_E?%WwCE~mvfm-AZuV4FMe`}Ra z+qIY^1B{mE%F+AbNRXgtBy=9Afn}q9hZ@?RT_cUVBis^!T6ESx7p?2N=N*aIO~gGS zP=ZAByG5zxZ5!u1wAI>e9bpuwlNLyz7R?LDBgM9HxPN7>YxdE`;{H@#Ixpj=YMI5Wj6L z>P2B(l73`zcgx%n#@XM>ias$xf&`9mM44EW7%e@ZQXzp_^4Q01gz?_rcDmmWa+#xy z237im3LjdETkLi1?QPy?F@7L_UhAG8yb{J6YBBB<#e>anZIP>e%zG=oYBnvrWUEf= zV%_s}kHUCls{`(38G}s!SzCgHY-3*TvAjW+18VHG91?+AIPRkCV!OlnrF2!?6+*;1 zgEJyS0>^e{Td|fY!j0|cYD+a2-zd9ILJNPhsJrBebdSD+w4OjKHNN)2wxWJTvs=Nm zVG?(VKnW7q3O=F*m1-N63JKJDeeRSdmr4*@i9iVway|C94KvoHs2JC#TocI#(sbrd zTadH6bqDAC8g6{cH8rkA%F!6_pBgtgwzTkhOOPNT+weXj zU+VgSaXY`}lnB(K?}ygnu067Bzc}2OvbarL@<8)zkRU;V@{5@N?t5!@ww@Z-wtG0Q z$%4f9ocy7Rn>7@_DI>I5cFO4R&hsQa{^wpw@5=??lY^G5Iu zJ7fa2#Qm>*yQD?jF11~+;_c7pMg@yIsM1)`l2F`rcS*Aq#JfVL&#ZsH)TO5W*Vj=z z+b{WPZh#Ub3VVOb>!T@NM(Iy+w@ZZcT|v1dg61L#T5_pgOrN9w`fxB`d{Q&tLA$(2 zh!J_G*b>&0_*cqSx*9)%54-G^2-Lzhq;-a|4UMXeGV%P^#d(8O+QUyF=C#O!K%=wT z=IbiZ=_o;B&x%3XgoTwX8$H5u7+L3^{7GT`ktq( z$TOK|8XO4{He`+934>$yBk@t?aqn2&s>YNIOwx9w}N@uW8 zf&}?-YJaS=ZItVl$GCO41~2+qlL*uzuVqcWS?qtD?v7_3ddguhI+AZ1^P2gprW7oW6^(CzEO!BaY4f7G;PXpm;v_;u4zBk7iCt;Ney zGK1X32p178i9iVw!5w3@F84#NQr&9M*Vvr% zfOa6aOCnI~@Vm|Qe%PhzoTsOexcph|$L0Za4$_ezK~Ibpo>jAKq`Nc7xbS3^miuV| zi9jv6=BIBDGU`2Br7FZO|x*sVv3^D=_uhK@+ zY7|}zwJ3%H?ZVZz>T$To2xE}iTvJol)=`3lxDsVTv24_v8exQO-{Foaok1c{ON;=| z1=O=_97+*ptlkl*?QN6699h~DBxqGttC-SW-@Qhw^EYz6a5vbLN+M8;#=e?Znex50 z18;>H^_xv_M--_>vm!@=ggok-bTHglQJ|1}Gez-00=48(pBVj${X5|ujpH^P0gAO{ zdjx2X!r2HS#Ow|wNQm`hd;I9^>WdY6Bv4EI`})E8tovcFg^RP6C_zH}rfharTYWuK z$w*YOJU{wJ$iE0%OY9E(Z%s05-(bh-O2+%B^1M3DIZ=Yw73=#Ceot)e8<4L7?emhy z6O{@H)Dr(L+REnvF~lN7Jy3##_$}JXx5HcS)go8&SK++ca&c~Ant3g;U*k_ZM7bSJ zTrcj0^We?F=3bF42@P6lURUf+4iAxRAR+ut#Fc!zOQ&|ui@8-l zf(MT*BN3>TuWeGweOMxtymG0PHlJ7+JC8j3n@3foQ@@S`39<9@=jmeB&QJ8|XEB}M zhV!{igCqjA#JKBv z5x9zjrIiVkAc5;l|4pEl|NP3PqG&hVnRy|B>t(jK_^(W$7LJKz0wqY`y5D~jsD-0J zhYY-biXx6U<3SuJ z;%dEY10_h{Tr`0oJ5jWY@9Zd`7PScM0wgd*pacn=izeusHjqFqT&+(a=tq=+5+rag znn0K~kU%Z*&O5sR2@Kgl2@*K7Owc!NAc0zVHxmf@A!5`x`vXYeT=c&Q)cV&`Vqx+f zq$eg}cL|>!*#=6GpdJqP)f`5C5>X4s!H&MDIZBYg@w`l+1PR>v`)>lZa9$!4D3J+g z&(k4%p5)H#2lqK~L@pC3K?3(!9YVAf5~zhEa+yF065_k!>^nP56!{uEvj)_{5xJu; zN`(?6#Cn;tf9^0cfm$?X)SQt%5~ziH^#3;@?gvVcpgRlR31|Ze)WT7}qc3WXTH-5e zdsh74geVnCkU$>|*#;7*g)80;A#9)oiGS@)MEN*zV#HZJM=knlC+tATHc*0usFSmg zLrj7VBv1?YDJbq>s=cNkSrV!T&RB4sfODUJ6ZQ-X2^{sygpk<2W8$~%^(1WAzD9D* zsmDo}#bHTsG)TEjsU9tsFwb2&(|{5rt1Kh} zB}fQ+j&D@!(?!)mbpyNuD1N*DnA#qZ%X1w6`(d{UURT(&+t6uUk=GI>{)98Tvz`)q zQlcaR>w*4vj7=?lNtyhhuTqv;7bX6Lu;KI4D>P}in)h~JrM4OI-SH8`+M@p*WA%@Z zRWBSJrtF{yu_!@8o_`cc`%?QYR|liyMB(+4jw0G_G-spYcdn!xLn*fbpS37GMQ`00 zN?93bc9*AFGOjdhVN|;2)=`4QwGZjz%kJJ_(v&l7Xg0p96h#*sSWY5P>z`&>;!mAi zA9|yRuu-W*4*u8lI!5TPK@|DRks#4wcINneZP!~imT%3?pI@zMTzMxGsC6eSt4a85 z{IfqdAA7c@(Jg5(ovwEzNc8h&jV~~1UFfz~A|L(8A1iUAd_`mJ!n_iJTHP|{jPEde zZD@HYev(4EoxCT8j29R%Dk>jL|m=NU*D=|T5bCWCl3Tif<&^G`N=1Bg|%wXi#+Jo zcfYOoAP+huP)p?Npv+yCjpngU_~qZz7*F0;r09E&1c@A1^3$`s+_LfTW;Y(4`KqqY zC@2xARdrgS_{x=*h0-^k?#J@@-u(72UcF4KniQkNksz`BNWu8U1D9GhCL9^aua{n> z8{aDufm-Af693@$rIw9?M5HRWN>5h9^cG2wATjw#A&MPs6OVTF;cMT<>gnH!SQd^1 ziO*XK#b;{0Bs4=A@m$?JFpSSRI8BeMluaT~Yr?|9l&@itRjQ{YNAk|BnZE1i5FI5* zEFKsTPvdyY#+MC)`L*l|^ws%f0<{+8EE1pN%8!#YD9Zpjsyu2o0>8hS~keBY*X-kXp6wsv@M|E}#eN;R(Y!8R zi6VtW)pb?P6~W_jRFu9rNQiHgmU@%*w3m1k>ZunN&ZCxai9ju3!!99X8S+N(R<}yT z4!B&wJgH}2%il(^3J0vV@;~`vOOO!1mmfJ`wbhmlD_jrhtmS1oYZ)4((;2=4q2f%X zC>7EUQLR&w*zR=J^3=E>on|aFM?YY$vCdbrW6%=c7QM}mZ04_XU1s9*_a`|KL}*L-b_jsLL=U`vn?zr`rSw_{S^VVhVw z(#Utr@a-EQVQYyzU!g%8Lw)B`@BQ2|wimr4cj)~<30_zHJ4F%=^_@$#T9-fcBT#Gk zk$A9y1WNn~$42uzy^TaGk84T0W;WNtU!PlHp246sx%hd{mYctO^-NBE$#QGGeca8V z#*7ou+7Hoz6#d(gAW^+@GCKS0qbb_L`awqKBdfGBw6BH)YKhDaZ9;4t{eK#1)jY#3vF*_Ka>eik4RsHLS&K|S0ud*47GX-sI|PJ7uP zfFjB{5+v5vNFE>D(YEoS`52@8&@$Shj=3cQwHnV%5npxM63fQ^`r$^wd_TB%f2~5E znT`YrQNJ1e7g_aqvn|3nmi(Ri=e|M`fm$N>gdlwTAE(<5Ha<3(r=1-oqOm*oL*&f} z&$`mo5;Gb5&N7y{*+65<-IZFKq&1{_j)Z9GwKaS+W5vGpGjgxlrj^WDOCnG!&E+IC zZeMBDqwmtz#({T<_?R|<&y~?tY zt9(bJbgdU!=Gvtx=9wcwLgXHq(SMcIR=3M_G(HfK&yPT@^9>TmuY6(KXtk(=@m<_2 z?f&_aI!ciEr&$tn^y;g}jGGmWbJr^JY`ybI1Zveyn>fB(#?@9mR$i)OoavN<_azT( zlprB8yZo_kwN;O7N3#5+cXU#nEdl z8&xw_GoI`nTM5`kKy^=}jha!qKGQh(8x+^AW~h#nTgPvsDsC9WAGv^;ZV$j$ZdPLWby!yL9T77s=aoX3J){9*IKv6H( zn!ls^ex|+DwU&)v&!sUw9%#bT{8N!UWgQ6;#;T7L`O8PsS^d-%4Nr=Se2N(<-62qm zGQt~$cG@;_)BT`7&WJz>679NwGKRQq8|CK)8sCnoeD_wFKrOlE>qiwdcKunMpPN}y zM+p+xhBPjj_F4ZaS6lw}b0w+fO=kX0c@x%|?SpMdnXPH=A?6_2$xj9)NaRZT-q?K1 zN7MJb@mD=Vu{Qks{HjEt7R>`G?)kbTjzS@pXgZ6*Y+Oe zI5)&Firc|BXr z(Omo&B}j&nELf9l5-T}0jqrGhEysBx+O7`g*-HmW1Zs)j&iLx#icWDy zCzD@zu!#PBlVXq0Fh}9%0vRt-)f_G1RgfaS1A&EdO@w;-R>DK5q zvQ;ttS>iC`Ufv8+J+Or0361PK!+JmF27itlerAj@_OGhuvujI`Xi()5?X6F@-t%>% z8tJ*)jWnKY3y}!aqSHeZ0n@fo=hAR}+@HgZ8ok8HFh_!fh?gT;+SixdU9wG2(rTbl z>F+EOfm-yXF~;(lRz3P%oS|o|ImEadRb5935;Vd$OXd4UeGCiJGY=eQtSciEs71Rq z^z6>GzMSG|?~#o1VkB*^qXY?Br3D)gC&=GexeqZM>V9(fBaDq46O_Wr;v7@kQ;`bC%Us*E{DhF4K9nJTzKD2@)bZpD2~@ zew2EX#_0L9iP3VJOrTZ|<(skZgl(hN?hMAcIgN}xcS_LdFGqre7^9S$GRv~jD`iFF z2#o?3(g*+v)N0t3#V0N`+p0${(iVSJh(HMvV&v5HfNi5k^_mul~$=EkhCDU5-) zOGyN3iQK}6-`O?-mF7n7K`D(N`j?>-3XTK`F)O;fZjM#+X>VH?T1+ydNJE)GEivl9 zIC`#C^M^I-7#rVYH?~)B)5@JAK|(|?=yKn-(e!kGqwdmOdc@}Z5`kKG!je*)5!*)I z*S(C~zaP_oro2n2CFW`ef17KTT_%>4ZfDF6|ENbcFG>3b&b5#bbKD{Bc~;GLmF#ZZ ze|tfn`FB-`KrJzE+xlvrRr8|cDZAp(D*bt2Af45BBuJE+lH6QR^v#OMyG^Xguy(YT zfdp!ae?J>A-?9D#Rpg2@+yf{Ns1?t$G~0G293a>Z4bi>yil65+$UJ zY?h7D8AlkSH}urs&=&_KNKiCM+Q(U7*=TvCk8zpK(GQ?=^hltVnBAW`Y1@doI>yL( zB$qyEsL1o_}C zA!A%^D8fcJ)wgnIOOOz2a)S1)^;wa>qHM2Xn?NmLqr>c>|FNEEOOO!1hxHy}*?3a# zn(Hi$cWcs~SEkliC}Z6ab8IMnFCIA59O><-bER>u6UGT7kSp?;sN5x2+|Ys`>H=S7(aleMt#2W4GDmMMAE*|1N;N+K;;c;|+c>REy+L57wI!cgu@OuD7F^{%v1Z)|} zuZFhMh7K+w5vWyYX8_fFs%2yOmf<{8-oaX9iaUrBB*YHg(SlR0y@^WAM)G!rn`u{z z$pmVhZxBFhWK%2~OUTC1TYa?x=TusaawJHIJ+@L)CR;YPU+c?vkN0Xn{E%BBP^-)R zg7Fm7)Y`Soyt6y+I^?2O`$H+3J3A62)SCt3`^=qW*>IH^$p47hsQos$v_!~S1?iqo zv}_dY*q3+i_lq`UoI=sX90?Ll59BuqU;MU`E-g7B}j-}>z`|lxAy3V zANih-e3qXdNSa?FP%BgGT(nv{&a!c85a&krB0Ni#3e;aY5+uZKf5Wa(mW_hPQgQdz zhP=usCIwQ+Str z!4iR5DaL1`F+;fJ`>`dk48QeH6@IB*Q605%CCyG>!?s)w&u#W zuWLcKy72ElDzrc7NRT*nGA+$yMuo-=7i-AbgLY|Qw5s-fcxj11tvuz@QPjGTmW_=R zDP$v^84ag1qbNbbQzvcwjQ+Nb$9uMEiSz+{c7*_mKrNAz`R$kCmW{#_H)?I$59Ff* zOXw&;g7V6e*V9OA)c5DJecHy!{dn_oGJ#rR?As<}xHal)ICru3&=|yDmv!qXK|+kF zWAcx%Yz#|WMjN_&G;iLgq(q?B_=QR0YwjOr+1ORtrM;{%nm=n5sG|f4u}{&@JKVDI z>1kE1bBob@Pji_-ExL2Y6{ITZztJ_&_T11jI2Fj%4NWRq_mbOz+R+M4-fDYLUJpyvHaSB1(`z-#FSad0$JtSE+<|HF*M}1PR<}q7~<7 zg;dXjR^GgR1Ztsg9Aiy~R#(?t$mIQv{1Z`2-X%jXy#`&HsWX;5@H{aIM}h?I<1wb} z$>CnP_n|uffUw~Zs3m$-$4gJ`xlm1Yub}tUt4LafcO*#Qek5amri)b19Pa6z?MI*% zdec#~>8I1xHV=z=*_%iMB}j<((b=!9GWwJHq3>4p**Td&E%dBotoS)yEj0M6hd+%J z8GsY~_mIH-VcPBMxm~?qf4sNpBbh)g^uJ@Q?$iV7)(y8j7oG`!QOB1L3EaUZf1l$^ z)J7+lc>j7R6R3s$cZ}6c{+oLERAKLLM4;~&dbi0Ch9fcuhz@^#`8B3C_w_-jj;+(AFCyULfvPV%WZ{qLf<&XroDQp25ssSyXtPF zff6KeJ%F*Yiyy1Evi|D1=SQFx`o=NVcIsob(9k=cxkR7@3G{ShEK|!n>JQiExL(p7 zLDkw|-iln_v$F~j%TzR2b_s?&t ze{Oyh+sBVUEitonJm_B5x}d&WH$fRqZxl+9z*QrvN9RAy6QKL)Nkjs*#J}6~XvRKV zNbk;dC5t!gVx)m9<~WAKF(YLr?fXetys(lQ@X^=9+2dLq@8Xph+chA&+VMgGwEz() zK?29xM%a^_Ke{-~jfzoC8vYT?>DW7l#IR1bUdtBHv~ z2@*JOqu%H3ckVC!fRN;>sEPqMh59cbenaVFA02@*K}q%0U|hp9iL2v8T$ z2mlGx!dWq6yBjoAzcy>AF7+c&3)kKmE0Arr`r_?PWemmHMhOx)yQj!TS;N(1y$h)Y z$OaOqg=_DO%?*oI+wer{60(63Byg32vEOD4RuA^7tp4pspcbyf(|LnaD^+dSV&%hY z@qRd~{Yc3 zt3KITO6l=9(m)9ka)0%L|4&W%-T#!iegtaaYCUzWmDJrEqeFl_Qd!$?yQi1L{5~zjq&xBIZk0|opb9NVyKra$n zudZKKP3Ky9pf1@!0=01V%GmywE7XHgm6f^F!=VHT+!1n0B~E7|fm%4nHHj!67e)Dr z93LO_{Xq|NIt!5bxLS5xdT%M}8BhzokYqw!Ki#%Y$_gqKN{~Q5DaMlA*%mu}$0Vhf zAAwrvm&I7MXHjae=Pa(*go84*x|1o}oY zb|cOI%+c$0vVjC@iGR0G2GaZ^daByfSn9285w-*g^o^w4F+a~%`)r-tg?5G$`0=J9HX}uB}m}Y!`R0Yo7Gf5=Jn?EYb(^k zXMwSt>wCp^c@?IJT#YC}LVm8cq~2_<6cr&ttQ6U&AjEI`v;v-|plk)(_9|QA)49IV zR}Cddh?4})nTEGx+Nmw~jqz-t)3QjQ7M9TWzJ{gi>sjFUejp*$jt+?ePY zMmF$TsDMIt!3~=7`jyfe_tykS1Zq*HG-E+d+eVoQ9eBWu=X$F#ZXG2^XsKIKTV1v4 z@m~aLH9OFeMr&8CdK7-zo?l_F^qij+9VJNol(#MIA=oyWQ@qSF6gP7f#mqzkwM5QY zL5R1RzBrjRKB{q`k^j45Z{)5x`4?~>) zKmxTQvJNz|{9xMb;h#YCGfb0Zk%MtLn7z#-0_IEl;N@1LL(s+xi$WmkZOLup>d@ ze1i$}#kpkN!4%6@X_1sep(%Z%kU%Z+;xL{^*fz*JS`*&UugE(ZB}j;zyt5YBHval& zi?%;YehUgFH_h{MgfTyPDIFz9xb{vZ&t=<27TT#ERkObl zLp#+-pjLrN(~V{aY#XaCHr9529A%U(oQXW>90?K)yUwAAL^kohd9*er+fXA}2jN5S zSXXb(Hd@@hXtt`h041VvAy5za*`o~62Nle@vlaO2PIr6mHjviz{x zES1kjzJ{IMi`qvRT66}EeA<22m5C|l=l!Y&8H}~)!lr3#) zA&EdO@!J{0wO-cAt<4YT8v}wUZtVi2WcwGPL)?$~*9(j3EpmP*VlP9r)PK|0AaB}_ z?%Nr|_^7m%b)jWTkf?ooDUG*nqW9t&?zefudCaF!dIuc|61#?OrgzY1kFli5bGpAA z3+E4OiC)wpP)qzSUct68{bhZv{?*aE@`{g?TS=~mT|)7MhCO<2){n8Xi?V2$GK}Hx zs)m@)t}Vejk%u0=wJ)ri@6D7?dq5HR%eRyX)OxvTGQA(RjXo=nyB9={;Tcvnpi{q& z1c_ZkrU6)Y4YJO}p#%x?Eul<*wv9`DsUAfJ@tD~abtF(r zc(2Iv5`kJ2Pl}=^J+ zB9uMCksu+)D5p<8wd!&4x3c_fiy(fvZ54??trmBC81IWe4So1YjP$Eh?42`3EAmtu zf^?K1@!s_AX(@qG8Q@U7p4N(5>xUDCz~?)ccMc}t}^ zzcw%>f8MVQ#Sn5NNQhZcp`DK{8#CXw;Gr?(FG^1d5~w9c{Y9=mvTS4tr_6Hq67l^9 zO3)caM}maN053}As|V}egO@yUK^y!>0f|5@ifl$JppQa3#fqooNs^BI&m1qbQ>V+5 z=dyDx%0ouu=tt&tW#U%#zC6h1X7;*-Yat=#(SgYyh0+;cvQg!AFTUfO4Z zF~Mu0mYBCS>iWZ5L`z?_2Ua~UP(A2R`kNWe7QfnE#*aWPT%}+vrrn{Ka&N==+m_UL--Pph;p;~8b zo7z>TQIL)jB#I_46koqeqH1C^$XJ7;-z(+mj8r6@<`5$?@&%}NtI;zqewV+_s{NGr zBcGZ*#{75s>JV|?Xr!E?GdcN>2k9t5qVddMOnT{(4@6YhurxL;oqRc7T_#X#*U+41 zspv#I5t-)nk7>L#+^G3crM7Y;NMQXKTUPF8WjW0dzqAgP2-K2mKAH04Uhn)_389*& zOT3L{c)tFXB8Qs4#eEjP(4LaAOO*E)m5Bq4y9cUB0{V7HBSES(d*HP5tJZ7aZ+hS_%8Hm6H%&cbNd+O(i-}r z-ZFt&6pKJVUhnVFEysk7z$Lwm7ZZ-@i{4hIC&ZB;5gK$u4}0|9vcU(nF}`m5q8lAu z5`kLcH?@;xBYBzjMw^Q7bZ-$66VQ<$L6I+Yx_hA`=ZkuToNxL67<&uwDvs~}e{l=$ z65KTe2uZHYMPj%Uf@^|X0ue}p6)Qzck>V~zO0nE~cW^IWw79zz*Wa1lz1{bupYQ+q z_j%}ZpPcjBGi$RmXU?3%ha5}Ci>#|95vX-(#Y_6CesuU|7Cpm}G41(;Cuw>9eLj@w z%akBNZxxMGKRRe%HxX^fcXgBY@MaQqa}lpxWhZWgM2@Y%58?BdJQcJk%3cFP26QM3rH zFnerVURI4iyyM3+mhn+hf<*UwxhUS~iz9cq-tU&-?5%^JEm2G&Q0vtDyyT_$>d^c4 zmd*0@^50+2rpKl`gz#2Z3i`A4MK@}J@8+xlES3gy6rF7w!XV#Di z)C%5Fi#(p+4I3?c-%+;>>Bggr+vvG6B}h<)d5X3E?x5%%Y8CyLELQtOHWDOIi}I6$ zjSVrrd@ofX?nRl6P=dtvBYvE61sFDl{IyJ788(=I?_6FYP>brQ^6&sf8yP6dAFCFr z$9E0pvF)moFT<1|Ax3A5Uwn6n@q?o5{Aa!TXuv@Jc7#lzmRLQ>JI-U{_n$YZA72dM zYZg?bFM%mRLd*av)YQi)l$SFohkEZ@EU&y?R1-7_)Dp9>nAsj1XY+ZhhqlCW2l;(a zf`phGI>S6RYHhEeMmS=5@nJH7TK7+n<>fM_sYf0VI`e0BtE!E9#d6%$ zCGD#&T9>cwCt{Ud5Jj zmqy7Cg6q(X-c4&;`?USeV#R&lpnclzRS`dtJ?(yDe{Fox$iTz9Xw7y&ptcv;V_o=q z#c#1LkG3fW@>O-qljpR9=aAegB+$0@o-0(jmx&yvcr4U*ol%v^1Hs7*=PsZud37#`dedeC11w~m+*QsL7B7uFI{5PJ#bFDB^K|c@i ze-TYpE@M5)I;7uoaX;|4=gulhzv34Iof%)dno)Ec`oGbWjz^-X;vec*`$f#Nbqh`s zl~4Zl-J?%mCd_svORYizR~<}!;YGz`p%$)kn1ncUl;Eli9?2w9+du->js9-}wQw~{ zCQyRwYXARQg#@lmnl@73bJW7MNk#cB@UFFIOio)nHHo|r=6WWsF3PRi`OidW^LLr7 z)xXNm6%xXKX|C|T{l3IGblDe6a4G_|sE#R5WS-Tfk9qDoZ)KZj*+B$q;VQCBly;tR zz8hn>Hi-DJ5!1WJ&=^?KS3 zShuA0&(R00=_uL|3Dlzc?%GPMh|w_bpm-8df&|7Ln1s0JNT8M&KOQW3$yn7lnPl2A zVoH#}_z6>AvJ_CzUQ8plK-1P2@)8cVpZJ~#gI7*NZJ~A_!D$mi?&WnwcEvqRW4hhtv@uA4lW?bE0LZU3`#(uZ{ zaYKs>|5t(pWkFPLHos;>A(>~AOsiC;1PP3pGWEs%KmxVsOGLAMPsEnVB-8qXDM13G ztW14j0}0d;-;ycyJP})cH!ijOv!0qrpnK15HP6-GT#4HcRj-ZH$LrWFw#ZN_*Z zE!*&EyVNpeOIcgMF+HNuV;u?E#>>xM?$IM#+jfqT3Dlz6JT!Oqv?}@@x0Fgwu=JvP zj-wZh@ss;RaUM4_ese#_kSKklkihj}%866K+xqLkpKQ&#$h|0P;Wt>jQpw`~;%UeB z;(7Q_-b;~hp2#y&)(CM2QGx{C8*y~ce=>nu=|I{6dQG6z81wAl8QhraU}9>7&cN99tjfSx9M-6+HHzu zOskdFK-$5B$3iXiM$!(R4uxIU*3YvYp|KB2kU;;wqP%(#iiCQu8#k+f5< zQA&@4gbShjqV^4 zsD*2ulp(g!Dywhs0&5l`P=W+{TNGtN%2?~Tzg$);?X^S#wQ$Xo*1TSRa|KSFVB66~ z9|3q)S&=}mk)qh{PqW^QX-u7Vv32sdre96}-WpcBkw#&3@3nfUPPmb#2hOc*hZadtTBT*($3q6H2uYEPz zdhp$7YrU5;fm&itZLX!Rxxc~cZlBlsn5u@N1PSzbl0Wgme5>tdkaaCZt0RG0xaO%S zvD^B(ye5R(eqSVZhMB9ZNTBCdQ7SDSZ!J)+gtgThnLsUE|5TJ*53;(NO!TqwJxP=? z*IXGzLd-c$UwE6?uGWWZUs_6kkO|bnRZ?0jnvmVHappd2C)ziG5+u;~PVdLI;_lsJ zZrXlmkSGzTg{!2BlCMTXYhK@m)&;Z!6eURD8iJy{Zav!C@Ag_t6Uv={1Zv?bsiJgv zJ<&2Z=W%OO+693UB;?-b>(lhs#Ao9z&1k%f1Zv?bsiK6g&u$&`{cvDB5hy_ddpr7? zZI5v6z1GOKdtDN(=$U>h^j~3g3auy}zioYT`nFa5Gl`;3%y<|i(8ET16yqmbkHu%P zT{t5XsD;rfv`eUT4%fbo{(Std{mdsjs{IdO_L zdeCd@QzB4;1bRFb<;2#yF6S1TZRU2FKrQ@AP#qR$KkKQ`61HtL-o>kn*9|>vv`Z*u zzIEZY!nP)dk|=YE>C;03Ba#&5f%gT=?8Hj85vd5<?-Scl|@HB1ZtsA zPEkG_KW=IDPZir+vVjsLFw%`Ycg^1f4o<3RYrRhECsEnfTCTt#xmQGPd-pn5CL4(nuVHb#;osF0;_uBD;_6x1BP9TDYRDC}nRu zttn$8EfMrpLw}q2?eV0ce@;=Z&i>W<)$3`>Q`)155+ra8uaAz~dOfzZrnvzUsD)lb z+6~zFy`}cjdzK$)=Ojvyz*z?E?vEbC<9^tv&Pni6sc&~sbiae)Dk)j^7;nS*S;(vatA$Hd|<`nAXXykR9IqEcX{P3i^MvKaY!=t)8 zXy+LD848@~z{eKh&7}2;N-#NsoKA`nllpulUrznqTeTA-Zay!a%hXiVgzf+8vVIy1pzP!+7RV{F~ zmPU9aNXXZ!jy;OE%yn3en^9IGP)oFM)sW7HjlW*@;2&SyR@+v}ES-6ljZJ7zSr6@s z;#H?zbd;NV6XmED@rfuwV&H?ul)bE{aX+$694*dVnfx)kM4*;9QilNj&Qct5WIg`9 zN-6G3d23OE#Jh@3Xm^{3X#Fynr{7$JcWPam{M4odiHD!VD7#;8$I>D%=njT`4&?bq zR_3>N7L^FpqWKOzg;9?4O>Yx1zgcY_bkc`MXRj#sNBt{7V(YL_%K8;aPl~XSKcxY0 zzN7?y=U+i0P;2pv5Q^67>*zdP*m!*1m;3bf;YW`(R8fLN+dDxNq1V?z@y&qpzb^~PYw$JW9|jgS{=QL3tQ zETk$$a+ne%3SPE5{{HAWkC~M|tB;^1MYdTB>LzJ63G zigGX|NIY%nPnjt_Hq^)8)TXf=c&&ZD5`kLzH`R4`M?_N(dzWkk9Js4Gzjoz0(lc6* zG9^gN*jI<5+=e@-1_=?>Hf&UDFCWO~FDoe#s8woeVjVu$gpK39)~J`;59G^d zTU3-FF}Fb#ii;ZQ__|Km*jl5bT5k3T-imf-B7s^ZCRCyL6_1UDlq{c|hbcjVGB;7& zN1U;u*r-`eHR$U|exZ=4|6vlcR!Il#gfcS3{&}gA8eJ@wFHQ;+Ic5KqAVHNrsrQMa z=T}szjCdXE-ZEem|KUh+i9jvN!{azxI>E@SI(EgM?zC(akFKAc;(g3x2`zE9A}g#+ zh@K%MeM8E9%1+nHJQfmsNs)hPMM=CF?e2bX6hBs?fJC5{&$WDxk`p``NjqFCYW>(H zo(HyVtD*#n!FlphoI$*iU$k%K-nLE4<9UIxp%Q^w;&;bh2}XX=;olC}I$ew7fp%3T2);kvbUPYqM8k~jy_V0 zt(6IUN>GxT@X)l+%Q@EmONkiXIw*^Zg!t{z67jAgd#m2670agEf9N)h-~Cxs?J*@t z++FpViCnCDZH~YU)9pD%4db_~$OLMM3IbIpjxe%?PO0s-=Z}o!GsktJGdCqjh`#W4 ze6*1*^n&+U`?e)Ld72i@B?7gY_jt)>?29pMB$YaAzqYt1KiIsviV`HqU&h2&O|NjI zmT}wT{^-xMITaPP#P?|Lv7uTtgiJI!_{Po}cjA}IYmtEe9t(-ymK!YZxFH&?D1p6i z*#mob=een>6&?$ifKiS#R{f!*)A80?r z?m_K&)xT^Kfm*(qPcSiB((}--9T3CqXFJ4756}C`Z4I4thHyw3amM;h_FQ|Ghb+o}?~;AR$I)``?Bet-3&ClKqFB_U$w-K?1c#s*IxgI~e0c(XWd) z*N6IclprB~e?RZBaVdXC9^37c{rLW>{~T9M*LCCRe!FSkO&J7`K&_=k7>zPKHu4l{$(26YSo=y1XfLuUK|)-S z9|w4BT#FjcKkgrI4;@uaB2WwOzoHzXI-@74&ggrpN`ew3#5EL8rT&!M%NE1m(yo-o zw3`D7)RLd8iLb->`Vj@$`=H9S_tkusem&rz`00+?6~+6nDA@vA@h|1PSk2pIX%~d~ z#34Z|iu4`SY1$dquPHCGI}gj>va&>=)(=nWQQUci@$6on=F6MR_GNMO?4ksT$K~r$ zEVajmc&19TM7AaDg&PUfYUEWPY;-E)%a2#|Wjj~Yp_-GX1c{v0>eHCW)2isL6?mKK zR+b}uEfpn5h$A_}I?)_N%+->Dy}AD{4VnGt0Es}Y`={$`*Giui8C8^+sG@`tBnIbc z;2>Y4ajo9uysf^Wy+x;KUlB@>IN!9sBe|caRi|${Rp;e?Y{waIi9oI39St=9jNYnT zQ?v1Tlno(@G9jRrus7?Oeq5PQ>Mv1e-5kWWJN@Y!WgZI&cXR`aVC`&NtMtF;=fBi% z%oa4MED@+B=AZ8>b~5I*u8hyr5C3#wX>M1f>O-aki4Fk`HSdQ$cka(`sh^zPSux6; zfCOrJM>N##pgwo@8oWd;LwiO;XwN80kPtIuadmaV()*Tbp?#^(TGZBN!X8O#f+B5< z8U4nNhtw4VBH7w90TLmO>(M&CEXeWa36G8XKIPSzrg3cf&C)7LkjOM9$npBD$A)-6 z$RovsHwp>Vnz0XUOggwkU9pnRoaQ(vL1KAOF!=zw80S&sLpwEd;0RX#Xf26AEqm4w z?Vju2kKeAPao=1vip^~+Y8IOkB;p?FHgpe*IFE0)?sgbk|i#F|mmBoe44{4?U&)z4$Y+^(+Hk13XOt=NNQN{|p< zBVj}LMpfE0&UK7pId|uj3DhccHB7Uid!wfBYHR=AWdwUs*q6SwrpFXLsVj#x)jX-f zUnwCqqB0o2j2-FfjS>fN+ zTQzY?QTwnxu`Ik*faZbpNRSZcA)Z~`zw0@Vrqn7t7HY|79&@a%Jx8ArRB5~>Wm_<< zBO&`mf1lLGem;H#+gVH|Pz$fRqGTRajjtc$$F0Nl^YG1V)y^=vUlYff(iW}l6bD4- zVKe-RLvvT-|D5pSE&KyTPN#n*Nbn_%=smX@Pl>0dUJ!i^kU*_ZiH*p&Yd35p)6SO^ z+WB&ps@I_eiLNPO)UVhK8dcRwDZ)4uAMn3*-ejSNGtt%%($g5V*urXj_J^pu4DL%NIpNd+dXAoNA zxH1vip79oG%5rOg+O*rqJQfn7@0vHLzR{}P??d>^hXr}t}wB_7(M@eMRMIUl9_hC3V`@LYKd`|c;@t0g?UBt#_5i!XV>~@V>piliSG9t)VqrALB zNd#(%G5oeg%?%s%b`IsmGR;&=6{)JtqdXEM#LTj{rMY3_ar|(8^5l5+*#0UKfm+)Z zF-Q4~Ee#v35{B}-cV?)Koi$aIAR+pZyyIFJHbfmd(f9VCF#{5)CH}rS#A9Q4?oc+CejVJ9&NEXme+rN8=2cc7W1;&UGwWO}8{a#p50wVb|I&uBdEd?9v22EOLfu>#(Z-Av{`@J286yS{c{s*xdvkTYaayWcK2; zYH3RFxZ<1yA$$W=3*w=Et=?>w3Dgot8l0!KVdKN8cz*HhCwEMv(wYsA1PO5svt4X$ z*w|l0w^8VAO^HA)VMAPX{n+8DiEKHO zjeV&H^MA3A{Uw4&pRYB7P_0@wuuLGi^$cSX!j3 z<8b4;#;(TQlzor>@w~LR4++%D(5?&lcRe<0B0T2Y)Ix7p+O^yly5s;VeKLad!v zov7QUIDpI1_TF?J#pyhdKrOjdo-#4YsF4hlprCFwl<=($=?&lM7=C7*|-<9637 zI*(@^#jMC|l{hEScCjN9&rffas1Ph>EGR*uV$JY>X^lu0gqU+8fm(}SwEy=S>Ncom zm`0!k3E|=Nv{1Jp2vKDW3Dgq5J@<(+rJrr5mZQuOcRSal5wa;kqUywU+WS~O<1M=9 z6X(=cdv6-aeSZp2QG&$spmwxd#E<4b;<+03V>xwq`&eFmkW8S~jD2mj_guFTdT5FI zBl$+BkzW)gNVL7vR(tYw8-I^Hs=j?6$qT+ME)l4;yLelzXV@Gf&f{3NJUm~~rrh$* zpE8~_r+uORS_BQw)x=!XGwV~7mYoNw$Gb=KG4<>eWno(H?`osH+mwfl$u_c|Pf-s} z?#pk@uv1k;(>fBg-$=V3f~GA0gO;d{vV-|V@P0DoF%tiQnRjqucm6;F=oHyeEG!yd>rJFeONc zxwH5>>(NvJvtFt@hIZ!V>eiMB)Dq*+1@i-ph%NDb@_eIEf&|_tiYX#r-#ce_-kp4X zNTAk~dMzjx$75r~rb4`FNEq)qq8#l6G9^gJ_uO~)AhtjIT>G88;cB`nk+e$Ho+4bX zvzXaYG^1-vaSZpg-{MM$zf&&nOT*dh^rP)9s`^nK#VA@^)+IiN4?W|F8W+c^<;!f(n>#=vP%C$MUy7`0X0+!EwnA4Rl#?vdbe#g!I5Q~eHB@`_>8GRCM| zXsrm3g<6y=kosMZ4dHhc*Gh~sP=bW`U3FrxVdJk2F>Kl5&g!GHS{A{7kA+&_&qvXW zrJ?bDWZV(W&hCm6PR&)b2bUt>l6fp7#QBL;0sYLU zE$Gg^ExM(ids|&1P)odz*)BFPT2+%;ReyW5+U0n4?XC4lkPy$b@Ga>!O7KY5rNmx! zKlw}WSg5u5MI?Fc>Kitm(dF z2C;)pi}T;Q<(3H4I=-wod2u{8CLV6V4lKydGnTDR>mQ~Bi4{S897Su`jLb$YGgoGV zehJ`(jt5EvY9+GXnip5ZABZ=DBHVsU~XomXtWeV(HdpR zgZ4LNMvJB>$&$3H_&511htmk76nO|m=ExSg`f_z>ialH2F!Df}5+p=yqKIP?G@VE9 zuDtH-+x8k`T1y0KU0N}OJO;%moiapQLED5WiA1+cqUgJRdcfMuUauo?Ij1 zc*S;n?z#*t@>?Z|K&_vC8AS0{g^lkyztokNJn_Ol_Cy+~RifgscSKn&lOtXcMJfDo zD1SU@j(vNZ!qT-uLR4zrUA&x;KQS@~#f5)8YOhqYh(w@P{G$={HK;%aAN zq9ipD_=kt^Ydesiuesd5G@z+QcqB+{KQe;8=ar3`7H@Bi;2XEJvUlrISt3wN#ESlT zqAING0{@70wAxkOXw@9AI9`2-TK9WC#+gUV zPT+xa6Kp%m8 zu=*fQCQwVn2MdpvZsY7kHqtcmPOpfzlKQ!YRPBb z-FQE0())oDB*br@Yo4py!ysz?55l7*2=jjAz0i<9I9`-H_l8oe^8jrPLd-L&|31*s ztVavS&VR14SamRc0~;H;CZjU6;4CN?pBY?8MF|p}rw^lEA&kCkqEGzQH-h)h^ieHw zrJ+QimZ+BAGgqk5xBrl)9}oZRR423$D?g?Li4CuYY3osX-@dVYM_xAeqx!=NnLw>< z7l+fCH#BnNE)DI$y&v9DKOD%Tq67)iujuhDqGu>jsXs6LYKwZLX+w!XtxRKL=(;(K zK5_7uXiops6D2FEC_zF*eu|Y*z28m0rayNFZC9_>s3Z}nC1?@NrB~F+KYb|oo&JmZ z;(0+D`E>BZgB2Ww8G^$gbX)vGPVTpP)ppNtoA|c*j zu~MzSQ8%o^_|pDU)R4iV&lv6CnH$1OY%a(P z%x+FOw#?@h2{9r!=gwuaH{zDpdHCNQyGjIViMK$^oyC(+-oeQ>-hq|p&34q(-Vcui z37o4dN|`=^Jk$Fsyzu615`kLi8&H(>D{8U|g=(>h3#+R;|EWfny|ujy1us`8|44CK zxq8Kf$2sm%0b{4d&x3qemIig$;o062fm*^-d%bJ{V>ie8>%MI7HOj^wDfaxB5+q)( z@uR&4`3)jn^CGNxvk;d1t1sPiQ-Xwe3iFQ3WvoY4o>+*Lni|I1PAe@Ds3pd_&3fcA z_R4hIP>?MMY{a_K3=$8cpb;wy$B2Y`jcow{z!&oKTUMd~SKc^l0Z41+)KRps8M2z3kA~_8kGY_X@ma^^G z>D~^BKrOKb{rYTn!$#}8C+q{Gdb5gGLR6F>(Rq4(N4p%^jkWC#qaNDbbGxz*jcZe8 zI#Ys#Sk+I+m%~`2zqjhH-9Eb;+uvL!P;2{YrDHDV3E&VFaVmb){{>s44=)%P5$ zaQa|JQa>*(hCwFWMJC#tpBu{F?hc{Rl6fp7gvY=)vzHM~<$U?gUMx7ArHl`i2-Hef zC4|;N&BZlR|-CyjJa}Hr+zWGZ8YKi!w z+~HY_IGJnu8Vd`eH5Qa0A-;i=3V4XZd6wAAFC4^5Q56i7AR%@lid`6bj92{MW$YUp z#Ig-@tP+7*VsGcxVVMmtXNSC%?TyOEvg)w`v@&W+kPxu}3+87rJgIrc1le0%AHg24 zlnK15BmJ0n`$a0+jVcJFZN;PWkSNa*bkb{whIp9P?O6d|jv0 zx(~U_^c&XrZlhRucQGq63DgSSA^g;t4Igr`UUgi_FA`WxFR|Onl%ys^3w2+(m`TuT zh8CrVg!t{z5MS` zt2hs_wkeKe5{p)KRKH%0VQDJ)X*N6(B*gm8)0UZOq#;(~8l6qUYjg}}JbyKbKrL*o zqRguiqh75N!;*)Wky<69p&x$!Zwo2gLbF3^!A_BE`^rGtUu(7<39-^Bb`9yPefevw zR=dm^$j&f}M4(o|%UvisI-9Xlz3$m|_15tI%=dI^}5kaJF6BSpePA3AJw3c z2sX7&J@V~PQO*`@R3%X} z%p_1t?58eTBfH@#8kfeoO!4L)b2nr;1Fba5FeRx8>y4a-SM73Ob>60jADdO6hC~S3qt!E4ls0D2 zJ&>PnRN^E54rF6*R-nCPrUVJ$nS5M6w_&6GGs>}_bY6YeQ3E@wse1V34cV)LQK5J`1He-2Bi9oG&hx*glDj#_vg_rXQXS{gF zvTWL%3bcxB9*c5F(daLqc3hcw&?A@^Xrabg0K|=N|jdus}@z08}UPUTP1ZtsY ziZVL5THBjs7|DEBmQxe*6{r0w6}0FUu~T#Fu!_d6&yv)OdVY(3*Aq#8 z9A!z{7SESvS!JV5f#lgXB}nXlE52yCX;+olqiDpcC3mNhB@(D5{!Y2Wj6I4XUP7FU zh;Bm(65_WYbQ=bd{J#i~mN?S$Q8^789e+#UdxFQgH;k*H+3-m4xZ<2{HqUPC0PVer zDqy5^bsyLxeER0GP)l4x@t*6~%J#cAzvmsqb~Ud|JHv8O>}gqT$Es+NJGzXv7ge`g z#@NYP=0#=xs9FF^o4q#eiWhCwB}nwPhYCXRIXAIfm&j$D+sYi zmA*LX+VbWR8QK0rbtx9dlpxV+R}Ok>OBrV#oz<7SGx@TX6zhxxYKi{v%Q}w@=k6fB zD55BPo5w*p`Ai8C;>uR)RKl>~zpNRzugt})0W~B7wbmZWM&q{<#(6AgLK!qy=3xz& zR#Q=eM3#+S)Q=Q5Yz({*#=WSf*L=#Cfdp!aFPfO?>DQ|3r}ljG+%&9B`MQ*0)080L z4D+J#g12F#>Q6oSAG6-efbbtuboNjwMd|r7zwOB(OZW4s_Ia`o2^57J}h^87M8yv?P@e7NQked_)1U~Mj~1+%g_JL+JybIsHjAs z7JgSKw>tIIzf({BGesw$1PTAixoNbde|;2X>{f4{P&|mOPW6pKE&TdW9{S5kto!c6 z*3fN-T{sp=Jw_3$OCp1W_>$|pokS#uAUqNz`p)$JcZ6dSBIXVW)Dmlho+Bwrt?Ry= z{;_28s^J?&xvHthsi?h0I3}W;nO8r$OI(g)UU&Q{g3cTvArUjX5Y5$eT2U(QDW_hi zs_8bWn2rQ$iPglzjVl;W`$meNKV^$%@2DmcN|2aTps+Tc)8F%u+=J8{OQPB4)iQxv zqL&suL)$y|=<4okw9WlD&9*pTiq$gybJ#&r)~tfK zOPkVwYF(KUBx=_xqxGV~o}&CcV2OP!#h6s4_!1;gON<>wFRI(1y{~q$_w@yh08oO& z`d6jtt5)8~a+9mk4tqkC{%rTq8WMq8dykc(uX6=sE>djsO8da~16j|p{*-;&lpx_h zxs>+BIlfGsM~3=K>8k19pcR300HmL0NZR3q7zVKr5hAR(d@#F^_h z_TQ^*f8&Z}E0j_aA#340>eIs<_KEr9ndM+PEt8IChK0mPwS+dm(rvU)+umOFwb2@+>Yqm2iJYO-g$ec9OS zR*66@Ts={gUd^hrtzG@tqO8>=0=01Mg>wATS8Y!wUw)5vP6|&1RWf4w7cv)lJHBbZ z#q6bMjjv4q)`~L`S^hjdoT!2)dP|W*$n#tDN}0yIW%@Vj!rwHWzTwMz)AvJ+%|*RA zLHK4aMqdE^x9DR;eM_B~eJR>Lg!UWQY4-q1keKzX7(MMCB0itf-j8+Y%KqL zp@?+mcuq)D2F#zf*uVAZ&(mhEM^%)}D~d$NUT6dF2NI|yx61w|MNRj6U%vNEAXOg{ z*`7XW_gRe2#Jv&sS^VZp^uH^LePcoXKA;hQO8X5^g2cQ*c_`LErxhjpsuybT#4g}HH`1iT`cQ=Rla{jMpX{gQoJB=AWm9>vK8OuL7Ymjfrl;FMjE4DDb z^KY0w0(g4juq#eU;G6#pkqFdkQn#>XTa5Z?uS}lXYG}?LybZ+;q9iqu$le+@vg|Ks z-@7!H7o(9365_WewZw06 z4NV(EX%~d3AB7Sm#OTblQFU36S~u%R-tw$Wpq4#rLHd56jqpqfe9_rZ%4=XskPzcX z)5e#HQ`F=^!??9oO^HA)vAQdI3H?0QI(n$KVliCFVW(AnQ-Z{defhPz9Qr-a5f-7& zm>I(hKeR~%YQ6cIPkVNC8(U~(d6sr)PN0z`N{|@M@@xH-ZX@{dE7eh=3-<{xE)l48 zJ3gP*`{=#v`8jJ<_F(`oe%Vf=3{!%H@S=+RTl)L4sOllLXYEKnpUDJj;oD4E>#7}6 zZA7#u0wqY)u9a82gSw4$nQp1WvUcZXGTS5qweX#%Sf5{2=6;83aNntQB?7es-Tj_ocv#58$%Us=csWsmgzy?3EU6eCullrOq;jk; zPo}XC5~wA9e_5Btu(7FlJ^oiz8Q%LwG0g+%ksu*f#rGadW7s(Rv?Wh|osFNFTumZS zOLz+B4N7a+5Ix-flte!5$Zyw(@tG0?- z!VkB4VS4SjGO^`aAKvu#K{fJr5Ut&r$3jA^oo5=8!DyA8T4kqJZJ<^mfm*`$@}P`H ztA6a&oqvhErHVbHC_zH_GH2}b*l45lYSfNe5`kJ`r7!N0$42^DQGD@9s!gNK zIZX)?!k$>w*YC$GuYtV!hE3|}&E@D@Yf6w1o;$IMtly7>8^ie2BU9AlQ_4#OY6)+o zh-c7k6poMPIWs1!K~#YlB}j->2NBPpuN!P_6374WEw4U$Sz01ct7}RYZJkrMQDsdG z_uAJ(jk;WuVmZxY2_LCgaTUjvi4B`a@T<<|>eAZ2w2#F+783Gm#xf#uyPK;OQxT{o z&PlupdaIt&x^@P7+QaB+7js#0Z^djh(-`4J%c#vW#Y{&0)@P~P5~5gAnZ4?mF9F(& z#UnvNcs_(LUZ1hN-xjdJPA?AR1Bt`ivn=k8=(U;Gm84F5~Xtk?2 z6YE|RYL-`wk;~5eVz4uucE)oOgrx{QZ8?lAW`r#+K3uZf(gAr*mI^9Gfs$jonsjq60rFl?X%39))5zES#& zUR>Sk)e?E#v~S!SLS*ss4Vf+0dK?Hq6N{ET~3tucibE;cfZhNgBh8)3rhf3%yr_@1)#@NT8P3 z;UT_*x{XJ)V(Fmoc|H1`qXY@Ddf9n;TEoVkEZy0jEVooUjmVHdEqwpU>pZ#(yVCu+ zno+4D^$bYV?6?!(c*Qb2?s3kn7Vz;_Du1rkdHj?itTid?z#{kL|W*!TP z{ckI3k)L|2(qD+7!0=0y9N7T*LTNPF2@*}}R-*WT zH^!O!R8HV^r!BCp?I9DWrRT$bZ`e3mBZ2R{I>(l|Kwa`WiwxYl1c?i=m9*YRKMzp{ zTfB!(n$aVHTH^1bztYdcLx`LMC_zH}eqZs6aUOi#C~k=>>fTqbueL85T_j5PZ5*bq-4J&kMwJ&8!5R*Pwwz=m^tDR=XZ@$CJ_mXvYPlprDEB2T^F zrp-SUWo1GQ^+5JG=2xwRM4*N|>-}b8dGKO{796+mmrUVI*HGRgu?S>7v zR|Pd8B$o9aDif$B=DVU*x-a8e2D_T^?FiN?j%o8+j|2%Z^R{Q*Y4`xrO^r~WUx{IM zszigwLalNcb5k$+hhf7Sx)f{fT)9@5kh}f&X%Mity6|1lL zWIPfigy&_!{M|;YN}S%HI_v}3^X>H{0<|Lh2%pm~!^U>U@9O27{n)y94e85iN{|ry z89MfI8s5PbG2hh2MI+eo4HYE{X0BUgXeW&vnExS2-Fh39@ZrJGGCmTOmxF!2u zu>s|QG$lwl!-`S<$$f^6V&@BU+ouq=VSF`-KrOL4Fs0re!-iM)g8Yxa!dPL7TS5sE zf1W5tI|n>A=H4yBhp-S<|5z1?KrInTHTLKI#<;!XkxIOve*jxptRdyVHYG@8Xjhy@ z#|Mq`pq$RyS8Xxnbw&cU#NV@B^w_91wi>Ux#*h8;td3^GBSAup`kT}}YK+@6P4ef{ zzgJ~X4th%jYKeKt;unXF^SD{j!atO)%vRs^*5)4`2@+z3Z2X>M1`+NR#?ve+$kZAB zDoT*Jf4Vg7n>fMr)z>c}!ThIVMcIYLJ`{&+wpWbw|2%O*Yr9O`o9@f&&-7(oC~G!K zkPs1iBKN&MyZfPJ5HGXMoBf`?A#M4Fl^Ix%vi>SckPy3=sWPTE z_EnVhox=H*(`i^Inx7+qS|X}O5QYu%`%IxYvRqUb03}GQURahq+ZPNQjfr^uKM2%X z9#o!I0WKOg8rR&SYKY?`(C?RpprzOmiV@dXkUE<5W8xU z<=LwQ)@@WB>02AT;~dpA*g_Gr@|T+SGt4aL3_U%Hd1Wg_eTn%EN8)_beHyJ-D|j;P z%+74I)8f+hDiVQO+0$;Y*RHjVJR+hhdtyfalqO!ZH^Wj(MF|pAtm>_{#?&#Sz(?(F2ip(dgm51i7LixzNsJ_b+wsqJ;@?Yd+VmH57_qf@7n%x<8 z!mtsSu@bx7Cx92(*qFQ@rUVH(zI}Mq8RIqOf5FQhZ4o!)Gunp0b|5x>PwG;tomJBG6<^>UHFPh6|>Ylbm( z?G)AWvI^~}8b@bWNYm;(eJtgx)mMD-j!R}DK2ayuWF5f{Y>QB&?v*puSe)R{TdW-Dq*1ZvGGG@c3Xlx{W7G+}PN^fs66XO)|8}C0= z+qA^62i6$%Fhx4a5|a>jQ;b>kBVq0xBv1?QKlQskhqI9%Mym(o?2-+tAj(8kqqshD zYenX7ajl~1DM1Mm;c~NS9kW-c}pEfJ{crXi>e#3Qd{yEHvSqJ!A`&XqJFJfnd0G0 z2@>*Ko4#)!HY@EOb!NMs6oq8I&qz?!EP6_E8Le8Kr!D)bN(P=cQ$vYBE$lJqjmlM( zEn4Hxrv_A^xYu$tFMFf)gV@7iU#Tb!8dhda^IP~bi?>9e7UgkcV(zTpk98d?vp0VQ z^4OS?QooyboP&wgEb%PK#K{Xa*rsQ-c*}k^6(va6v$khxt7I~+?!DUn?5|-}`S*QQ zB?7gmS|QCrvl^``Gv3ZzNu~LfXKiSu!IU6DyLsp!zTKK$C&HIDitWElc{O6ynl>5`H zON8}`{3q|Fo*|OPqYvmRb=zNrWsC~pH+NJf&$cN+;$1}-J#W1<&l>GldsdwNbf6(G zoT0cxpjPOu6nZ*)8#XRn&&yf|H05d5dQ+UGDM3QaX+?%0y^8C+?Pb{nFFRk*o>4r5 zDM3Qa9>s1n;qOzFD+lYaby=$OJ(n#Kfm$UdY@n|}XT!$lv2|EnTy=h?R{&LgGbKpe zZ2lV)d0Iu}3snG1sKdS_SLZ!b5vaAh_$nsyf9mJa=6h|nu3`;7uCUlMYD$nezHAkn zRG@=lWq+i^K)o4?IAElT()EcQSp^;=;?YJ_L zH_n$ujPT{{X>9@}NIW04guVIN#%R@*_hD>ZJk9XdRFVkPDq3R^-E&W?svQnzeNHL- z{uWVH*OVaf=Ib1)oF%HSimQ9Uzau-f@QXU8Rxyb{t=hF_vxZZfc>0pJ;jDVKw0!8* zqLeqvlpxXl-VEwX8W}{|*PU3C!Ee+fS1Zy!dQ*agIFiUVulEdt*Y{%^*X&ZKh5JYZ zYK`ABg`ULvhK-!l`mqP)Q&cv*DrJZ@B}n{v;zyc8)i-RMnb4o*sJdN^r98q&pjL&N zlh||0V5avBSBUtt%64@R5hy`osBH=pwWaj)NRu#}RiljWyJ_Dx5~%g#fQc+2pWU$W z%dNre%kw4b)W;&rjVVDQZIy|X`^9Du<3VYne*tff78fJT6(e+gEE<5o#3@s3ng@SI&RseDr;;Wg^)? z365UzNR$^x&E%?S>tX4Xs#QqfXjxHKS4-!z{upZ+l!`zt9Q9K!z38H@t<|$y9@2T> z+!<%Aa;s(*575s1%;zMI#}dCia~zr2e6xV7$q;W#rc|v$0_RtXGO|oQm%B+NOOaFr zYGG^X4sNt+ty)X1!nWfKTW-~aW_4VJ_Pz@EOaw}h(EAl{qc7R=ysWEGqAPGws(X%F zINw#2?z_TVS1;RA(vc06VE=)(73EoGn|2<{Q?&{S@!QjPDas3jSouE)kCxs-Prtin zesR~Fls$o0={!(^J+<7bpX0q<_nsUIyr1gKk-%>W?ZH}})wT9!eoI2CJBV8Ng;SJ< zH{Lo=m9klG5rGo?I?Jv4{laS}PjOhDq_Tkojvo|dbm3+0nnS$Y!8zo6j#@aXq4Cv- z)y|Z2L6*O%zd{L)$mCY>mV=x{n+>v*`7YZ)0>_M$zddfIbN;pV79|ydS~y0f{qxg@ zIorDWSxOOs5*#7RttwW0y=%aXqRvgP2fP-_#8F^B?iFo0W92!miw^?&%J@mUr*Id+k%XRY+i^V9K@-62H4nwbp^3Q?&}U zuzsVDN!=a zJ?Hs&SF^P;fm&h@rdesc#rrL;F23Q`?~ju>O0YIL+E$dQa|*dbKdeiMIV;;hLXYt9 zRAv7XXR+4))ytOeFPT6stm&>Oee30O+eiEzkevvWV0C%9Re#Pa=q{A@T7HL&;h+pjgB&6o z+OJT>l{`BC{#^jte;R=jBt%|APi}diqBUg^G;3kEs<6-1szQD1msG7nEm0ZFQ+r!cR+Rh0-E7rq z_o@w}IZCj~nB1xgQ?I%>Sea6sJg2zHF@!PD<6+PgM z%P~0G+2wW;$33H{g|)pD<#L(iaD{&>rs8q}A7UIow}b&fVSrl1!i$?gk}) z`>i{!(ss+ zU1-M~5b`^Zxv)BCf9-nn`;(N=JNmf9lO;8 z2RMxr=Z$pE3s~>!P6SGjz~@c>%I42`&a#5$26!Hra{yaNeS4GYfkUocwoD-cB}iaa z1V!;Xbs)v>!~qLWMW7buYEYED%NDrXbr|i+YfIuN!K@Kzn`+({nUk`r@El7c8p)sp z3CwFj{nd#=DQ%Cc*3ZH2S zOJxHI%wy|+f#$^Qb15xK{+B>4%o;;$MP1T6A12qfEFl{x!MrbWtGbrT>Ad&Q*Rn8G ztB}B~V~TR1-%sv-kFGfHbd~QpYKc8~W_GbJO*f^qZoDb*HrYT4W&x91)#-=lDL3A) z4!oGERY+jAE=4)o{$|Q&$J)TlsR-17>E92}C>$l2*-CDeu1#LfRINfn z&SvEyXbK{=3bjP^wb*Z~#~}-1_s}-lEEQ*?{*%*Cu8)y48%iCyoVo1~_gKH5cNd&4 z&xDcq-{&D(g|lkZ60H)Mhs;*#=aS4(lA4gugNS5d0}1h4e|}S+hkktVf4Nmi;BGfl zKQ)0`xTEf0LYv*81b6M>6;YJWFSn_$t7Nv#8$VdOR!HDpLRt-f%$@#yvpZAhYk zE!^R#C{4Eh;{0`6L1zhiu26!zGx11@(l*-$=h20EoUc;ZKmzxVQr!8~Nlve*Von>` zz+<5n?pLKKi*4haE3y`Mik=!JxLX#Fq+i`m-C8==r?P>B*hOpZ;f)y+>fF7yk~2OP zfm*oBSW#lSwsf{ju0ZP(dJpH>*Npp}f^jx*~d9Cl?^0tM>dW6u9s539WH6R zJ!h13tx!we$v&^yTxX{3JDs6)KTv{u(dAZUs=L{_Enu^=WU4!e1nx^$lubF?IX53X zg}W6L<&Ae8r_%JCvnLTKK?3s+P;QYnS=^mRp0)gWStd{mcPr4&N%fSw$%^aN zWj`l!lpuk56lj-I&liEUj%Ri)BtJD0sD--~sQOUZcPRm--#N3=d7uOd%m<+;zx6BR zKDvHd;4JdPA%R-B_u$|Dihd+>jLpZK9GC-ysvI9HW;uWOS4%E>+A#wOM#Rg+fs7~J zYhJtx9Jno!qXY?z*_T^|1ZrVxY5!o;K`9aW$2dn)tMF`vmRJK8Bg_AudB~+7oWqDf z2@;q!L{U25dlOjdu#MN|2DR)rw`4ocpu?Y}rH46~^Y{UI4tain2XlFK6cL^DOs?KnW7KTY-hV||v-J=6z@05z1wJQAHZYS8wpKoKlprBz9kN9Xb03?Z##L&neCDWy z5&yc4G5y?@2Xr1NK?1WJDaxQom-AAlITn_RKrP%CKt0^#Mb1J~7g!h%Ac0!A zFMuk5)tKxaI_0=00@FntH36nDfQLoFR{h*bb{mogH#1DGl)E5Ey* zeBWx#K{il=1n$VE4CC>6+;tXJvs|H-A0$vq>?IZJZ2B(K9EHZX#-0kbz9JtP?pPOE zo?XAVTV7G-mrBvR&iP-6^+9vzJrc4FzTV$mBQd{w&CLH2o|a&q0KG4H;GeN>iFRST zP=d$Bb5fKEeP6q7#a(pXp=*VEYH<%Qo*z9WGe5Wnz3J73bphVvYa;Y8)!4gn3dNr)}vtHXx|Wr z^~MW*MbY@QC2K2jp887YX-1mPV{;?*?JwO^y1k%kd{8Y33IECU95J&)4I3X`)OJqh zxt#N8T!I8@$*mfc{G0RsjycXK`r_c(E)NRUs@!f{^n&4$XvfQwtCmtTazwgXd-3Nl`yK?;ev0)WSODisIMc(n80eBSGPd7CDRb+Z%(mZYZ9o1)u(8^)^^sJCvu$2|DBjenU$=N=`JvjH*2iqw_h7=g;h#P46ITVMQgGXxnqH zJ5!NjmfOveI7*PfI!_d_MbE8Qk1no8G%i5`wc6fkOa5I?t7g@kos@Zeu38eqY^n1s3l*k8#yw$FTeZ6x%R5)cg?Ga1Xk*z_>!6X+%TB(Rp0qTEhOa<5yL<&Un(GJ#sy=POG2 zbl=>$XT5cLr%$BFvk2`BMYSZXSJb3#2RQQ;2iLk+J^!5Yv7{K6m=YwgqLSXNk8D}b zvXjmn3Dm-Cs3pW2}>awP+*lo3JPeq^>-dRNnUht1|`5%7P z;?#>`{UEGYgjYmSN_($zKU_ZB`K^+O(lf6W5?E15QRZZ7>&z0FVwp!@10+xjuN$qn zuMBbKSrKAAPpv{?{GJHPj9!O!Mn7ZNT1APha>QL{c5c_={fW|fAmN+2llIoO&+&}D zI0-Axy3Y(PNI1jL#;2o=TxoK5wLW+w6R3sNjuhq3 zMaP_(f=62)lMR$0G3!|;trykLBkzXQ?v*9CI~SFc3Dm;MM~d>a%?8)CE3>SnDOv<2 zNMN-i8fCQp(S2p&P0P?;;*BzE6N@ziPc3Aux2`DFG9GfSPk+d|=1CIA(B!`G~&fNT3$hV4@wM zN2a)XRXuGTMB`nQAhEPaSIXkyv9YPhQTLqC$*znki4uWYShJiWGaDzn*Hpi1IWt;B zg_+gNk=Pv4oknE-RApII84IgC&b{jXUQ4rBnLsV9y{;&IRv&VOe$Hp>uvdJY&8qB3 zd|lUFtG%q-IA3VK`_`{hQs#xq1ZrVrbMpHP2vmJ0oN;?~jHmC1StT6_tW!?olI8L4 z!jooN|B4rpvSwv;B(78KTrs=QtLj!gwa*>V_KGW-Cvub^A$GU%C4rQqL0l`_oipy@ zFTC7Ol4Jt4u%5X7)<)JiZ)>q6k>fLp&+hfIeJI1Y&8TD+dSsG2qGon$VE-g)l_^02 zD}pP^q{0*38#)xVHX9@psD&-0m4>nt-0H-d*0Dpy4AMM9B(So%qLgA|-CcXNux1<~ z6R0JhdCPnA-8=0goGe&=Yg1nZvkLeHuZ8a5{>z-j8zj*@%B&BL#Fdkgw8qA$X3-P6 zA1i(5xyLlKIL9@T3Dm;M-HKA={z~`Vr{i3|mrmj+K|-!H-gx6kcjJxkt$F$-k)PVE zL5@Vlno*Qh#bLB6M~e;a64@uaymQM0YRR>iH+#>Z8DuWowc5h(W7a=MV*lGHT3xDV z*ht)yPW3JR)?GLKD2YI=+~Iw-x#8Rf;#wVOzQp}gnZwp^)KiOoSInh^1nXjpipTEg z21dQR$KmtcjxKcr)6rXt5+tzVHSPZxFvDGVzzplMRx*KFa;rWcS?bQw`Mpamn8Zlp`z?8vef-!v8C3VmE|-4oY;@P2EqT2uk(P4 zBI(}#n7{z2NHBqjS&RsX&P*30A|R5@S)*K<7f@jk!4-8~(upSo53ucJWkQHM7g#kG5=j5yovUld0|EGzlLvyavF zPg{+LhE`4scl|eEQ{h+6>5cxc-Nxtd%j&k~6uzka`{%e`@m<28whgm$w4J=w*d%&| zj^~#NRN>d!>8h=^+Oi)9=)6LeSdO@FU9U{s?Y7croVz5oUvLV?2oii{4|>0iwVfsB z?;9O1bTF238Z8m1!mrMgs}}6A?`(j>zPm>^?#Boc;wXrUJ?pvq?T@F%_U3JAV;YU- zNT3S82u}Xhu{&uu+&89`x{<=gw|+wc+gjeqdq0h$GrS{x3Hl|=x3v*nA4!&F`mL81 zGzKl5mA2+g3gxgzN>q;y8PKhRMTKjk`u6ABj4!74*0pewb0CqYX$*Z6xn1^`dyfT= zHHJ*o8{KQm1gh5ci>CL@HrZSCH2Al_#F^;zIAb41kPyqAwYD6)iflLf1iBlmJ{`?} zC2T73>xq`l%!M!M&fx!Lqw&q0$h6w@CXB}i_x9P>NNW3bwj4vTJ+?n%I7WUYY)4(A zdF;MLoQY$Wc%?{$plzyM$q_UIX4mX*NZ)o_ZtT5gTHSQTO4wAH@evlHa_d(#zYG%9 zc%(74X|1}+J5xADkZAHSjK&*uxAcHPR>WUG85#dCNb4jchbJJL|nx)j9m*%H=TpKBE6%yiUBgX1m9mupTB~lNLO4nAT zcQsrWs)QdT+VoZ;^!2>dPTlTmPm?EukxS9_LdNv4s&GwotPUlmRyy=REBdxEf`o7& z#XFBRN9W1Ts>{7$spF@PqIx)cL^x@sCbqIvUE`W;*M|6ajFbb3at;PMORV(&O`rn7?cHw;hjLOI&I_cQjVJ5T;%{q#tnDyXeB z$Fd`1i12e-t^RzG%pay7O2Y^ey1Q3Rj$_>o^qxh;kedtD<1|~k>rN#V2~-KX-9dN5 zr^N9@bWHqB?WP&RdpsSk!w3>$Ie3b@L5#DfXYJH&YUcTVJY&T`9Y&Ctx$uf2AW^UVWz*sJZiZH! z#LPZtZX8y>B=q8md%W~Wpvp#zSqwCqh#@akJ|&5ZGYca~*p|D`opE?O* z3`T6rVoPe=zGV7w$<1(L@Mt3Ze|%NDUTMpNgY%@>RAODIdNBNwsqUX{2Ho9RL@ZiT zgg2`c&zDp*XfcAsMc;o+^j$@R7@4clWSny2&*S36R%wtxm2J87&N)O(cvyyC++1G} zV=!V{7VDj0x@hWH$ju4YCDw(iHnT38=o`|8b06mtu_|{U z56ctCFMUb)3nNI-d}pRsor)O5=vIy9c5!bWe=LF@&g!Q}0#%oyFPe0Bix?gZpGQP` zs4K5sG@iaAy-tr2BpTJeVEU9;#2{vTpb?;5^t@Y{D!l%}Khuyvl~`U7TEwvT_yQsp zzNpFfy|2xy{V=7WDx>@b)3K-`mTN*Lay0A2jR8;9&|dp2gl%0&ly|sb%H>zYAV%qG zG+A`4#9#3X;<`|^?8teOlD~){ZpK0)ehZA`J|W)RoK#bX5hQM$J!k6L#ntfa>l7ji zKZxVAKNR84g%Z+`Kvn#VbEbk-T@6bXFD61A)|0Q;cU-+yVT2ANNbFf|CB!&ly3SLA zc#|=;_`?nLBmz}0N1ij?DeGz|b5rD)ma{c4cQPk0t6QVP2okS1o;B5LU)Zqe=rkgh zjcLo=E9Ld{B@aGsv&RlG$c^fefAlX`}Tr{4;7aYQTbRh z54^crO+0rs4OP{4pE3F8wXRntF5el!>r}68*$<5T7h&BhICsY&VOtg|$FXNjkJj6^ zsvFg)RX=&km^Nyl4kJjc>vx8(S{w0r<1=>1gu9?Kn5__WBK##zaZP5!Z%K8 zWNGbVUl!k;@wyDGxzWeTE*j7?pAMw3Y`0zWE^&M-vrD6mLk#9gZ zXG5up;u`ev7{vpM7E^8*rs$ABl{khXhjUIrJgYi_mvatNiqx4b5vcn8&>6b77cg`z zw1|l0uuZ~nbd49;m^|#hpf$#S`V8jR#VmY9emDXrpJ9goly5Ca@jozdC zm9VLXrJtpzLH_Kg^P8ju{S$|#@D z@N#4d?Z*+X+FX0upMB}ibr?ZHJbh>shQZoeYW^XJd%yE%wThV~0#%vu=S`znK1=JI zs_q{~@`!d`?4QQX(lCNVOp=w@vs|>6hE#9P|16Y`ndWto2vk*Wea=)YcYcGIZ$zVM zd@_n(SX-RE9av0{5hUEVpEFh6ozL)X^K2rfZEeHv=hUzP>ESvgP-Q#1V$LTTA7yOM z5Ba@U#Pb{@|3z3^OP5M_<=uU5{fn^CLbWFBEZwgPWVe>qMh@bW=FU+5p10755hUcZ zWKd`rUsJ@FWzAV&#NWKbf!jpj>y4L)LV9oi$RTZ71d5!ROe zn-V(yY;Dzl5jI+={&;iIRN$&(cH8GcmYxq<;m3-6qPmU0kUsKYb_KvQ-%QFJf16H`0~qcTX)^s0+*R?whv0 z-({0?PDcav`Y`dHFy7=~D0_1loAct5>4LAL!K-B_B5vour7kMlm8D1JPQwTiJ<~4H zRqJRFv+HOyYi@h+go`ok!;BJIBv57BTI*I#99E9sjSXdM7gtTg2okB0xaZ=RqehI_ z^D#)E%J%=(mVQ$IKh&Nc{n^c%@v+qm)iLj2#+I`*Bv6%6{iCIB(r6ghe?E>ZK`$$uTz`af$sBNi8wcClsfA95O&SJ3M;XRn{9B7v#`S8tjcIyo3F`nDn>w$K*iCNFW%)@U&D zUqq`;TGCydcU#HPz6)D&sg-fgi$|8ToxW&BE!HQNtBB@wXPb{cw4C{3#xZ(>T$b6j79%a{JTiG+ccAu8m0z~%yz>|3V(WHnRpTBy+^S!Rg#8ZWmsF!1`7-V)HQ#n- zhjjfVLeMr9U-`gvVz2`}&)tbgd3{bXz3NU&WO+g4zlauf0!Y_?-*1ZgW$?cU8!c3g zYTvhm8el&|8+bsp_=TGZo0ZQFF7o3 zq#L^7m6EqW5=)IJEZrGORDVou5k~`k5lFgA&|DTdT-aAl6H5-f8~hhBc786>{dLom zg%yqY7h$7?3Xh0J)B8z5Hh*g(n{;nc8b*-7qd;>lUy5W;3VX3D(}MK4hq&iE!qZL5 zj^s5M2Nt1iN&FDV_7n(Uq2)uNMKL)75*1tqlJpE%rJf1oXlrbEM8T>VrhBpZs3r4=a=4Fg%LW(!uIyYJqC)~z#(~c*Zxahb3s8kJ!KS#LRhFrZ zbr?ayt7WEXdanWo`aTD3OXkyL7P4x!;+L=|6$w;57@lc)=M>k9MpL8wVD@(NbjyBV zL*+ndARO*l3~ZnU-n#+sBDqPDjd-Z^B4+uWdy^jG^i^%X|3wkN+sP zWubcaJ=1jNw3C5a(4_m}FoGSw5TShS+9nkvNSr;LY4U1m%Tc}IDE9qOA?44u4tmUv zTO||olp%~yoS?M!8I^_+B<8%xq$|p{Rr)of*|^=O^pg+WlL%Dd)@n2(Tcoh6Pi9+d z0kZptM4u4$6_u#|!s3Kjk8alc6!vZK_qri-XOdrHk02qIFAsIjKDxCj$MYYv^`+aa zKlm$QQ`wH2IO^okQx0+7UfMBBhY?(_Sk5SK%Q44s1bcrb;$OcYs%&er{^oO94Q5pf zPg8H5WtL;r;ID$@g*h>CUJaUC$RO?vA1Y?j`*|V5Z_mXYWLB?EEXm=i8a#8j4kJjM z|M=YEi&8}jZA&FxZ`Ql8N&R$lpG2UF>c{AOD`cRt%9JC|A1Q1{?pWjZd&jh5+eLq! z_&vn73%72v$92nZW9!qS)>6c17T}g?)Ltu+CJ0*u3Gw@vnkd{s@y2{AWfWVT(^>Uu zy+jDzR)k>g35R7|h;J zoT8R7F4STK332Ar*HsLcq90I>5mSe=5A);H&komhNT3RT8=ARgQXkfB`T@1;(Z6)~ z8;JYWsnvxIG=H8V{~o{X>CBF%-&eDC^-IGD5}y;FQ?F%VgYV9_v{l_lc4POOUr}?i zr3aBf71geo-d-$B&YftNq?S%#--2BE`{W&3j36<=ltFD#S9*{BOga9^7t4&ZJ-9f! zNTABLOrt_6NB8M%n16kZg}{hyS*%w);r1SPHP9RyLt;GKJc}DHWA_kgufrz6sLs^R|<@nPwjdd77!j{jvRp*Y?W|e*X`GyaFrXhi< z*raE4wilsZ8PNg=nG(cGcc{geHb0Su5hUdOD8D&X(O2(d+4HZXA6WXZudS^?z3kTI zn4~ziap@z|WDj@4xcTD!_ezX5+Eju-)wuZ&O@*7gTaJQ8Q*LEmc0al~ zKlyiIJw}jt_x+Kj7n$l0D94#9@0B^^-ib322~^pZt!Ie?cVMo6w6G8uu`P@B=4txG zRDYVA;l|n8l%vL%pGxeOHay_x^fa4FtP54eazCNIA~%EeY>x=@W(h%&yoc|QgBU@= zb}p3O=}$Svul8o!Uqo283JFx%maXmG4x1~mPXi4W0wcC%v0nOWglW^!B9=FAnv;Bd zX;x!l177o0qI4#r3ePc(=E>OzWm5K+Ty=Nv{{PB~D?{&7f1Nu$8JaQM|66~LdNpS& zD@qLF%bT>)V+4uso9|P5*PU8qO=-^)uFuqWFF%^!{dUZV1gbLQ?^$xwJb^?!EwWYd z_}Y)Jb^ofv2okpQO`Me)O?dPYrJLtK-Yx5XDiWwFSoMLa`)qfE_4i2kRh8sjy)8!< zBS_em#W_v02(}-pe5C#&aVFaKLsX&Imc{W|Rs61LUzR&P+dC6cru%$-4XS@^?fy=O z5hQFCFdZI?{aBlvSHFrzI*A-epvtx^YG$bAUCq_Vu2_gM7_lvj^|tDC)3R08_u2!C z^Q!f$kK#X1jWgO*VqK`hiW`mQ!|5Vy{D@3_Y-S9%ZM)cW+`_J1?ocnXiy?kSGukS% zS8-NoONRb(R6~vtBrvy_zuleP9hjvrdN!J41nY8eO*B?Njot-|b<{iY04WC&SaCyj zGQHhd*FqokOXfyP1gg4rxng>9-X+_o4(eWvRrxK4lCNz;j@P#BE+Kw%d8=Fpc(C~X zS^Cs_jiek%;N6biYkzZP{`c zWZ!#U((k#^Ov-@-UcprL;_AUV)_tL$RbM_4>Gd<3acl`=3;FHE z$Mm)>dPb{=vt*BW|02hze{-5w%^pEQEZf@f8coRx`BoBo|iS~`ltT)Zp}DGu+=Yb)s4TODcfBJq<+rXNy>o)wg#yluCf!m)bpf% z&%VYIfhugsdSsh0d zWSP(-fs`rytofhzfms#D#Ao%rXQ-s!P?og;xyYU*9m=4b6YF4pf^-c-5< zQHA#ys(~C-fc;3Cr%%4ugsv#N6ORtPyyxqW=XU*!a?LqLY_6K{_vEeWUNkq;Ry(YJpV>mn zfdsm38cm6kd0BL>_4@snn@I$!Y|fO`hihRj!tO8sO*bl6Jbm-U?$Dw8C2!Sl<{YeW zpRf8teOgL6kU(FH?r@zQS)J>f^mBCb{R&k!N5|@T9e!JY#a4IH53bgfW@xf|Vdxsk zTXm{pK9+WDnf^p~SiC8(Xa$&=kPFVr_`D?f42 zp+|q8T75T*uv@j}>&KOk;~2r72KlPpd(eXw8#GtnyI72L4I+VkA#@@xc43A?tMqd= zCQ1aVu!n~FQUi*y?jB+=W0eMRzCU9IE9TwCnbKlt2O%7Fy-X3?4G z>c(p3nXPYKKyEXj3XeM7eb$s<{nsqj7fNg>{T@hQubf6RyrL(Is<=>JSCy|pRN-Ao zqcM2oV>feHjQ9p6NLD~;g;k82T-Ahia z&ow8-{dNMk)u)L{Ijlm%V@Nec^PO06hYR|J&BU2#Ke|X@eHpdLS{GpJFK*QL-`7MU zP=&Q$H1pTM0_^&f&H7ubWdc=L`$cnRaz{3xdt3d?#m#A~jJ;kAE30s8>1p6wg!Mi8 zT3<6k99{c*0T5;{o7ZfePusZNZ`l?noF;nmUUhCQ$M1=pG2Sv$03LcA!pY1 z;ah#PVTm+o1db}8KCioOtV8#Q`UkP% ziDTanRN)u_>Qpv5GT(hUmGN`M)7d@}0jvCRO?10D@4`azr|ZoFQCjj6WR%46layj8Vs2-Z; zANjmO!d6>ttt9`s@R1_^Y9=(0jxMUOmYe1^=<33T-|whzQm--1W@WGc#%gSNs~Xa~ z^R=bbl(Bi_Jx2m7v#CY4$%(bS{f9o~yu2T%!Y2{UpZIxQTB&0x{B%oq{k_=lH0~zO zd~L0pfo8EX{k#-sZtCK0pz)Tbo@sIBJZ1Rp52?NvQ&=ev5B=MVm#NP*j%whF zQjP0n6S-YfBkE4>&2vE;4tTp0acA`UwC^YTun8eFlsoW>sdDRBLy78MqQ}^j8W~69 zN{UlYyXg6eqn_&GM0^dto|e_BEi2XHyK-Xi71Kz!7^;CPWk7o4(oCv|kEK4Hl0@7b z>R^oj*?^t?$H!8Qw=21kVO_t{RKshhMccQbQ;Z?vzBdsgW|cG^SY3&A7{S=y<5x^o zCN(5?xeV1oT`}2c(U(edZW%f}_7TL{vPOO(FAMZ(PBZjeq1JYUp~(2MwEcEk_!{&z zS{iBLOT@fm^i7M&r<6a}^VB193x1`zj^O}wxwM^ zn!-LcEFux8D!tReJf?^E{XHnh^>drj3hYl|j)_(D7(qguq4~DP(QocX#HChy)0Q4d zVN)~QEa$E*(q@)}xybl9gBTHpYtm?X)W>t`KCkQkO;-2BIz7z52&O*!tLKWA*VU^ENX-;fAY-8fsqy!UvFsL~^1o9~~- zVfRNf?~@umMv!>4zJxjAehk%9iyX&VxT`NJjbfYXKhPn8ssY`+%zR}G{eprBzgS64 zX+M&AK0mIDn^DHRWO1}XjJMhmUXISqXajwtlWI|sn7Xct+B0?}tLfKChY=*Y&-OC6 znH58xzSxgXBXX*%?u}yY%TAUERN>arSmZo))$99c9AfUK(pDYtE@!?M8*NwSwnQ{J~T2##|TE$G?-Lb4!CQhDPtY-NQWOegy&|(A$^U0d#GB+dX z&L_62_qt_j$(;jOtwWP^NT6z8R!#HohayQWw#q5TQPsV0FE(yWWgSM4$mLhdydyl) zP`J6s@y~=q>cG>zSX9<+EfT2u<4usc!C&E2-!87G_U+8-?L58NwI{WxTHPK&V(;-F z^Nq9NR1G0=%*uMH_Ux9#YIYl|MFLfOmTT!83o}$|EpqsWzEl(EC$TNVKWQ<7#J;RL z<~wB@(7nA75ux$r_`&2*mQzzo_j+SJGksUhkkX@&sPv@g9JOw{NVQb?De;}R3uQ#t z8fV^uHDPzA_0VDj3EV##cSpn|B3e7h1gc(+)S3%752I=rk)xD3Q&m4DvFdlq(fC1o z1PS?A9W3X=H*61QgO`n#j@7Nt_2@px=nR*S8@<=M2J*>S0j$oTLOMJ?NPMVRpRRMo zAjbF6oaB@IdCuXrS?PRAT_jL7)llC|J(l!57k8hL5h2_oMMvNKEz!Gdqse zQ`L*e@jRp+Uz1jzo$FGJt|%8#h!*yf!xQ&H8|HQb!b zPfOLGg$y!LzHkDMk8)+*bCgKK2oi;xN1A&-t!M~Qs#+JdZ(a}ga9sQSJ+ z(%h<3ZECBCt@>?F0>3b`FdJN?HjP-fuj|z6Nb}2)wW$iF5RDW;;?Bx;{Cv&NN;mH; zEk=;IaW;zXSG8z-R6)v-dsRDbAYx|+nLt&6t5KA%mf_22u~nB}_uxv)Q_9179km!i zqGwto^SXXD4J%%W9G_qI;2BA$l-6b2Nd&64m5ed>*H^PVSEz@#QBQ8(b3&=S@A^Sh zQG3z+PjodJL0*u?^daGMyFagRbfZ$imF5_Q2olDDvF1EYD^qoX*s9yVC)1dpwMv^2 zWpzlPirNe2at@UY|3r&DA3Asdj|f?z41O|J`>U#%OC`&CWkRD4;nTj3QA(N;wHOi0 zHeyG3JUto87>1}~tLj`H!Y77|QJe={ln7LbW%?Q_wW-Bcol}Q%k7hB-)rOn37(v2u zY&>0qzSQayIR?9p;Kf&mD_!&Dq^FvFU8w5XCBeKe%gZ2Ymo%Edlf(HLVezoMn`q|wl+3r#^<#}YTRyYo>w%tfxf0i_eCUp z*QM}z5yhm$s_IW9wGM z%_vB%GW!;4G=DyzuXse?)|Gz~ufqru8ReVMv-W+6py@kUM4X>_OP6nPGl@Xe0q-W} z64l>_h@(KWI?@ccQ+Hm}c5RcW!w3>3syCrl#{F#K2+hYiq52W6Ys(-SS7gsFRBdKu zShhqa3Q~@WZ|`eIEGOF7ix5!wm4Wdc>BSrfBY%hTC=zLW^@ z>vi@{(1}P&k7bsKAiB?5hTR2*jMYZ!W_R6HkJ7Qc}2w-5b>Ma2-}|9RARk!+)TEu z`fmc)g@jlZgmtT$(Qn?$=X7c-uLRw%giR&R9;yP(-l|65Q+Ugqh0{iD%%jB!t~dLP z$1rKMRC3N;#B0?qBE4BpJM0NW2QuzLz&(ey#inpA*w(LkOJJQ&^<&A^cdN28Q zH-$HU{w(dEO^qc2RWui~g|Hso)T9*NtKl@`!H~u}j36QIN?tAVX6Fd&PP5|AoNUbT zSGYu=%0F*o^W~w1&DOh5llCcer=DqCGOu^tUsaA{3sWn(Ak~#1ks2|YkNh*!m~!z* zU5toj8{wQYk)G7W>B%PUC4UpKdRL~gmj5A%K$Tct6IPts?&7@KT5%-L{IiB?{!&Pb z5hSYZj;E(VX?h0{Im-Hs;mfIaHZ4L<(Ir37~8U0m{96F`N2ol*( zyX+i4)(+)i`v<5!#x0ZxRLv_IXP#=PNc$pk^xHO+N4gJCtM)sl{Z*A2U(vkjXhCWp zB2lNsK>lsxB6a5IFItRyi78B4!M zW%?_&YE(u){=MurwRd>iRE!`IJSCd;x;nLBMUJsVJRqW451Bw!_u0|(dsjCLZ-DB^ zoV)YScmGjSeoj4z5hO(0?#j@bW@|gKW&LhEGyJOhdg>|~uV!Btsy-z~nI8-fF#Ams zIn3H_d~WZn>amEqT2vj2iZTzH8(`LG3eoIONKC%cnr95j!8aNmbr?Y+yRA)kAkl8$ zv$75U%U8q4xtuzP1gZ|dkDz?P8@h8mc-xj-Y!*FTa)BS0Yd~=S6+;8T4k)>mo;Rgu=B3 zKVES|P%5f^UaC+2g>}6$QM9=~Kew?q@AoFEE=G{xD=qKs^gI!JKC-btkN%@J-#xgC zM4;-_YJ>Uk`w+8pPVs)8FWR3^+g+P~ZyZ+_RrG}lYK0rjyOIl$gNj7XmPPr<{n31T z1&t0PNUV6psP$UUY;_G{CY0b$n?>=E)qkcUfvSC3dh(V+&7HMktJX{`$>ZlX!CGXu${P*iD{{eO*Wx2U_0r%~lVfWzBrNdgf!}3*msdiYD#K#4=XpulwK&{&J z#u83<2eIcns=ZP3pY6csd@Vt4l*O&FtgEkUZL|CKaIy^xxeH%u8iG5v2{C=paIiOaQS4Ds z9Em{HyYCgrp>Jf~uvFxznLbf9E<8LnbNd&5{3@vAFR6E)%W|h-u{_qY~zn&Sv7hYHyv2?k5`n5`U%kvF{j36Pr_hIQV)Q%JP#1!2-Jod8G47i9l7s zsvhR)y<+JeDYk00vqm-FAH|!leWAk$62)?Rm`hEJHH$gd=*vLu*BSE^NZ~&(<ejoTVSEBN3>guTPsVMaNKU zT;xb<=A<5PH;jc84puON1oocL$i8{r>gCEqS!>5oi9pr2&0c2Fm)iTO=pUTy6{seK z4`KNR8WfBmfjuezH-V~p?aP^|wMKK$i5%6A4^q`!16jY8It3$0U@r&FuOHQv2vj}$ zTGP^F;x|d;Xc2KleLlV$Td~ql!3YxA%c0TS=yX&)zOpOZ`8q%%P(@!*qV_^K^^l7k z(Yao$L+`a=?G{#3FoFd7`!o{i%S-j|j%}H{sggvXDsG0B+6(m!V)j`27DNwc9yC0j zC0_7VFoFd3Q)o0ruDkGu7h+j2rG`YHO7wDs1lF@u5zv^1(dGE>e=6+i?urUVkigyn za=Z?f<&hT|8x!Fp5vUrLUXP9yqh2zxAJfLwo|Kkk)blP-8F7(oKPevO8k4ZK;DFDt*_S0YeF^GMT^ zr!KiUVn0H%;<>YfGn+NgoA$#VK?1#gjplUe1paZH6H~61mIzeUYadCijM~%_Cvs>L z+wkFkzf;`SdD9hTk060wKaDIN--f$JW+~^#l#vKjm6{kuzeg=juV2&K(yKLsO5psP;fgvZhwebv#5 z$EtD?fhwx7F&hV#p*fyKj)6~y@b3ws%J>~+6^tN(t~%X!-wxqN#?)7?1p7(^s*2@K zpcaM~J)=a99kqvZ_xA;ruM0}k)7c(DLUx^%$HREecqe6f*D4Z$s$71FiowSABl+oZP4y4cD@z2bb|p79cPx}UJI9ZNk-X@r>$+E_$_hr1K*yWT z5;l_08hu&UabXpSKvms88(Z9Snt_J)V`Srz{OZng+HvcvC>TLPc7*>g0#(8(6n?uk z$LJ;_x%1>OZLJMe6pSDt99EkHt>MX3hj4+rP!5bBA(p#mJLxow z`TrnnDzPT9Y~7Dv30tHQtXC}CUFZKr*i<5iShnVvO~1#4;1j8{>G!}0u2*)1|1Sbn z!o?L%u{B3G-ALYS>EpD#^Q+LzCicj$gz%89j_{$lk^Fh}hiL;2SC$BLhcn|F)BA_0 zl@jOG_!}emhMg0Q-LfiEOU52S0$tqyn?Ti`<%!h8TbSGrkz<5;I4}G5i8118Wd$Qh zpo>dycT0!!w*#LU6aKC$5vZEEFu`1~YVqv1)FUZFd5-Q?)f{uHDHuTlU0kXVsxgeW zJXlH9y(=vdsJa&$Z$7oUG`THeKMu_w#5Y&%t4`?dt6&5PbXYZ-mdyt7lKuOse|0J^ z5vXeLS1g@Z70uS?N3({>eD92zs%wV|3PzAXPnMpXL<}QhRePC06@CAMob$@mjuZPa z;Zbj1y3c0yx>rR7BS@gbO4Ya(`taW`Y*NdvFDntK67S~IdsQc|M&#H!vooK$;-WgP zQ3VAfNT6RzuCqrMzIpNmb=M~!i9nTe&L}g@6Gy%7qFr*fS}X1~?yDO5y^MkpB+&iT zXx`On#pm_@s;bjv0#*68Mvy}vOuf7!N42b2{wA>?Z`!zmf)OOpFC|BKQ5-j|cHzmd z%18vNj03~TH>pd0hR6})SD*KCD8+;GR;2l%>=7i;{iLtmc!u#;oxFKqaUY35Roy@9 zn+J5$XL}j#a@XX+p#i+Vwj4b_>=7i;nWVZUZB0I?LI8PvWh4SsSB4ts8V?~?SnS8! zye0UuAexh-U1bF$NTAP1^;c;nxbw#byz`#25`ik=41Y=tB_CPjaQT>*pUu&j2Nv~H zFoFd7j5J3|hx|OEeBHZLR*?u)?aQi4JsAznPtJ=RcQPlbvri`T ztK(}b7(oI(KpJmA#7rVyPLK&yQS}S;`b3#8_zDl8OJonVe(^#4LY)AbG07f50zE*Q zHG6JnwRV9)JfW9XB2d+Rb~$t1KO0d+mBiQft&vziu5u zvvUVZ1gb{3dDC?sLyo=35p*`EYW!muU$Ze-!3YxQ&uKKZ#{M*tOTo7d4wVR0kz;4^ z6JwHu?=x}SUgPF6!}$W|1`0-yK!1+>tAa+O?}p)g;`}-ifhwA#%Hk*fxFm9%c)c#| z%K1KQyRTf?kDi=dr+<8}ukppc5CC$sI?TBc5LMBjEZMPRa(V}TKM3F=PytdkLY%f;BWt)N#B+zZ6r@@p! zwN!9#wqM;W5vVHMyd0hBqKa8KsiP8ys4XsaXK#*dR4{@Bx@|PZz12|l)Y$GUN43oo zfhyt2g#o#qVrTFoFcSZPYI5w?bXMyCZ8BxK$!h_4Z;->i>u^ z9DXm(_Gt$Wsr7TVVP~#xRxpADx@|N&%ap$K+|cnsPmnxw>wY2vjWy)sm-QpK5Exex!fQ&j+7vz#{%!t6&5P zbld1m9G;*1?G0z9_p2lVRZ4zFee?#yyrLpUS`{B&b5d<)N?xvD1POH8=uLQr4{y9G zh(#A!B@w7PF}R+kM%a26?e@Ztr^QxbfBn9KMhV#?NTAzBGg`aU;19yfvu}AeNCc|l zW`xoki=L`9#htq3le+xpxZ-S=`#SP0?GYrdV zDjG$OoP(qI)V&UD{G}!2>DwbnpxZ{*`OGL@Bc}svV_YT?s9JU;f}YMnG;6lVv9xYe zKDKkZ@;G=Yz0unvNTAzB-?RvC%FDStQJ&pdDG{jhye^*p{@D)wW54#i>;Q ztxs2eHDSKeHpeQ7K-C0O4Ao^+q&j_(VivryV2NTA!M(X3z9hx^^Ft?(bKBmz}5 zBQqT#BTTPqXIQ zBS@gzMq?BQB=gmsFY25xu9XN>^?sUYaqg@+Vkaf@nJbTJOWs{e{c83I66m&3`)bSp zeyW*=uJ+sI5`n558mn_>%~5vz0A6L4qE)k&D;Pln-8PM8{^Vqf4_Ae@3JFw+{};}k zHOGQq2wMaRv21hOsGi|}5H^*_A(pKGrPY^XS^hP1BkK<~8{?_r1v2sCir?y9sK(~!%Fk9K5v%l^bOB-bZ zRc|lGlV4ffeCxBwG5K^~9-3KNbru1 zE^Suwi+kdy)Smp(luoM8{E5lBps{XV_beyW2k41?b8|${^ZyFp|eMc{&J-0`Y zK(~!%>uKGVU-mk#?s~gaB2YzNwWey;TI3Ll92-t2@))WmD|ux(ZIwNO1iEchsktn2r@5q(0 zDU-|8M!iyPYTCcV~41gb<; zV8xmd<~c7!j*Si*)T3QH@`Ujl6pSE&ZX3v7F z^Ok0l)as|Y@{D|2$QQLokU+PM-f?PAR&Rgk%J+BKBoU|*o*Y%y&@)}+DAvELdT>k+ z-g3)k1tUnH+eRY}M|D@1ukOJ|vP}|!Dw<1y&RtQ_C~~YnqgBhj>BX-v*i2`MJ%R+f zZ5mCkzI9Z${=N8n-6$Li%|-c!}Vrvbzfp1?v=J(!3YxQwrMmTt@Eg* z2KM1$Z?{PVs)E{jSlq!U=S7Z3i%%G*g!bj9K5eHvoIQes?8BWT;x{60|3aWj$zPD( z2V~kB|^XFIU$FWC{K(|e!>DhCI@!5ekZ1?4F z5`n4{gI%c(Ae!p>M2?kr-x>!zY|XmP{YoByJ%R+fZS=*uN$-pwN3~`-Z-0{rR0&V+ zZ=Xg~mmy{&xm2{Q>RqlCOIrEWQbA{nAc1ZhJq?Z(P)&u~vNBokBmz~!lk5F7%CKRn z$Z;(?KpnZHHT$?S%TjY^iy(n+8_nhYJy`8AsU>Sz=8Ht2Y7{F+6|D`)#SuAPx+SaK zmN#SL3Vu>Bf&{v4RAjQ!c{xq=ZS&~2ml+SL1MH#L;a z-S%1{P__4XU25gmqmkYsNB4I*dB4JSXhz)^3PzAXw~f3dB^S*-7tDt3c_k63TGU6O zu?->Fj_}FRo;=>65_@$tlYDA>1POH8G@3U9Jo%46nn|anOrWaWK?C)l>uD|vC0NTj845;_K(~#0Gp>~7s~o*q|9(#;0##4Wi%}Lj!w5H#V_Lo%{CbEh^XT@N zs&VZRB+zXm51?HQexSb#8+Z1FM4-xXY#3Ge*P$9n%Tw(*wUh!rD)Rb!OR6UD%ud^s3W?7p0#!LQ zji_I{s=;xr$no$q&1(E;3hj9Y`3&|566m(kOjCPW@J!zs%JhMcB?48|c1KgIvLe-4 ziX7X|x8V^DJ1GVCKBB$}djtt|+o^#JHkbdb9d>j z=s+dKp~?&LsqGOY&~2kygRpiyvv3t54y^!YNL zN(8E^OiG})m0X6VE+Plb9mJbxj_M{{eyU&u33S^un&JEgzbqP;1B?46wObOKL zdmm!WarR9TpX=>J6Ru~HziW>mf!-O-NcxUOI?Z&_?fXY2P$hg6QFCX_QS4)q#fQtf zoM~}bY!M{HvdwLyHue7?Y$}mMEdO^7TLjlDmThj^zjGjAQ;8g6*_xwh_Ex>g-Va*@ z*DIE7ZX3;j`9BDoO5_mB)*OK!lK7~_dsFjd=deX^z38@SH0_9(I&*Jor%TqOYa>v# z_jrQExwGa-dzQqL{Pv|)qURt+kU+PMo@!o6eC<}A=JvOE*4o#FDsgq~J?=pB>WOn# zx2ppWZ{5q7e&v}z(;;O zZJha8+~MpIB+zZ6F+cH1yv5t&#(p2uB?47qp1?9Ui;=r6axCcGp09H%s?KkiK@~#w z2omVF(Ombt+Vh)JJk=sC(`OaB8=h#x zFFD1l)!NDgs+v5Erh4iM^h_5yJchL79xh|l{Z%p)j39yDnMPARPix-1$WZm;h$j+( zDw<`CdK4>>3n_B=H*d~wOrE30`9GyfKzjrUbld1Gso#vpjGwC(otq&MsCqdviX4_| zRDUIM3_cXgzfIq-X7qVZmE`sa66m(kp8Le{HDUYJPOUN|0#%}~P>gu|wpru|@@c>W zhTTyI-+6AS?6*abK(~!%D(n%?zs$R>rZ;&e5vUTQ6`XU{rt$hBM}k?;3$^^JF6o#- z{W$gr66m(k=$e%ZPp|StJ@?Nui9l7c+zqJSCs>@nl;i2gT6|NJ0^Ha>gR0f-5hT!U zqqn;<{`}it1^AL&uOtFhG}|IoKxpZ$UbICcZ~5@CO}+WE*>C9FwMURZw~f9m_q+`E zDeKK+)5N)JCr~w}M+o%-C{zU@a;VJ<@+$|b^C~l6C>TKk-8QPyJM6-j?x@aJ7k(uX zs5<;!M`K?N=0$x(4!3FF)CJ|VynClN3PzAXw@stzd-SK;K8Kc{>Go10P(@={sF$H0 zy?2Tn`VRNi7gs|0%H6N&S!<6Vfo>bkVLJAn8ueEwkALw-B2YDRVG!L9!{}W~Z(+)9YHl%B1fL%<5ibDP5I@LpA?KBfo>c1 z8!Y}!J#@P%f44eIB2ZQ1c13d3BFR}3Ig*BTRx3ue;C|KKD;Pln-8PzQ(%e!_EY+Ns z+woN*P_?dKIl5mpq_GpiXQ=l&NbPj31>gVRn}QJ}&~4Lbn%}Lfy5?%d3wVB#2viAA zPPl`E=87DeP)Bvv{not1<JlxpJF`R9l=wtcHsclAEB+zZ6H~J0{#?mhvupaAV0#)?2V``B# zqAe10<6d~P)>v<20~TJ{hhYQ>blWtVtS9S?n#Po4hD@MJEE@+#(cB!R#L+FAW%QjL z#mw1UqO+W;WE;{9f8La1S}8}h|K9qnS)32U2okan zmw#M7b!FT7ENqEPph}E`T@xl|1r|ACo%~hp(|RoPaVdroB+zZ65pMo9)epTw+2nYc zKvgrBau!#O&S%QueZGUb{B;OBImVk|1POH8G#bCw?bPEF>apC%OG*T)HXZe&e!FlQ zohi=50nKKswQuX$=J_QUMvy?ajU3?}zpGnzC@k$@afv_`eZ`z=SihCoK)8*azxk5Qu_}r&&HZP8Agymw~gM4*1uEB zj3~n%^eQe9s9LgEPkqi}#!Zo9aBP0Qu%9QpNnb$02omVFk?XAF=cn@&W%G9yl?YTV zI}$?fppNFYwB&I2;CIS8vuRnL3?oRO+eS{&T~9t|L_XGeVljz8mHDKB&MR%U>s+>C zX}1Ku>}|`Ua@TG0#1SAHABU%&%UI<|MaA zkU+PM+>cU0{7o67lI$cCsH#6Lg3ha&R68hgcr8=-r_3eFio~J}BS@gzMk6Q3G9KG` zg)*X%heV+2^~Q!Y`mBn<+REs6B8=}mGeT*8&7ENc33S`&Y|kIYTR$GGylm_#5vWRy zY(#zZ6{vPl?8oG55xmRJddf`?PlgdB&~2l-@ZTc%=64~=(v32KDw=_sT9v+rV^JbU z@Lvu2(9*?~%M(56Y_~^{K(~$ROZ=nwq@(w&|RBS@gz zMsM`Dqj|*4>H16MWdc=$=El+JWha{XU*y=|G=>kTQ&FE-$&+CO33S^ungcyzIDcAE zuQ>B#BYbi&+hJBKZT>lMp3 z@9f_>kg%yl4zX;_5khE)%G_N24=~jDMY- z!!S0Q2L^0U`#Q5I!w3?x5BFhp4F7RsUD_5p6OlmGoELHCg#8Xw(;&_(=Y}zSQhKbh zE)f_(0^K%h^?Ahbt66czHZd}Rsup$PsHeIxJ<~;w_y*Cu$NW7;&sa}}5hT!Uqn3<+ z3@?&bt>{VFU?u+o;N-PbA-avyS>_4w*pJgW-+HDXKuD zwnPqxuMK!|d>hrRz9+*766m&RG`kx&;4jLwSJQ6F1gfY{o#uzDNNp35<5TTWUT^ex zwa!mZh7lytZPRF`bga)E&EwSh>pUa^Rd>oZq#2Z}QmvZE(WSkfub~Q_p>*HH2okan zm+z{c?|Z*aEx6u8B2YE2XaqSp{xoZv$Pw-z$d5NXsvgyQGK?UBZW}!f+6VI9fk)NC zbZQkRb9K(BZt0jwlgsx!Gq_h z?ZR9C<-st51iEchSu?u`KknD&r_sftP5t+{ocz+!l6>se;tV55 zpxZ_>Qyj^|6XLvhG~F4HKvj!6I+_DAgl54MIg)y1s(yJY@_TAgx--}#NTAzBE#xKX zYJ9GWeDdXD5`n4_ZkBf(YAcEy`%}-Ur^i<34~)ebMvy?ajlMCGabCU2t8@3lGJ&dw zPC+yuU?}w}iX036*{$At9K_=TOE8Qefo>bQ=N-4J!}bUAmzTUG0#)Ms7VG+jQLl-3 z=bXQDj=KIsUA~8V)BA@#f&{v48qKb$3)NAxbUb}$35h_}z1XT2cQAO0$PsXGlhbgEy%V+-Ut#0-CI95U;P$e7$s@|b%UHA-7iUz8SeuVLLmrF2=AR+s3S5Mba4I}FF z>-oJU0##xhY=3=2YP*XZ`7b)C1u4ffqc=T2>=7i;J0p*5LTy3?oRO+eRMp&lbkUx%^mME=3|xC6)_T zZD^nwj%ll22W>E3-C3D!-l(@YceV%;=(f=vZtDi))6tdL>x(jhDp5@rJSEc5%thn~ zjC^5amnyM}OwTZa1iEcB%jU@^#xY&0u#Frq6Wp!y*f1y?)P_H7NN-j>@Z+s#lI^Z2heQNcx-P$(o z52%Yvda%F8)Sq-QwMzcE9ieZMXUBumIkLjAaYFPoM z)T+ZUf&{v48jWfGRW+`6L6)*KNFq?RVX02E01O4IiX0vEAJlE-aj4 zEIuA25vUS#e$ca?>fuC=j!7>3=Ehq}k7t48E!iVTpxZ{jM{5^8A@G*cqF|6jph|ot zh-&KTekF4J?oym*el{x1Igq}0XOAF(ZX5M^H7(9BY^0aN;sFwYD$zICu~2}4u4KxQ zxZ0Peo?EXh98rtT5_<#*bld1l%8tG~#dnP|;#Pn}pi1-&URztku&$rTF(|1b_ccyd z(i@4bvPY0Ww~emBqviSP?&FoIM}i~*RpQBf?xSC}dmehok57ITuUzaML_I(D2omVF zQME{|s{CzM3#G=80Es{q)d$n>QNbWwFPhm%Q(2<4(0D0##HcZ~2C1 zhsPqvUJrkM=3FoR#F*OD7PUu^K(~!XBE74{*KHfAzuz%XB2e|@d<@M?R41P-4k;eeq0hcHogqtUd4LrrWOul z7(oKvHfryF4B#(5_SR+Ol?hadd5YhCe;0D*w8$~%X8?bgeotG02#g>h93^*7hzM09Aep;BZamq!!SFQ z{bkesMQ|J0G8s_OfK4 zM4&2{Uo4G$aUgeFoL2$U0(e=2zwuD{K!y<{&~4Lbh6D%jLyKw|O+#b?RbsAin&;kf zKcQ#Tuv&ap&V|O2a{?JgkU+PMDr?Hr;%(K1#7fF8S<%%R^e|;w^tW91u~2vfo_{dljBWgu4+4|&0T^d0#*Ny zuk#LzA_>~~nlYndz$}6}XJL1S6)+$Qh&e0ftf(j=n9iK_%sHMpCw8X?JW73tS92Z?<+sym0{lI`mG;xMInzbxlv*lU zkU;H>b_4!ZO26`Knz)yxrbM7ug-P{k++Nj@8*VZwWsB)Ue%U0dZLg`K1qoE!s7B+u zBD!zM&7#+XS`vX?_v3<_M8;y`B=h9nmyeP(>swolZb*QM-u_Kpj z*V3-7nQ8Uio_~w}?`zSB%xysewKK|fo{(N&-S2PFZa__mK(AUI6;jpAv}>Km?L|&L z7r|dL>UT%fQqh70s%?}tdHb0tek`M|skJ2ny?8edorFmlBiu&jT<65`be?*($U3CA zyDdnd+D6shW}X#$zU9>Oj+6=X3aee0=4vWc9^y7uh9!w7;raBph3cwkK?2n_I(uNE zT}->4Pp?+6jzpkWz0NghAB&aFmEktZ6j~wLd@Q1uYgAW73lgZd(THt;-}8O-R*eEwv><_M8`Zgc*8tmDE)(cwYgC4+i`A#yz@qin;zbw$j+3~I&ZGJ#$V@0Ft*$RL_|@zefKmTqF7NUH|^++0Nq z5~#M3BHTV&>>ZR&%~xF}(Ch5`%9QbIbt$RIZzqb$WxpxDgQ8TlAc1Ndjb3|95_O7x zQC|Ej6X^9kvIf;>2(bFMqW^EN6ei4tMYVq zgo+jc1qoE!Or~G2eiM;%7Ac)- zMMwmCwd)p?vgLI(m+D-9b4I=N@NvrM1L5@Rx-Cec+C~b?n~eJVXXBOOO=JSS3RMoF zwe!l>kxzM)kvDU8y>Fpb%Gx7Msb_FokU+J~WD0AUP5)EUqns6I2{k`;d@1B~65`h*ZP;H}iwXt4$`}fbx1-i=wdhyw6u_B$- zIh#1O+e^z@BW3%fmtMTbeE&-k5fXu3to5>TH`v0z)fXZ-=k68}XhDLn4Ylq6 zCk!vX6JP)DZ5S53FJBvK+ke|Y!tmlY_}baVSJ(Hr?fM>u1@Ft(hT3K_{eOhv#clAl zvkg17(c|<0+jv(Sh6V47Y8zE(`prw9J7u6PyqWXYH3;n(nr+t#%cIX~b4b(kN6_fiZ9xLnHmbh&G>@KHJ*b`95-t(wb+Jh( zt^5=tWsSeb<`22_`eu{(a5Y>-3lgZdQ7+C5FMXcpXU$xosYIYxm0JyIM(;;zDz|ZR zT5er?R!~%E(v-5P+!iEIZKIv{R(j*KD<#%vi;xKPvNhrgZKYFQZR9o%r-9l3Y6!?ZOjx|bq~+3BFnR;l&$Wz zAc1Nd?HNszRZlabtGL%CTq4k`uRWOh_9|34g4?(nkzVh2ZL0V}?~rF$BE#n**mTF-D=`Egs2K(&oBApT9GCk<_M8}*6X4~wJQu8NG4n@I$E-S+i&)a$FipTEc3>f1%~?#JS%&?w5x zbX$->wT)_$$L|x5d;KBC&Ww}@^lDSLPRf%{eD*K5v8KUlQCWE_d|Ec6EIqdc2~^u? ze%@ob7*+SR__#hwBG4;%S`9jNL8XdTJXd?4X_BaP_>&kgxH+xSyDdnd+D0?s^OJ@5 z+s~ri=_rXnuVM45km79R)dp%K#czQ4^>P}$_>ktb-@t7_0@XI!UmG<<4BeVmFSa&H zBG9Wy+X|Ec5k#j*avS}RHWryS(Rth9&1naZ+kynDZS)t6YAo{4%%CehWdgnW|52K9 z?pP;h4Im-5qIeaONgw^8IjuXpEl8l+MkD<`HN~$<9(wDQQ4)b(SJyh!1X}ClHj3u( z7N=`w(HG}zPUCjB1qoE!Os0FibBWvOGU>j9TS^3ak@`<7?)9k}8@JJL;NM#CT-px15ReDwEdK)MWTOQL~I+Z>ch&ylw^J?(A_ z60#2WetN1l`e|l8OTCs7fnL0GcF*z#^#0&BCPY=$>Nhy4^xoZvawaY(Klv15rCr68 zyYN>s>6F$qCSPlU`d2coZdm;?`VrwVx35;V_DN;^xIUD<u^1G4%Gftjw_-> zUx`33B}*oH+C!|V#eJy_|JsYRQLm3FVSn^d(Sii3ZL})WZlRXr)p4cr9+^O|z?!*f z{XCc|zOy=5y5CW)lqnJ#(|gfe z&22#f)i!#c&nzygy-!iX6M9PodR=Hzj@CG>E|pWC?JwGd2_=1&-YQyu@3G?nr3X9u<1gdS6_ZmM&^sKR4sce=B^!nYWHob8I zXh$yh)LQV*qV)0gN>sldDq4_0wT+{ObnaaTsd&M zyF{QD*A%X_yQ)hOPJMS;yzo_&2V=UcXh8ziHj~LG;Eb@0QkBq7GJ#&{$_G)LSF%>~ z;P3J4i7TRMU;$-$+U}J5;kF=wY8#ybKL3WWKlV{hu8)=o^zzFXLTh*B=qzj=kv-mV zU+g;g$#TC_w2BraP;I0582UhTZ1c^sxu{H_7ta%D=0!V;xebqoe~4?(Pg&Ny?dn+7 zH!Mh?+D7Vm^G71AajGR~MzlnrSFC8j^_Hx8w{sh_`~D$@by;OOKQdZH3lgZdQO&cy z55>ri3oHj)c9RJ7;;MyQTXfvd+{VY;k42Xw9WBp8y3yWnw*?7Q+b9$8_(QRzVjD~I z6*7Tdy!V;Un|u+^ZS;HoNX%T3$?|nxHx(^NpxQ&NkiG=-2Fd< z;l+32YiAo&Lr-eMu;6|9+ECkQr2qdAh8MTN*UmPmzMZ2Ds$S>#9)<<)%h!f>=Kdc4 z8)0~H8`V5+COg|`^ZRpgWVK@Z>}tcX;C)eTqpI0N_-s>bxj#5Rl|i5vpNbjY^_8oQ zw*?7Q+bGLo{8JIT zak6&4l1!jiYH_aTRE+XixQ#U>{t%_-9n@+DMXP8*0@XIkJiBvO1a;Q5>%VrB2=t0N zSf6H(ex$5%8{?91ijxQb(P};GrlJK2RNLr0uPu+nmz&qLJQboP0=;rptWUGM(ka)^ za2waJ-xIIzq!%77qE)mYfodD&AuqWj=55O$e!n3T=rwF!F#T5LX{^X?RJ%iaGdh(L z2TFD$4Zv+d0@XI!o3Z(d2;E#tbm}b==(X>7J$j}p(>#aA47&Fjk!NYBIB>cfWgEIJ zNTAwAl~vFGE}C9!ByN3+mI(Bk`&S^X#8o5ZfZIs(?xaXpyuJ99-h*gC0@XIELRM6EH+{U;qHj#a0oTz`YyNVVhP;Dc9ca$zVEshiK)5rvR zu@bdtKrLDc;5Kq>-!A<7&Jy|eb*FuIZVM8qw$Zp`&StUa@l4?}tA|9O*TeMzj(U9? zmOB0(?G}l?tyYMbx;<62Ac1O|$rRjng|K{CDJs6`E)nR}LAc1NdWo9;uT-X8_bOGP;Wm2B4Hfahhv}T^o>Vc*Z9xLnHad@`qg52?Ekx@r zy(9v?VnumUQUmF`a2vmdmJ{ZCDPo{kZyH&;El8l+MtK=S%8Se~heh0zUJ`*`?YfnA z?0BAHPz}%t0nd5^ayUF z&e?yp_EEoz88iA&eu>+H1gdSc9@RR7SXl3b*mSy=M4%VtA5)(jM9(j`aiPX_ZC1fk zVosIb^p0{{kU+JKDmsk4t_`!E60b_h1bRLBl$Z9H1k>petS!B2a8T=!_q13&pf^RW z+k%9w!_`RGs%1<$DlWe4BN6De`E?F@ZaJTV+t^a1uD0y(K&9LA5f0^!bD(bfmXg%E zyWxK&)5-%?F=yp&+{FG`T_SD_8bROOZ9x?e)i%nMUD934Ix}7=wJla6(Cg;MjI=v5 z$hzzZD|g$UOxN~gj#oA$#?q&9TaZBQjOrBqIYSE=I#Q{z@qb%=hyR=sJ7-iCWnLw}XX$#Tl)oP8H!N0D5vjf_*&M`{m zrLihnkU+KV|0d9j>HyFV1uM;pxQ#Y7FKOkv4^~FEjV0CDZ9xLnHX0`$xvZVo)?fK$ zrc9vMp5-O!e4Ie*&5!)FS6%y6d$FUpQoTkj^;d2S5~#LO#=x0%Ff9{RkR=>>u|-B#)#|t@+q|+$pm^;+HIja6V+Th z@AogCBKpkuYWaD>Pz@quIpAX`-CSsdQTwBN6DO4OMCVyoz=1Up$goM%Apt zGyQF;-YG^!3lgZdQT>vtbHt|tPb~haGJ#%x8LiY`RkTtaA!;MI)X$<^w&RvB%VS7Y zb6aErb+`^u^Toyqzgq5`k_pu0cs!qJY*}mOt=z_g`HMu}1`90S>th__cEf^%tiyF0 zwNOk^=2&Xnk_q(UwZX93elA70(2T`mOG+P0x1%w%`s%hIfodDo`1M>W238$zxiVTN z(2HxA@aWh%3NQ0+nb_aHhUIPV7!@r@pxQ>eAnGm?KW(XOX+BLR(2Mf5>CIfo>a2t? zs3O%WUYXAFDlvxoE4Kv+RNF`?+P7M)n3B)(+x{UEfnG1-8&FLgFROFL@_yhN@vZzx zbM{?BsLGAof`qKYWvZ}J>^{2J{F@ph5$HA5+K^5R$!2xVy!OvqDRLhvZT=JxL*pyA z1qoE!X!Lq)xwv~PxB0;&nLsc9y$w^w{+!OGY(G4+TzpyeE?_hfXh8ziHp&FNx>97G zpToSTs!X8QuYWg8Idke=pfjKPpZhDtfHVyQf~v*PUSzig2~^umrp0fUi?1cp2SkpS z3H0*I*odCpd#*ON5y8scU?R|h1Ya9!8|8&RSt(Ryl>Y(x9zPO>SF0S29E9_GcoV^G z2qMse_vLFtZ6h7={~-)7z7t>n?`;?syf0rH+Sz~GK*I3iHu&1vMtb^GO0vne()B$I z3*HyiHX64-Tq!pCrLmQwzaZWVy*T^#dE_fs8{W58il(nm+bR=*79?aHuIJI^Vted; zo6i)PK(Ar*8q#bfBgF{!iJ#(^i%k1w**&Mnkiz1&Ac1NdX)=}9imyrW_H-+TNCbNI z|AV91lm6p2{@k%frc38uc?mv%|>Z{>Aju?QVDK&yNxMnww} zsJ2l?N|&W#dSExL#Wy&9IjT7A#Bv5Umr`<_M8_fsbHxUp~<-4-NJZKLXx2TO{?rX5AbpN2~WdSy>rnVu;NRsZ67?TQAug?^%& z7<*|r&2iioBv5UmJei=pBDqmdv1Oi2px40Mj}x+#`)+6TELWkB2P>#?TK?+kU+JK=7T*x zXtiz(6$4fcmk9Lwcr36#0)wjfbw zN?y`3R9a8vHj-P+)_P7^ujQF26X=ECk9xTC^RyAQvp;Hnd9S+&IhzM_g83xtH}giB)GZ;XG=SE zxTp*lv|FW@YftaW1bShAOjZ8-Jn zRLe0=CeTZM<2-1aM$A1lODh~P+ECRTZyY3U`_`i0surDU#clk){vWNy)tOptY6I_u zUif|_B{h2n@o~*q?SA-Z>CK76t|4`(%1sT|d+^BakD5JolvaC*OrRIO=Sgp0^F{Of zJX8yqGg^9|BT=iPKdmBHcfHTsugxg-m5J5nHI)hUl1DN(4`mk#4O(k|R~RjgWRQsV zG*hN*6*`fPf2(_wy+yaXEi^4TULw#7$6d5CnzM-LtJc$=Y>TJw;U1A8@$I6SvREs- z#y**!d5QLc&9tvCWdgl${Aem zeibjxKadE`R*%jeC`tPidH%6EcSX_mP*?l0P?Gn(BKAUkVphi3nnc*ziS?K zYef;^RkoE~q2CG#^pa=7^$QghqZ;|y?a$(+nJ^NUM}&};;YrmB`Fn&MFDf<#m$YYj zC==*~^LAPvj4UQ5be&>reUE=#_lzD1-fNJvVn)}Tb8CDt@v7fs+ZOufNT3($4<^%J zi;Ia_zoxUjyB#m-3`nr5I@S6r*_n;&wXukJAC%8F@{LTO7wRZfpJ7W8(Qim%(m*26 zf&|xuWOc{c#ksNl`x#=!NQz$ux@yR=nC0ffgkAnl&e98~-0+c=4SKmC9tA z;cBCes|~|~_vLHWeEW~J69Xm=x@II{&@eN=~E$rUZ|JS+u)ZX zBD!NI{~1J}1&P6y`ZWJYXLaf&ZyS{p_J<1t!~mHX znnyOPGwbE;>LQ{<-b((L=u;tqUZ`KwY$B+LNSS#$pz7c8lCq6Ns~jQJm*jD+f4sg| zRNRSr5MZM?$9tg{>hM%=X?{@=^S+?D+v9jiZ$~1HDLAEjKp|Q`=f0%Qt+FDtd8~P8 zKbb%;Tu-1qyr#0E!m8Egj)UT*bps@Lj~8caIkR_$XDud1y$v=$p}qv~gcj<4A79{S5TPSCuvTH4MNnkE9R?lp? z8$`9GVC@dQaNUq<+BZFub(AaWttW2O6u1C{IW>W@nW^5yiHIZj5 z?v-jJc=dhapEa${Y^sq;2GJ*Kk;PU>CeRDl)v3}{{xqW9q!pIGDvg#_+>vn2MXb&_ z&bbF^M9=)QEajTW1bSh90M&s!dRsg9YMtfXy6-aXhEXH&Vr<4 zsI>Ei+bDiHQH%)uy?Pr-Ku;$G5jy z@-37J^uqiy+ACADoYpJfFg%BD5pT)7Du6Psjv%VV)f6B^$bG z(=YF|#B4~A^5Kx+Y`QA90<9%)aL(PGfoAPmnO&A0M`QxMc;9W8M}gK_9ZM0hw$E6t z_}lH;r|AjmikInVjuA*>LO&}ig|v@>^f5m3g@3DExrS}4ns>^v} zf_AFZ0FkZ#2#G+iY98rnuXBh?v;44anrPJ}IwRndY3DxOqB=>0q`BKU+jG^CG2 zpjWhKdg|L7(m7cC>>imkTdQ2-n0VmRm-dXhEl6NKB-QbnG+!(8IE!A{(o!POi$5)| zlsaqaU8^%syVN|3Zd=jP@u>_861ZEMYI4+=r~S3Iv|eR#phU=C=_yk^%(W&Q*l&sU zVnI3m^ka+T*EK9iU@b+;Z!f$+(?S~Rwl+m20=@WIcyqo9%?Y_)Mycs@v_s44>m3q` zJDw85f&{ncJ{Q@;dy3XGUq{`)^=Ad|g>LloikCN9{g5Su=g^pr<+bSwRaD z{JZHZn^N8@6US5Iw4pBt=z)dzNd$UPttA?_M^Ijo;{^M;W3~Qw2I^TJ?NHEy#LbU6 zXyqrIRxJxq8w2Wg(?ZozdZ`xy5`kWOmZzh>B!bo^@)6OyPB$%phy^bL6tp0b|5}ce zG^Pkz9c3cuPCYGVW}@EtVs;A>=tc7zI$b4#swjIA5f~bz#m!387j({QK?@SB*3T~x zK_}kiA!2irw%V;HJGH6X5>&1eM14le?8AOmjCnq@k1L1pj(k$pwzScf&fcM&-jzW0 zklhv}F#aiDw0dW)`sN`bU&IKBKre24;-4X|zGO<1_S(n61H`NOBOIT~ZE^QF82_}> z;zT>G#M|Q{B}ZS0Krh}+#Z^y9U!w1!Kj^5PTYN%z?&_nW1qqCQI_tMyTg{T{q3a)7 zNd$Uv-Uppiol;>Ek4q|i?y4=IDl|F9wxs%rZVM7}&oH%jD=l+ydHvl2z?HepT=9hFa&}OY4W82C8U5f`4fv{;6-DQd`?Zrzmyw@sSAhN@IGJ%vIAKpW`@x z)*)CcJ*uI8`(RPpm4X)ceKDHpjT2T`I}_Ydzv1;oK?1$5pLv=5Au61-C9afy-9_9b z0xd{j{F_Wm*XGfB*B+$Dd+d}5^opGEGMVeS-}Ws?ZQQDmRa;Pcpq}gEJ_RjEVEoev zpz#%Z-`jEe*P5XcfnMd>zfA6HkDzt0{M1I_h$r?BBSz`@f3hfOK?0+h;-mg%`?SM} zdf&N)EJ&c&-S98TeA?sHwH)V#s_nLyJ({R*sFcTo79=qKY3wsHNXuGcr=}d{QHJA9 zPrIQ@Q7)o8=K0@W#0SxD#Up^hw}Z5@-n+CQ-ANc0BryJ|r}nF-UCT2>%pT(03t$lF z<*M%C>Px~_)YIxx&yb)u!e3{qHQag5|a#f$>i@ z=t}0)Mo*#hm+usn2=w}G_~qnyucodYpv4xa)3%)o(f2p?QPF|~MzhIOx9eql)R*@9 zn(S#L0=)ujUZ!2F;jSwGgK9ps{~pm%pD_H3f)*q&{%QAFneFzv*9Pi)Htd!N^zu%B zIoZ}If_B$%oTtmO*M2#0kUnzIP6aJUVEohDIZKQ^?UOkD(2h`vK(9CFFVQI*5i}>{ z5dacsK?0-MWE$GxuC3$wL_MW$J_{1))qBn*8ihyDoG>r7ktz3WTf^TI^}$67SkQvR zs)8?*zg>)QoL^%ySv%?We`0rAw08+q!}O=*U;hrYUO!Ws&eqPK{O(I2Rih~5xQ>_+ zWSwd)Lv6gMy*erXg+%qv#QYXkxZiz=u$Hb`z`E~vMxrBVB#@u#$Yi9EL4>vZB>r{1 z8>TZ)EVtOwwtk}8drp4~-*?%OGS-2)^8^}g@HMwV6_to+6XI>@*KN1u;?)GTd%&zf z{#5n%m$oWdt^^u$4*vIIKLt@efYLmhe?1 z0=;g2oEb>H4?PDwlIi^EnK|={vg)EGD!o7479^-w3fwg$%(ed``|;!EOnQjAVwjIa zpjZBDGXu93ZtB`SpiDn$ewns`+TxGm)bF}2NKmg7Nd1+o=5lb>b>^oD9n?^(Ng~jz z*}<7q3n1L;Jf(7So(<;g%{r=+)_hUWg2bAT%Yo(Ehm-coCsgRoV$54A4^$7g*e?<2 zHFM$2z~_+>lu6DbnU8-AGDn>opdM{-P(ce4)GGy2e?`A8N7TFm+09#y#i`4tHIWGP z`fd2kK#Frx+jz#3c1jNOj;C>|--}QMEl5xwLnjYKP@Hpr^-d`mP=eyTcR+p%66m!i zWM&|putSl|BbjfF3kFmr;+{`_3tEt%UWp=@KNa^^WriL!UvD%-`LbyQ<$kz(0E}(^ z_t0$hNsZz-zqRb3x%r$S%98a?!muELu}LfLdv};`G(M@kebC3zhC!g$=GVCc$Mk7v zeYKI}ylS^i=8eClDs6K0Q_+Hi9JPxkEjKqDlTlsqYn%TNMg;SnX#aurZaByJpH<`8Nx9CI^O>)t4p4m-98{1%FFNah zdeI0{?l=Z}6fbE0xynG*(tE#x79=n>Y0c~BLjn8$h*O_bZ7dP!b)iY_KzT zJvhQK$}kA*SD5DbKl`3P)sKW>L4vQ19)r%D-1<5ox#mfw*84t=Hjps9xDD=0w!G%P zWd6g~0e!3|m7VXLgkiz^Vjn{zndI96ztO2A)jPCyv|$kF#cgnpLD@Cb#+s(b0@6>+ zs6IZ`#z7bsB;?*FeD02b#JIBRuQ%2I5JpeLZE#=W%rMom9SQIXF0b|vRvd(3!TVy5 zK}zbJjR9r%hNuavd?W(BxDD=0oGRy~y&D70QtuPA+({S~B(RU6ovghl1VoIazNDMU z(S|{w7q`KEiL?IIp$QWLK2z^gyNi=BEJ$FFK`Q4GC7>tuB}FzIRPbKt#sAKI$&Lm* z0+{ft5)giLfI48clQ1kuU>`%@Bg1+BWa>+7!Hpel7zBE88{C&nNansIw90w^G*9Bx z6{?dkEJ$FFK`}V~LS4)DM78ClFq#{gD@;eU>`%8 z{`uc+W5(~c)X9*j=G~sjF@p%6CPyv*JIChpNFM12WqxI|(cEA+%?;4v{3OnK0$&@^ zOxe+Qp4v>UhbZGH&VM8fFaCFqN1ikMmoO}NU%ob?nKJ0o-?nABkgB{c+|SVl5{4JI z!LixwphNv=nPRI~>!fm;dIq%MefipmW||3S+-;kB$wM8{uJwNi!z*G&reu!CT&+3I z&sExO^E&ULZlaz6EqGtPHsar8y8CsxZO-VjYQyX5e+a{CdcRD`9L>(`XmhC*wq?{a zJffZfEqGtPHsYT~mRTp-CQ#HCS>f{^!tgrQI8!pmrgJyBt-?fGcj_5-QqO=Eyf0rH z(QGpPJUi0nQ@^9Cr1`4+NElve%V#3hIovhU-_>e}tvmG$KGZXy1@Ft(M*LIenOe!# zfTH#)MeUD-;YIZzNGXb-l{k*`(*rBnhEUJoM?C{t@VX~r|PXKKiOL+?6%zal0bh!w^G1QK93nV=K0?_9-XSekAz`Cg0K1C zojpUvq2ufaB8MnBcaLzifrR13ZE!q3T+ea-BVkzZzI<&&GxZFcW9?NRr79b8_j9y? zgyEI0e11oqJLd*IwvqP8GpS0`{QanMq}zh`jZ{+BC{Z7#6%Q#y_ntDUIx%6U(XvFQ|?-3n!q-2=p5HG@m2RomnqPpaltxW~$LxCxd-8#d(PjU+8If z-wVC=9na^8bLR<1s{%9FClfJ@2(%!9(M+@LgDJKa6z5}>9h3<4dbKg1BhH;?vcA}w zV(UVLLIhfn!1$+KDZjO{y`nf@u%n4YpjXcx`5YRVb8c{LSsPn-B8nt6QP6?}Ml-Eu zMEE3qrnj?ajJE{|^rD*E$?mzq&gQEQ6{fktwYfY$cUzD^6_57z9o=G&rg3}q4+(0n z)^q4yfsT1NDuw*-tet5?IR>kJwb}oE^NXcP&O`@cSdfr=hWNn;?e%F!zm{h7crWzY z(O^z8>u~w6@w;})%7gYxq#FD~ssUP%kb8zo@jLBF$5R!HXTSdtM$Ge_c&_fufLPRY zr~T2BRAowLCt+Cdz8L?s^X1_h`y!grC(w)@?}c8^O3g`T9gZqc)Au-QUTg1g(nI}) z;v6kV$UQ@W%yaCXE)C#E!if3F`R4E{0F4t_hx0qJ(4Lv%{Obvo&Npyd@V*%TR28`H zIQvtY(c32ZNCbNQ`uFT)*5RDlpY=M8vu~ymfNhQs<<`0_NMJNmJ+h6F_MJ4N9~o|v z2=w}TaW=04P_-UjQEb<$q5Ww{M|JJXFA7?a!1$-p>ryW}&**#h*)I|3<-d0}t?))r zg(lVjYNi&l@22;8($5DKv><`;PiGHww%NYWjQ;ubCK7>OdB)FXy`8FAu@0v%vDvZ` z5w)_3f)*q&nrYk~_*YUvirSYw^V6uyeJ}KC+I4m^>u~goQyXI9+ob2zGo0w^Z9xkX z82>b82rsC8j^1slL3%qW&ENBSFy{H+-Rk*|gkeDfqnW;WQhsefqk&3On{(|B38RXYIUfnKgPJ=d5z;LaKQE$SJb#(ZDxb6AkT_@{B=fVFndwjEX9$zMoycB~va2=wAL zJ-2%Ps>LSz9#ZIEt0p?Fz->VS7kY^P}R6 zweVVr)bBbJx)fIE^p)jU>*KT49oICzrmQfYpU;_4!?vdUVy$|^?`wCoFTV`c9^0ie zk$J20nh9Pjq`7nJ)`w^|p~Gx~_6HZBRmJxT%^>K$&TE>B(5_t8GCo_=X!&a|)Vfwk zG}a)g@?mLGo(@p27D#!b&THx=D8Gb900rll(Po!jsLjfosG^sv?{Z`+@V~o>>kq1E z9m?(0lFlYL2*ZK|UmH0HCX;9Tidv!73F6to*#8iQ*Yz_8cw9o+ZTzX~hx%#RN)Hji z6Gy0M!Ta*Hk+nd7kHDhZ{U@p7<-@-JAq=lrae&7qv>MKHgSLzOv}zMiiq_luPz6l4 z1@G(X;ZmIIgI!;HYrz*h^x)F1B?7(jZa=`|68hD7-Js+fZ*3|Od5J&^60ROD#koG1 zGh=2gG_t&I^H3xLy&`5DaAYTp^5SvHq*Y(-^RkxF_x~M8%7oj31ova^e1=K&FWb{o zu0diKABjM(a_tZBxPn103lgp#F2%V%m?upod+VohdXt->5`kX%ukCl_ z0nGi2SNjU2uVn8>#F^`%3R;kG^<`W1qnH7r;Yh&KT(@v@Mf%|4TC^0ZiB|~l*`F& z*!F$2UtgFYc3*Z9h6M?XP1+Uz@P&QRvsBUhQ(s3L27z8}>UQH9J!Rx_oG&NhJ`wGR zKnoHWo79Vj+_aaw>Y@Lsw3Z0;nqQzBYXGzpo96}2FL-$Zyt7t(2W7A|ZizD`y zqs!`z4yzJ@UgzFNvj*U*Qc&X6ar+&L+APHs6)i|$Y*H<~#@p<%dqecDQ+*@?y|P}3 zW(~l#dpT^}HoJNvL_a^!hpGa*El6ObnM|jqPqA+drF@^$Unm>bO`zAfpQBj=piEhw z8|0rc#hy$=B_hy*1V$Rw7Wpa6-thPUy;uA}i9oN9QPHdcxUvny)`Zy~Q`G*_`=Ejr zBrrB9FC*rqZ6sv~o%d=i5$H8AcQk7NDWTc;J$Pf@OWQ_@!O>qr6|^9Mu}Np8q@83R zR%)>}Pfes#b=_}dj7R==_uKhL!muDAM{O6s@%D{P5`_PoSVtR381cw$aNK0Q!ZG+G zVOa3Kd~HM;^$f`lRZXy}hWA1*{&$W+rv~ug?~(S3qQ;Hw%M#1#N6xDffnJ|C?b100 zojYIphBmfG6Y&obXh8yFllCsPs$jphD@6Z`^mZiBi~pTt(77`#OI!u}$O9pIep2qx zf&@mI$@EJ(4}0LK4titnARxYZVUAyw2U+V4Mv;l*um)H+wg!&+HwpNPoiA`A=Om#^J%KBRnA+lccXdV`Lw z9c>_CcySvXwa(S>F1}T5Rfvdj5rzft%h&EWpXOD>_BF1o9(7A~w1I@-#cgoZI#L6t%SG^&?^Q2K?_Fwa(S>wU5pxb&BhtmydQ5h6V47(QGnJ z{bga&%G3dRLt68~d!ZNqJ4dZ^HGI|Og-Pit&V$xD3B!T}#wOM4D_uJ20!8gfidwuE zdhx#-(e+=#uplAF`QgfC4h2%w&ipCNg7-o%IclTNc2BBDM1}Y;3tEtndxjy(*`zm= zjXSW1w?v@Vme*Zbp{Ke7tlTY4&1!p0M8!_t7PKHCj{t5z%WBI=Il_UZWdglkZQRf5 zAkB(-mypL#gKf8|jq1+|SkQumJkn1)ahNU7g+$$cJ)cCN7iJrqOxbQ$H}@;MSoSAD zE|7boaX)hp_vl!0^P-kRM6LrPRJ7oI`P%6DXe|05zj;B;lcM-Pef~ojUVJC+i8{>U zxk1|TrObbxOBJmP^>Yx01@Ft(Mn6XDQF9BJ<7uRyp>^y35QZ03&*I!cmj+-vRKT43 zcMm;x`_@!R$8Ev;^0m>A(chzQCiC8evU<}i>VF8sYp|sp=MK`7$QnSQnVHP1N0!xp zqaF?|cwfFY`Z3zaa_mt+4jN?~U+VK8!tmNwxE$vWy3QC$Jn|@@CJ~E>KnvcNW7FLy zX6e2!pfQb0k~^6s0==GmD$BWpv^%pHeUG6F_XRv1(LujKJset);A^AjqjLeKj|=$e z*Z}>);)BYMgyGdGsVwIX(p-(#4LY757f^xv#Fy(2Drmv`^0m?P(OC3r&VWob%GhFQ z{2#*bTC%t-=MGZT^0;J~pJ%`}>Jzt8pNJN`FJBuyA7vqB$mJhI{Z;QVVU`~W!|U>h zvZVV(&|IB+pL*qU`BR3F&KW{z!Ta*Hq2kdv@j{Y$`iR}yyuTgmgPgxdZHC+y?7#&bdLc zcSp=a=ME9&);bBpf&|7t)feq-H`|}4icFdM@fzg!d!ZMp^_-`l;#?nme2mToEPqne zdDe$l`@UO{kb8#lX*QZ4(`=%1sAGLlB8*-lto9tv(l2A>r}nH{lKR z!%K_v`rvm0y-2O+JbfC~vJTf|;Y4$r{ULgwbA9l;1qqCQs(L!Ny}50{_WH6NX?cC{ zJAq!L);r?dxjtAnXQ+7tjbxs;HmPVq0;8E`cS=_CmAZrU*$;Q}`r!9_p%PbX)UJ{gehw>Z7%WXJm8cLKdgt>-*_svgPfgLC#B z4)}%Q{AHEKygvBdf&@l0twCye1A=xY>IaTB;`PDr1bWdb0L5kmRp8?||2DW_KpBej z-Mzwieek;l35w^|rAJkpzgWn1CB9)tW z*SOU4iCZ#Rk}i4ZuW3aQEl6Pe(|Yanx8_?}%IHr%I(7qozZZIu%FX+6T)P4F7SGMG z$z}Czqf}n)`))x3qnT=we@r#Mp4m|E`>$g+;CBMONag0;H7@o1;FMG557aZvm{gqi z_IA5Tg~C#?ez_-($eXUZVM6^%_h^P zV}s0DX|MjVJ-Zbo(2G=V-d*ET&l|4jZO%wNL;iD)-GJXMNXR_{tq+=MeNcC;4}K@m zi$=@5yT+xS)B2#9)(3Uh`rvm95*W>n^}zsIAJkpzgWn1Ca_z23aqg~B`;H9ALp?*~ zRgHLkkoVmrcJZk^x%!sl zd0P;~cmBgx+uwp|*M1tHWVn2A>_qO9s z^*Xa=nf0xwc^f^6Tb?^ss=q&p`1HN|a?|m5a;i~rMX&nPt}T5X{RPp2g!~t*IIW?1 z-PuIFP>H+}fnJA-rm=FJiB$#pDH$Hy$~^fYX=Dv9n9+g+KX2|G>(Q~1mcHIQwOR)f z)H+krSQi8ZrL@Z7M>V2u)3X#vb$v@YuKA7g2W28GkV>RD{jqMu_>KQ zk(>H2BrYorm3*J#^qA>K&1gY_&(iBXr%6iB9{j0(&#NkDMvl_+edsR{=*9ZNvLj(s zy_Ksi4Dtz7b`2P%FJBpMMhgLpEffApdgZYj&Y5%smXtTIu0?e9vTnZ5PV0X1xBL4xYs z(Q{Ql<=q!gYGd=gH+9tQLuG$wRZ(>LN^J9}4z21E> zTe(KD|6Wh(OOSZKA+Ne`Pq;qz%`$(qAaVA+YF!gjk9s2hRDPB7sdFO2^>x?h1t5W5 z-!2AHZlX1XcBB#ETdsf_eY=T%clV>ZXhFh%Z=khP64%Y-Hafkmpf*W1>kE>P+mJvn zTccpw1F57myvI-bqE{8v(z;o{6??*l79{9&RobPe(iuG5MghOe@%ejP4-Zm968v=K>r5MZwd>Y^vcvpo708pS6Cjb}Zj@ShQ%*hWhLU!) zAd#zeBihSZo7Rf>Q@xqjLapnQU7wV3a{vQq-YCw;0SpS!5NX1)?T zZ<_s)K(F})!e|dKZ(H3Hv-VY*9OrlMF`BU8?;vXWOb&?76 z;%mwXOBrn8Pqq4wk!pc#9mRu+`vcH|M5eKkd{!z|{^B+=6Y(WqM^P`&K8ZlD18*ZK zx4k%x&bW<7dE?Ym=i7?Z$<+gX^rCaG9QT!p_#yGC`DMD@ngEZTwQ^&|qlCg*EGefvL@&B1Lf|2L6p z0h&xbi#9VSs4c9CeU?!MSWatL?H06Ca-zeFuS?#z<{~CuOjO&g%c@Oy6luo$9xCd* zFVk`x7yn6A6Md@KH>GK2Mhg;!Dz~6#_nD(@I@RiJqFQ5KZu`=J=@Nln@~7JQAyNIe z&?npZ!p+QRL4v;@e-HkIw07tE=H2O=BY|G{i_nb4NsRgNw_-2;t$zI7z7vjLm)kIY zQ6%`9{{qfWWwg=5^{J3By!cMvF7~38GX4v?2$Mk=7Q8QC^S}S^Z6INIaT}$pu5z`3 zPYGJ^zI;tj&;QZ}TKJmpYy4L3Hh!jGH+D-G|1Pd?ZdiUK=qYivv7Lw-7d!bc>KWnq z3mOD}Lc@#y{^x!El)uNA)b!53x#7hGd-11|i5yQ8)jDf6bBBK-&1gZQ%B|)!n@CS* zdh>Jje-Y^Q?V^)#{spU!OjMU|7-N~gB+`r)Bsk`uM|zNo!)^RBB~fj1DaP`CMLLN< zuYbxkx6aj}7-bxaI zUZfZ}_SQPbC21#&P?sJUrc}vx%y>IH?!{ib58#)hIay->V(P6S>Xa+1mG%YG(fr(P zL4sCDt#pDqX$}0%AI*$WFK%6~{Pn7xKN9G*CZs9#q7|*RI`TIUY8ayyY`0SJ+c?FB zUV|-7Y0p7L+7pnI<~K-`vG-Nau0N*anDIObElBjWJ4TjNn~XnI_+Ne1VTX22s$rYhIFszx)FoOJRq5@`>1QP<3PtqfZc z;g1$123ta{iw4wkM3u>;Bz9HX&U&F#K3UO@1bSU;5=y^QE$WT!?Ja*QTQl=oj{lfnMJ( z2Gdhwp&g+7sg8s;QfrkhN+Z~_H0N|%kmzd#84oG=C(}E3CGa z`VuRtEIbqbc%h1V&%3UAJt!}|4crzaTys%+Px1Gd^1hOq=dQncY~@3Lv>YmGrBRrb zM#i2r4szegWZJjMTkR{tR9pGK1JHs5&%6g)>d~HhZe#LK1=Sn{!_*8*S4aeUaeer+ z?}KP&#NXq{VsCZpsWA0zgT=MbtIiY)#dk32mY&pCA<_G3TD9hvcB&q9Jpe68Jo!|Y z6t?D(%CBk-%@#`E-izi_NZg%jSBib?ucq#uXhsVXbaEPz4XLl>?=dm=eI;RQclE+x zi$tIoem^=9x}~OkJl9`c8ZtrpRCIC~y<(65}AekUYOR1Q(vR~e;_C>=`g z54Q!0Hg(I;xaJz1vBDi9j#jNjV|8scRMa z_KaN0rRpQ~8d_}yElA)h2j!~G%%$i={32umz53dV({tM-rT%_iyPH|Mfl?}Kto|Zb z4FxSo;3@}I-@6y4JoOr`=PO@NBG8K~woJ8#rF0MAHoDZDsFWHuM7OWBC}=?fS0Jb+ z`J@R-c)l3j5?oUv&?{%fN)+#*w5rcf;?2QZ6pzaNb?d3h3R;lB)d8yS6SP(N_ewv# z>b>d`fnI!lKdvDuklaSmQkRv`m~ML5*oq2TkigXedOMH0s@Qr&>s7YYmI(BsbFle6 zNJlL9}Cp`>W_p zdDUtc8tawT7E{oI1Zw^CT(!=p4*nXd?<-$QBG8NVeH!IZo<6q`n6HBRwpm^M_iII| zc7of2gska@X%*Cj&^o$(Rw;=UHKe9tx|Pb&QW5x4anrx^4>+sH#&A zWY2o4DP0NOdww~IK(For4e0OTPdnVWjiY^{)HQ9h=-H3?(tb|21qsy1sphgZO09D_ zi+*KZ35h_jO1m4;PV8EAJ}0+PWP2yIK#k`jgD9e)1qsy1sdmE4PHLrZPsP%=z7m06 zn~pZ7QGPW#pOfFUzoqt67wq{>+zaZ#`SI3+?l6_N<_is{va-h-8? zPvkc0XCA2D+O$EONGw2me%ux$Q1RyKlmpfN$?L@h@4^y+UbIS0XJD13^9{HS-)=+I zhHb`+0(%S6uj{rTfr>ZH2Yb+2_dCW5Yb$SwKrhNnpjol6YlU|}#aOk|n6~24q5||J zx-Cec8ckI^io~kx#xxU+Klw@odi9hqFR6VPoz0@Y}K zc1No1cK8UN*D`@#`;IrGES>_itC4@J8JF+2Yzkz*}x;XnRt-{UnbdHJJf&{A3q~aVIrJmU^ODp(yIf+0o zR*iDC&P;2N{5`e=$EyYZ_0SHUDW{+X2~>(nzp56me%YQ$^PO2iBG8M~XjZoG$8j4W zQSoYAqNjcPx(W(fkYE+ht!)2a1bUr5)!aci+c@4L-l3j%-%vq83le;7sJN5~=p@Gc zZ-n8+cj9Yj8^8U~hGD_`^0lGjQqS;18%P*l+y-CEZS-(`55t1@b+_@q5r!AH z!Pm|@JT_J7@5~!Ebu9O#} z)aSJZT9)oCFA?Y!E212#6lWxvOfRm*srP@sVYyka0-ZkOwjhCeDXl1WAEmBQ{)eyg zfQlmN-u__3oBQ>byrT+a&5`n6;{VnKSsw}PLCvyDNZzKRuzU3B+!|p8UEIH;d914WqvV($GbAk z)}uy{K-ZAg`W@AZ?=aZ&V$szk0#y~qhmfD_OTS-{viLs(Q~-$YW?6 zF!O-O5z#L#=d7(m?GiPDgzR{YYLkj6Hmp zxsLA2E4OfC7(oIZHjQR{?|V!$uq%I5(p4f*CAP=T74EjkQERS&9h%geH>y#eMwqD) zB+zZsXnMIC*`~?8`QnLnBmz}WYq-;Q5k{*=h#cj-7O}>5@jSVU2aP{hBS^^p)yJZX znP1^}{&7e>i9nT|rcOX^w{UW!L=OG;aV#M+koX#Wy zRbm|$(eiKTDc;XJ>~F^69ftBv^#U12kU%Gls=}gLuxAU0@+ZaIB?48VjynIC2%1q+ zQF=>_G<|L<=0f`C;!&k;_kqTDeYem!T{VIP zI$<8U^4<2Y-{aq$d)y~(6THz5UT0NF>+`LkTWxVZgY}au!!w3@Sgwc%2KO9)e z`TdPsif2j$s>HV6&Sq4FBywCR9K|XG^fi`lyV>lK*&;}w6QF7OxNsWfA;yyB#HX< zVYJRq!&2O?rp}nSevL$+Dl1H^(xo#|l`Q4xQ>!LVKUv?{Z{A9}@2U|b&RRAQXl7fQyydIn#*VgWi^5XI$_k3(Kh3yC*?M-p1fQlP!&@&jQZHT zt+OqhD$|x6O+@UBxNV5|QzlSFwe-}&@G!Y{6i>CEn$A4S z{-ELN)@2MMNT3r&&cv5ad_zH#p+WF+i9i+IEoiNxdQ?d+a_sorgTKAE)Ud`YgS;g* zf&@BYG*kNJ9{k&_C59jGmP-Vx{z;Ccr?abxW(pQL&h_EFUX3uctG%3I1POG)sFg9T z4{z{dsNvj&l@fufWv|3rQ8lVP7dh6q>(4!|h8o<5h^tnOAc0O8)j-be&m&tkGnCA| zTp~~7N%`qkLR{A)lPwotA$*2(|&l(lNETD&M<-mI$`AdgvayNwX3uDC6-78s)o#orh7?cs%;SW_I|8CzqzP0 z`!r<eHQRkKDBbR2c5no8stzPJlNm#~+Gxh!WGK?0pH>i=ljmG2z8 zhn;s_CK0IW;U7*FNge@~Hp8Y7ZTXIKH`(+}>C_*kMvy=!jNYB&+wy9+ZnBUwGJ&eV zE@9NG??vl0h~sGUxEY_Al8g5nwVYuD33S4!g`676?`*f@<1a0i2vlu+6HJwEzSMRX zIc84ae0CQHe&WYch7lyt38Oh{^3%K}GadND&FK<>s>79<(6d%c^{FC4f*5zG4u965;71*Ssc8QK0bdiJgD#h>2=KRybb=32tMvy=! zjOt$RmgbxKb6!ESN+M9ztdT;M9D#K2747!fPc_``W;5Pn=NkHzs1YR42_t7BCKuP= zXvPhVR!9V@d?x!+Z(kEywOizP^5qr_af{(I@~>tXK?0pH@E7I;)=G+^>Tb+E7oCuUtZ|(I))J>&O@B>A6VE}LFlSzKN8bJb`FuLzff21@iG>|vBwpk)jCB_YU zcZ#I7WrgokJ!pfHvvwf$J#V462{nR*?2*NHT%)js19|(wnG%7jy)GrFHzSgsA0o%< zYP+;WTXZsdFEFoG@b7FS=$FZ}M%>AlU#TA1$#}HQcZLxp&~-Y%f^y$BjVC}ubIIyzSQeSb&evjrNC5hTzF(`e?ON>Y};i8ZdH5ynWMYE zG?pyF)O(hgFGJ^cP8l8&YaCep8+kHn1POG)X#K(IrQt$wg?ikN7g^afz|p-MARpVK$YLlO7uhvrw9(SE*(nFgvc>%)oS)OE!z0%_m2!CNT3r&@8^|Q zvHi|1jFZd1l?YVrSn5gR4T4P>hUo&$IU+@h1|2s?E+#$#r%&Em|mY1fFls`;XsYX!Y|E!w3@SgwgyAlbZ8mgSHwT z$3Bw?REcVM@~utz$A}!^OJexhWwQ(s$xrDUR3k{B6GrtV%VPM6^|K6}2gn4fgeSMx z#nqJ9OXTP@pd&xfpogK=(I@8WHd_P%rI2{kbWg<1POG)=-xiI6Hj>QW%x7XxkR9<%$6t`O;yp_3y?Oo3*Ypn zpkc=KXAC1qpc6)YMVq?vX*-G;CY*jK5vZbBDn+Z(Bx+3P{k&3lUO3={J}3L3nC0@{ z2omUo(Q|NGSH8|*(EAT~ArYvmx4pR-En=dP-;`sO(v3eanW8T-^aWK-su3j638NZ% zB2E`i(d++1plU@>3#xl9K((AA$D-_R{94_Y*&gf#_1&ovB+v zb(P=7iDv zpf$&@2%Ade5Zm%`ytC%8MR31j+vbF+$MGw|rV=^Cwk1bt`c$Kb1pB?T=CDO@zvzTf zT_1fP>obD=ieI*zT^oU_hv!;Q>-d>9$NUG~_|7p4wPExt!3YxQglRP0Zgu0{XBKLA zK z9&Kv51~aF0<=5vLm40;9Vgw1m9niG$yWvNDxKqpM2Nl0nOmq$!yk0;6msy^0gM&Cyr z8to>I<8E+EK6}>&meuwN!w3@SgwgwX;}(4D?2RnF!*hv1mCmcFm`{dWFOj29a8tha z<0)2b)eBlDPK_XeP8f|itlx~sRzAr}Eq^Q#sG^yBX?$WsnpZ~T_V;33S4!S7vk|zj^f~`}*RMM4)O^>0qjV^r3sN$WbKIlV@%%#255@N^f3j1POG) zs2*;NCx5r65cj$z6R4`)u`#(_{?x7(?Onf16?yLUm3XVlFKHbtHG%{>VH(XNZ55ul ztP-ES?vX^G%EMkqbxUG2tH^P+aS?vGfd{`+{~5yw66l0crPJR<_;nu-p1kCVM4+m` zCM~rS1L&<@XPZAO-1quxi9ppP2VZJmHKym3 z$nk3MMK&cpm@ComsDe(7Ac0O8xgT|}u;Tebc;3vH5`ik=)Gk^WM7;na$2ijtcA!!e zzZm|8X8%?rNT3r&UU=^vtX`og{_~PdplZpX2K0^-LNgtS_rZ+Bxy(1X6)#`>JykBN z5hTzF(`bHMI+yKj(Tcx4@lGO8HO$}^AX@(SSBo5j_l{;x!(;fw)gP#;SdAcoP8h9_ zXP3&-cE@mk_xBQks>79PnP;@>|48Im*(8!Jn$n)XfAE3cCe#QLvPahJ?+6wc-=3$n z{U{NrYSySS)nA3v_zdAQ6ewSTZ5h*%7kmGSyjL}X1Ug}Ke1iK9+@qI1Ug|ft9;{^%8oZ3`Gd7zB?47$6HC%;I-*BI z_&)o#Z%{5|#PYM7zcGv;A$w&17lA4<`nr7m2zq{q9AAQqDX+f=8y}oDuQN*3`<6M0 z(fyKVR?Xd`x>DpzuyL2662}M<=xb>-DKlJ@))PXFxpG`20#*6P*wI`7&1kMTF(>($ z%n3@7cA>^2!(BK=kU%F)qj{G4yHa>furX{>C5b@QCg-9w8$!5sO{6Hp8RcTFVB=v; zC5{m!WRI*zo^#3!Ly$3Hv`nB%^cQS=)6}~5*~pMm?9#F(#P8ijArI%zU z@&*~Jhsy-2hRmr%Bgw-|3kC=Wvhyd-Ues@FEH}l4V+09w!Zez5^8?t>z(8a8w2Bge zs-xjGX-~)1vT6hgbiy>6d`}j!OS$}wVf&mV0##yWf!Qyb&>N)4Q7I^k z6`9n~=s&tV#|RSWgwdSjP4_X+w_e60mC8v3sw$3eMBjNJ)x(K9L(Q=_S)Lztj1P}F zaf~2=P8eP1Yi_XtIkk;HkCl@MROtu%Qw6JzMs0~4x>Y~etHesif-}l;j39wdm`3yT z;1Bk(S|ww%i!y<#UB?*Ba3khx6giSjCHSGnC5+cYoM_&2HG%{>Vf1|*D8YAVOBhQ> z$^@$3UI?HXNPp8GP9jIdxypS1P>u0xEk}+KB+v+_lOuNy8mci{S{Uw&uV z9z)%c4jdy$pc6*tD$bY3%-dt|OO^>#ojw&x?qCCx<>~x(7Uxej>4pzQ95_ahKqriR zhP|BsSt!Hs*PJpEfhy6b)_Yc6npssGhg**zzU1II!&9lMjK*R(I{q!Ac0PpM)Q4b2*0>9)R5-pBoU|*w0>|^ zYaR02PhotyXBES{Z;l)zNT3r&^QsMK%7@2RH@vGU6Q~k%7VTJCk?Q)yaeVS`#vAt3 z80`L`vDFYk0-Z4G=e!=yb3%XU&rWre2vmt#mF|voqPjkjqjFRPADFp8-@KnA#|RSW zglRN2wnXwOi?{2~{#HgJP&Fk$%oSgpW`!0xdc{ZaUV%OJ%bJ(r7(oJ^FnZh0AHkbn zjMU#VI7tMmgtxfYr2y3qiX81aMewM;Ioad3(Rdb!AR&8X%Z5krx)UyCADJc-sLBj! z9zhc|wgwehIKRJ-FsYDL3 zZOQS>nxnb(Q`sW8U$JfTwWxh%&G9S3rV=^Cwk5|yBF;Vc^oy|Outjjc=!9uB{}+L( z5^bBCT{TONh?x<5S;ybC8^$@&%0Oxa33S4!r+NcjAN|H^@8)xq2vpU`CuVQWW9{3X z6dTD$cl1?07jonnK?0pHjpq4}W_+5RP6@x|BoU~hb)?L5+*x}2X8ASanS0kOr|vt^ zD#vOB33S3VnlrN^dH>}rl_3|(NCc{AT@Lf8mxi9=`uKBF1b=?!u9E$s495r(=!6mB z7Qx+;pDI~F4ibSXF|x*Ia#f#B2Y!|Jv0_TjL#6q zv9?+eFEnom+ttdEV+09w!e||=V!=G1{a}`Z@yI$;{kiM=|${`NxF;|a|)4g{*YJ_w~ztPQMh^pzd`_&+(j*wkST93x1e z6Gn5rO!ehGeRi{(^nDFr$Ed1Rd7(L9*z7Y-^r_Sc66l0c4gG;?Ji6Hz7F0(Z zhf1L8Vnkp-=p8?r2TQa?_uMSSzq=IU>w=xA?W0DJKqrjc+VZ8jW3^(uNIscB)%hx% zMzV@|?nI8UEA9B|T`s)+fU+DTNT3r&@6Jc`1ocJ^KsPiGlg8}T}_Q3fle5$x9vHQ1#IX1>@k@@m8h$w_3r4m zC2~A^+nBMcfjoG8MUD|9&m8JT`rc@U$a@1V%Lg~IMm``5q!ZCsb zI$`vw^pBMunL)g^eIwX(@fC1QZ9u}3j(UbV*-K?0pHnop+R zG-c10+Qw&J6p28U@XM-R52Jd9%9P{!#gj@?x7x;AQx%R8B+vy!A9hhVMvy=!jNYSC3Nm9$SL1Kjv=V`;$ZnOWW+KG& zv6jejet;Jn`$tVc*EEG%_rh=57)bbBrK?P8iiX-|fVjD%Fk7{bT}FdtK_1rypcpVfFjfsm%LECFA-KKaLS3 z&cFp7 zv#&GC85bwW1gdr&^ES^%HhsRxk<{x58``R*adb^zx}www66l1{ESq_bu=~H2GB)hx zBN3>Io#96#Ah@-b^UCtOtZu!6#(j~E=s45}66l0crBj}Jtk`UOV^o}vM4+nPc7^7X z(bD{I;y4l~P$hYr&xWQEjW|Y-Kqrj+T_V;t`C@4L&08W+HDr#CMkx4E9hrC<)bJ|G z2N%3)xbwn`{9QGI1o~Rk(mz^+&yT-s81b@^M4+mNe`9JbdDBWnBFAOdvV3!%y@ns9 z8*z*vfle4zI@Kx58#?VZI24r$R0VbkqE?@$$>In<`n@tAtXpbOV!i2YTa6%rP8dB6 zE?4Fw9xO9VGI&V@s>B%8kF^?5jh8qMzrdPYb9j>BbsH}_4mE-VI$=};xvBGk;BzuXMbO}sfqkU%GlR(-o#kH>n{H3UEMk_c3l*%D6IM^&ng5;;2k z*?{-4FJgF|J;zocLAQ6^CJeSHMY0PbX({X(=X3%YpnlphQ9H6D6#j39wd82Qxs zJ@~NnIr`=HjU)n9qAKvm#p0$0H_ffrOb`Cg!Mgf7RMUYGB+v@tDtpnouWAGdbi!!v&u0y}OY?o%f98`3R8<@wMZEwY z_n$r`j-#`k7hgGPN!GCZUeq_CMvy=!jOs=6c=2k_mt=|mBY~=Mwyaw{WKT9h`cg^Dlz+FkzCKLIaYmc$e$mM z)gHF@;ut{!oiG~h^tvHW_3ET8R6-_DCH$aLIIE>xj*$seO`ji0Z(hZyc1h%jyWq+1I80Ig?CwRY>Z%bW&I@# z;@<9Bp#lF{$Az7r?8PyH1Ug}~^2vkxd|eM0=BSqmR0)?Vc18`V;T1XbQFXXxTol`0 z+S}}3*&_cXRFCY=Yd1dVP86HbQzp>;+UpWVy)tfezC;espEdaCsIhGOL2qiksu3j6 z*P?!$4X)hJc?`Qw_g^GX^|@sT)nC=8F*zcKQ-P{{U%RC&D7P2K2omUo(I}zb)p&*Z zOIXd9o)Uqo@9Tr8{>qcwRFUJAgEQBc-plUHX-H2THG%~CT2vua)0yuqzL$-jAQPyf z^;l`XC2y+15;us(QWv>QVHi`@6_7 zY;rC>w%iA{=aVlK?qM`rh{&=0+eub(XmMV@yf4QH66l1H zb2sP&tKwdqSJ2A@s)BbnB4=Ai--*c4zU?N~rn(b%{p!atf&@BYG*6LnD|=R~EdR~J zS0YfA+NU9T0Dbi$}sZT~%`#zt3uX0}YA zO02!(GdYZ&AHw&!v2DGwEVwq${zswJ<<$rhvPU*#{d%P-5jDQb1gb>c+R5c%)RQc7 zRQ>LxxV*48&T^F3^hUo-UZLB5U}NRQGkc@c#~6+gBxH|l+1bWQ$Vq$Spn`2B0#&_d z+0h$hDAhAmrB9XRG(&l`$=-N$PYlNh66l1{7+(MBN?fG^#=V1N0##yc=l#_orWSjI zbLSU%Q0aWo-k6v#hVEBt1POG)sK#sH0mV?SfU)-BHWGoVWv?8`cMUdeb{0AA?D(lX zYGH3|7~O_?0n`W*=!8*^$p;P7gyuJnd?^#C62J192ZCrMtM~@vn^j>qUgkCCiEYC% zf&@BY^r@azVhL698zVNgmIzc$pI?J&)kKY#*@5iZgr!uazT&H4Nwt=A)v6IB&5YElc_K)X;4}OO6pF&ox_rEvKkau?!y&I?zB+v=dXqtT2@KSDb z4Qt$@B?49VSBqAkhp9*|kt518KM!7&YH(f~#W8{eI$;_OFPon)t(t0hdr~G)mDnqY zT%7tQizEDv#xq>H*UoTnL=@FusSza538PlumO^~qgmwnMi86tzUB`mS@v1|kmqkm) zJEa(JT+G8TGB}FfCe#QL=!B7bey|vi?Bi)zm`5g1C3>xCm3GtXwIYXpe@V{$ix_4f zilo+(8bJb`FzSihS(4u;kF4TZnLw53Q>!zviYa!6$Wbb&G_T$1k$%|bNRAOC&NxX!|S!!tar#2#W8{eI$<=|%k47!@4egf zd)q}y1ggX*q$dTft|D^8Uv}X6N+jxwghra(L0bd~bi!!9zB6Tb;(^}!*iumvfhzHC zczI=UQ{FZr$BO_5KId6k{rrMa<|+$Y1POG)sK@<_BkvthQ{TxDArYvGDH=huG}~Ky z0ggsF@x)gPvSW5eP%p9?K>~d(jpogL2VUH5T6P^%q(q=fytm(~_Hn=EZKC}<2fp%DZ#>#aMILBDi04!f4#Y$JhSGw zaJ)3{yR4ozCnB2KiE0E1*(1BOqcrcnxW0B|tW2OPnMcqXae1t7sYen@^W<&$mFT!= zju9l#38T@nR3TfZ^(SqU>k$%xDlsd~(ePsQ7AbyL_U#?HZ$^J*-OC7$5hTPJQ9ZH_ zw;gzV%s?e0I8q`|B`SVc8;j8Y>=pc6(b{f#Nb=Um*P^l*rl z2vk)ZA5M;%Eyu!crMTmVok|DCX!5Dm2omUo(HO(xWk)Kvk z8o}O`j+O{i^?%fuTp|y;4n^DN(i;uGSz``MnI275FKPq{bi(Le?Ufy0@O?HbORYX6 zP*wGM0L}gBMed!*F?Gjl8mG3IUELW?t#CDh1Ug}~lECY?ti0=HcC)ZdpelMAr|ZMV zTEh}D_$I3pbBOJG5X~`y1Ug~V!~6Cs+narm1$}5P5vW>nNK5PG`cZwNIF4403~cDN zYpl}u791l;pc6)ShM=RYRlS>ROmMVBph~RjF#Cm;Y6nG*F0C?|cgPF&+P68!2omUo z(R|1?H?cXzpEHeL3yDCLczbzxj*(+4a!hD6pVdzK#1?;QVXgzPMUX%zjJlP&&1Xku zd}7s~$^@#YmzqWc>gX9F-lKY@jb|4I{bUROXi0Y;HG%{>VKh_vi?Pf&@H;Coua!ig zYF^{|^xF-jUK5dnvySXpt6V(3U2EzwQ6os86Go$i+H_+keQtiPbSsHK)%5wb&8tT= zxG8d!-4?>~Ey~NE(Wk-)66l0!G)s$d)*)XWzN2^>i9l6DPf@GZghn=s_w#aXtFX$; z^YZso+i;8^fle6Bi_^V2`+OuHAD7u$B2cwvaV2uKgUFE*IVQEv%|14<=bak0;TS;z zoiLi+I5ih5|0q8%GfE~Dsr6Na#^Xd%$~cSYr`>u1Ug~#w*Bn7GH!bT z?p0JKP$gzetKBh#Mo0@!ruNPZ<+X1?-g$Bxju9kek8Izb45hkXL0}KU+rN+5 zRkKBqKqrhU`-@FgG*gco&QK2A7plZM1eqa0reOx*+|`TPsqDCS%5p< zNyF99-V%YTYiBCbTWVvIFqS_vafc1YF})=M zRky0upgMp+Q?a@tN6yt6?7;2=24k__^qs2_B+v={bi&B@*%886_x*<07d<5cRWU`~X_P6WdLNPF_r!i| zK#9GEz5zYyT}_Q3fle4X^bPv6VsCdFVwcMVs=7XCNPV1&)q%VlI-Z>zk!iS9swX-0 zY6J;%!f4j)gz?O$=tv9k0E$0}% z{wWiv66=(Yc;aokJ5uD(+3Q)2pQ(nInjTbPsz#7NCyc(q-Pvr#$7F+js7#=$&}B|f z=7y%MFp=Y}={Wn|uY)1rbR5SB66l0c1znYsEG;I^5c96PM4+nkz5uEscemDy>euD4 zi5GN+{tLR3C!Rh~pSR0-Z4O zmYQ5=yBcW>7s~aJ2vn^I3Z^%S8dgVG8*-bi>T^=R>r))Z2omUo(f8qan|W_b^zUEBQF~X7Ac0PpMq^lWmt}gd)1R6v6R4u+A*uKe{bds&+J zKGX;j=!DUVI)3-rxzZ1^a}^ffhf1L8;kl+XTEw1u$wZFz8}2hZukh?z`Fn7TAR&8X z$HqNko&M>PJt8trB2aZSJe=AoANO0T?yoL-#JUx<&*~l(M{^FS5hR3zpn7CSXfCJP zPs6j`uI(-nsG@aw%!DOJvD#0WaMk?QiEB`eAR)GGz81~hW+764Mc7ngPh#7WqsV`9 z*dn-Jv2An0Xx5?szhc|wgi*cEe{vvUQ;8g6+mfRo5gW=y z_<32+l`Vq%MJJ3ZAZP}*Rm;x$>S#tb+!w0EJTDvHJhkS?bL=?_@0VNK{&P2u5hTzF zqjuuJN36ioirQ*1aT0+lRxq4uqw-isbJ){tj(;!MqJ7aWj-G>R1POG)Xm%KyH}U0> zOl@X@OrWagZ%wIQq8PQr#r4tU;RDvCXgy{4s5olrs}Us738N|t-BY%yUQNZNM|X)p z)vEX~n!C-B<_i-!(qbR8?mH(d^M-Y&xr)^Y66l1{Z@1H9b}w_XlJXw{Rewi?nMavg za{OrfnBDQ4q6{YjBS@gHMI-M^JYg;wla(D|GJz^FkAnD3-Cr$^V_W(|)~)0o;v@o9mA3}d^Srv%naC)7leJs_R%!Soj$;G~*(2M0;}%;K z^G0cyEEA}DS|f<+^lO_PLaKVvUS*429a+}yIF1n{&5z8T;pYnAc4LXtpeWVFSggIA-j7dP9jkC_^VjgvVpaR z<$m|$EU9!G_Q<0L#|RSWgwfg@$4{~shg!2snQ;<5 z_j%E~n$}wH^C@Kz%Q?TDRjSpCTCZvZ33S3}{&t7q>`S|y>{35k)dL7rrS12iIerKJ>T-J9My)d&*kgwZva>xJ^(`6L_X zA`_?*b4+H1H4)=XD95ZZCzQvfPO&w8dUK2*flio4v-sHo<$cLx>{Qo25`n4}K_zKa zaTBTy7daZ-oUNqL{OY;(^`X|Q8bLz#$O@&*Q`UDq&I*&`g#@Y&eke{3XAnJWMUM2x z^_4m65)7q>4L7@LbXPVB-vs?Ko!2d$_`B%KA@?J(xw33Sg5g!i;T$7Kpsz*mwbxrJ zo+$$iNsDCyRiX-QUgO55n4%&_|4nJigqi~k-TMzWkLR>SkU%Gl&Q+@vrO)J1hRLgj zNd&5@UN1uZ27#u?Zo;{{cYdYPc;-k$-Oj_P|3i%+fle6B`w_od={{+wA!D&jpsM0{ zM|!&ppf^ZycJ&YR$}H^=L#{rZZ)zNUVA_}AV;Y5!06CBv+Q@ssIU5C;; zjv7G%oiMuZZggY^&viAlIxQ2Zq7igdT`ER3iX08Q*JXF|wlggGZ79bG66l1{EKmt< zEHzgT!_+fFBmz~}&UlKan(6HYaTop3rwRK}t*zl+>=5!S)d&*kgwbzzZV>BQF~Z>X zP$p1yZ@yI$=~(v@DHY7fU5;K_KCM8M)s*-sSje4m} zy)xolh2LJxuDpoUHytC6T#X=sz80;WS!yx6eJn)3eXUHOs%)O%02;YW&kvEKYS0o^ z>%F6XrM5weJWc7_lr&#RTb0sF@jI^J!U;}8-XgC zUBm3ES#rF1n9dr{xZ&TaK_ZR8QX@#96Gm?n2WdrX{civAQxhZtRiZMtt#2MW7IA%? zUc8e1nAcu=dRl^cjJqv@1Ug~V8{U2?v%8S2{V*p{B2eXFA48U!!Wr$3m>c*E~dMk3W=E(Z8gvI_y-!UBN28_)TdLmmm?S zO6}7mpunbTR7Wpz43A7_^+FaaebW-Co?49{fxZ^?_AQyuZf?j>+HOmf2vm)D5=b*z zyILKVzntf?Ese62M#mC4Mv#y_vcEdcWnsm#lm`ZxK-J$-0rYNAhk7)`aYPtrvEuu# zDrFxelFO+^kU%GluJd+(uqvZ2E9>Z4iv+6rKjJjXi)QS7Jb_KxmYeM~4weX1ZFXi4K>~d(8gH;Ln4R*EVd;@WBmz}y7B{3i96uT(C32MX^I-%2 z>BL&i8A5eSY6J;%!su-~$DeiE)P-H797v$bbi$qH(et;~Ee$(RlMPzYn=RiqgwB;3 zK?0pH>RlS>!iI%+W4f_JB?47557edAJQQm4iub`XX+>D)m|pAwy$NFk33S3}lxdd| ztWtq~EGJ$jP?gxr)m+zSaid~(|5WN8j%SY!45cw9Y6J;%!lVabo3;vMt2@0ZrZI@o~&A}Ol+CVF@nT|XC>(R;Pk8&InwKNS7r~LsbqYT2~^>y zrTM~lPEhnGrqe2`$= z{gS0`5D8H+K)w&PO+=3GIZKoWnG2MRNiu;d{JoL4bZD)z|H?e2@}Oktmx#p4<<-eA zQLJul=!2C?%Cniu56Xf2LKR*&8clZ0O67CmSxSW|$IZCnm$yOdWeHV!iah?HF0(_`fQJky#2grdOKT&yoMJ7;%PYL?%K6pbbhq-{6NC8 zj*U0HuZd?=Z2S$Stjk!X^beUp6+R!Ss(8^;C9d=!#sAi5={bl*O?Mx1&%LOISL9fc z{$0^F9icc*lL=JebDqYMEy~S`wC=6A4-rSMwhfTD7~w}f?qY@wk;AcMLAHKsKc$+# zOrQ!|AGG$s`ogSSk*>F!4{fb;Nm6?ki8YIL zbe-3-wnfWLb7r4BHA>l=GJz^=&r^=06`1MK18w8WNm5H630jGk#sO5N??kkzp9Pd< zrTaYBD!E2W1gh}8gzEIeE3m)Mj?j*xWX5Yt>V+Ikmn-%o!@E-zIvQs1oQLEOT2#g>hwuMWj=J>w|n@V=P zrvE30&GEwhif!SxS#tdU2%Adeu=#Tu&3_471otbpO((8ebJ&htoGaWHs>I(_2eRPC z3e5Y(;J#3Wekon&=@nT|eRS4TA~1r4 zaJ);6v$uNs`KDB0uiANLO{6;m5~xDomu9D&T!Ed)v=A6U;?nRSx+fMhSt?R44XnUs zy=jm&?R}EuA)^ZYYZ~qJ$eC^GQYd@-rzFXzMnYWk8{RmWEcdH!^PQRZWBcqo|HuTY z(1)jfoET@;)~i-_-ET>fLyv@5dz)tDvi9TDt?b5%3|p9;*CcCP;`n^vsaz zrh^~Ey*;pXMHc)aK0E!IOrQ$;cj&>#kk9{Fr@^ekB+|qL#aks@q(xeRuO$mS-V* zHTugBWCB&#Po>c~WR_?9f;9RSL|_DoBDq8lT^*C9%}{x28Me6fvh2?dM@s~%u)mGm zr~$>tYz2lP5?& zkDdk?K|**XzFj^u|CHIVa|5cdX?+~D$95Pz!=S1T1S6>>z z<7w@8ZuO)HJ94+3{+X*xpbGnOY5d2}e5}dPp?a@oqop2RB$mBuM87Ms^1EomRV8yj>PM=9@Jj&vCdP})A(9>5H()EcC$>N3hM*t`lxqJ zd3|oCzQ{vyJX1^=<%9>&y!3u$o69PF-$Ka}fhw#!q4kPREmsOQ->A=OmMm4IAi)Y&r1mZ| zS*oeDvsWwUgBI$`kZXYZLKW7}&`6}wiRP!S|=@J6=haDxHw9x>P3NQfV}8HT{%LFSqKqEtLsW$rW2I=J!z=M`!9s ztW1{buaL+YZb$D0I=Ux{8n3f+nk%=qWa=C5kqK0Z`m(MMbSB}=(A;h3+AAfy|Dinp zAl#3<)Na;Mm1lKoBd88r&hWf6zP&N|mf|?pHXozpU${n@N1qBKNTA!M(L60RL#g;< z6ze@|m_(q;XL8;EF%rL>ugI}z$26te&=G9TT2XnXMvy?KP@}0+W4_X8!wI&K##$nQ zD)D#mM$ynyTpyhe%~h7h9cQ7-EQBqB1lIV``Ubki%9;N5+|d|g&S4`^C31)rb}V&E zBb%iw_DVrM=l3>d!WKaSN9SrZuiZ10)9qaO>6c6*P?dj7UYdih8O;eG+6)mhRw&oo z)aKRlGMWiNjUa*Z>XAeLHC+kY9Lz6!i1kuc0#&Qx^HQ5Sf~ri#JQIm?(v^`DXl;(T zN;G;vjUa(5V$h0g*B2<`@5b_nqrWjEP!-rEFRil`MHRVXou84(3zU$VP!h@6 z#t0I4HPh^S9lI(EzYb!X+YOfpREa%Jcosyxmg1^)-`Yo+GA4=DTR+T9*dj>awW-kz z`QBGq-smKA8QVu9P$iBpD=d`Gm$+(w#PwBN?w(-&bNiT&+!jGXY}>9)>P5cuo09a{ zo(Jc|{10JMJ^uPecYJ758V?{^0P7C-P~sQc^T7OVX@r6r!TpMD+to}nl7@Cr4sCPg zzt`vgL)cW+uD{Wx_GxDAMV@-Bz4CsRD{s?`bBy4A@!F&rXugFiqep~tcUnUV2~>67 z_eLjXZMMw!np`wcNqZg4eQCXTj39y6CiO0rt*dnY+>wtsqmc+yT^jyI_qnB*&s)ra z-6+db$#n0;r-pxL7(oKBW?I{6LRn?+@qzqW@OFtnRc6Q=T|3|CfX&Wgjdhnc&dP|| z@w}+}R)!HI@M@-2-v)luDmO>+_RGU00#$f*(YgTrziDd_QFmn+!w3?1HPblyQCqdo zZ>8|dfkrJ7sFJVRj-A^oqvxjZ3XL+g7(qh5Gjt^)iHIxSGJ&ckhl-Os7)5u9Qgp6v z-b_*s3{K&*7jMvF1c|p7N|^6H6~_x=WsgARkMTQ{1$&cegxh7&HlT5?HR#-{_Z2ZW zgnn>is$dswhM;virS1DI%5rKgVFU@h$7nROhbYPe_krxv3QI-2ZC|Le&Obp8y|`ap z$l%J_G6}5rU<*MrQebUmN_y}~ zHi~AAzz7oZ-RI%rI!e?zdv0I;-&ttQ`$E;OW0!Pd6tHCuh`FEZD5GB3^CtFfX$BfK zf&|`Us5j%Svy#2sl@}WI@A^^ZeW7al{7brR3C#jl#fvwuvn?tpXY{W8T31fsez)xlRmX>3(j}FSq?WiCcUP}#VZ~!gDA(R`p*eol2oiXY zq5E#+6YZSnPF$bxogsm$6+xGD;#q51$K;^zO&Lazz&i%brkkAOKk7mXw+nYrkU&-MSvhoV zMhARbUxsod5iyboZz3>)#F9g=sbwJ6`xR{l@$cu)6!W}*G|$og)IK_sxI@^sd(RU4 zO{Hg)?e9RO$`K2Vgk@V0d+j{g-$j#*Ho~eh=M&o%+NV)9V?&yU#nJky{v_fy5pqP$ zkrkFkb(}V$cu10Q`M0`k$;9sa8r)3NiQ4nCpK?>{C{0Iextq7wEKZ{_!k+Y&T8MHa zluI(s|LV@dyN=n15hQG%)^bL!yC)fU9;wfC8GmIVfvUSBE&0Ukcr+fTc9L;sS_8A^ zE=TZ;TDC1`{W97CUOjBa0|F4MuJy(-wQ9Cp?ie(voYUz{w`wbGk-*)75M)upz z2(5!vJ&6zc+JMbn(j-ehi|W}G^e}^`iK>_BI7T}raUV{fivGzFmB8Fo-Bvh>A3Ev5 z^0jQH{S_h3sQ7!CEuOS4nK)OTRg?HcPj@!_#PNM{L_H??IK-!^Ji^`JTgm*p+CO$K zr^+nL@71>LVV|~NtV}9e%R+A_hur4R zzU!D}Tb#q%9X$hFJBr^`LpqKx4c!e&j+Y-s#4*_jajrx@%kOGynIxXp*~3tBd<0!Slu^U)mR(MfL1{td*vt-xc*@iQm=a zs!7JZbuGssM^plH(^`#0lBue)9g~Lx1R21bOSkcb?nr zC%xgN>HKzPz~2S2%u2`+m7wR+zcjhEA5(alrI`9 z{$r0|ggPVI|4GgJChc(h`>$?t{fx9dYW z>e$ZJy=nhL*i?eJywXM_5mCeOjNkBFziTmaWo3rWbfN^gL&|^7)ifgJ7A}}IqM+qC zY~LUfV!LVKlC*}cpcfO-=**mbgL?fhVN(s6lc7u7??7#zU?R%X_tB^0?W`?ZhiEY( zwng9n)P5oV`BbL&Df~-74gU>M^Q0q3Lj1<&H7-w1PD`Q#{z>6(&p&3pYvmyksKQ?* z^;*74;UO&?{T{pR(#qehSof-0K=AHy|M^sO<%r*ID*Z?Q@Vk|Xr!}fmoy>S0eS=?8 zc$boadmBvbCw(eG+f>@y={oV=bF12PB5GZs_q34&`QpMM42t=x`8EYl3%ib=p6bb79AbE@9&!OFA-JK=P$Ef%@>InPsF+R zm-b#AI-Bk#Y6OXT+n4I9=W{hbyJ!p+5gPi}z0C}XK$X}YRl1}~7;QviuQC5-|0`nb zzbdh<<{0uN#rUdLO4imXowPmtQ*>fZ*DEW#=$4mDfnP@Izjr0-QQVMXJe-x?8_2K)&00wjpjuW*N3_%^M1|WpxBZlGO^@PHS)sE^xG7p^YCcDOBL5h z$02B&YRA$PxKkIS<8Z#4p7pd{34e@;?LkTvS}#07C+FVsDaGh8cFf*yX=9}vNYw6_ zVtz&ytJ{s}Kl-E??W%v%ZpuAW`jwyxe{VE56@7z0a(QN*3!b3G-;d+fWSzJVj(8&O zgLtmy5-~T{F?-S9y`)ctgxKEX>_B~MK}4@7LPP%|BgK7J-IuJw-&GGHb{YJ9NBU1S z6Sik965rZ9j{hZWZ67(J65_1ea+_16UV5Lxk2RccK5`Fxjmg@+(xvp#n5^w9o%(md$$S+OB|CepV;pID8Kd7e*KnWY+!fEztXUW{XE^S2b={?v zBdNMY3#-#=)|R`E?Tm!IE-0=^;FMa-(u&Km-C3>2p$34wA=m^RZtQwdbz zwL*R(JwJk+mSsI&zHT3Ww_;nY!D4A&se7Wegy>h&cFhLA<&}0wIgk*~UG43fCQJM3 zGJWU&{H(VxgxU=FsZb@_5|*p?6BE1lQ0%TZ_z=L$tzdDzUBRNTinK_xKe5=4(4?BfE(=x{6f4*TwvH zr+x<+|K63TM{z?6Kd~y8zTLyB^yE|{qUCKPN{!1PC!{=8I{m-Kz5`5(;(2>HP!Ksm zC8K~y6chm^-QM(~AgG{{!~h5i2p0&V1O@iU>3}ws+sPYw^#YT-#w2w&sM!t9jmLVYr3UhU+D>C^z8MW{vlk}aB6{Ij~lXN>n_4cU72dJSa^CdA&W z-Rrh&Ii7mZqacEu#zI2Bkwd=$1RKq#Uk{s%wMA5wHgm(+4$*S->mcfGZ?QfaoYbcEt!-^bI`k5D~2m!BPz zW6Gm3Na1I`mD{ne@J(c2iRFt1)=3=B_LWCL1bg1RMmhG?{J5Yv-0!c}b)Kvx4G6^t#MHJCw3(u2yH{;&H6T zG<`8bqrE_@k7F(!>1~g8HNu(e$@lWnU1V%ud9rQ$N*oJ&HwSc)Q$ooI7X^s)-J+g1V$YRO*+bnp6XFmSt^T5rBgfv%T{PQUF^Z2 zIC*4QR;fAT?EX+bC;HLstAA9WfE_!d(CigA_z;>oqgL5zhEbUOxs@ z>?>vsj59}E*Ad9I<@H&CBex%sGRo}lEmr&-G|POS;ztT1(yz}T&5&QpSXr*Cm^Wvg zxqF|EKrTiPyBIBH+?}5&s+Ka%S96Z{BLxv9s?H$4jZ4M#XcPO67;-(2bz@VihCr_V zVn$%YjeJr@RN6b@db>Q9Q9ad<6hurcIz8}GJ1!N=xO3no@#@`jR_O zsFJaKZhbNS$kSH#tg(KiAmYsKw7~pnj9?i{@0Jr654>c}96e4$AlGa8(gGaMXFq|? zfS6NOjQIOyt4#4!KT;6UxYpFb%-x+Vu0}s5dWIROoy&NBuU=3ZK z;ztT1vgb?+L`NwZ*UNPyxojGKen8^I-gt&HDO{76B>uU}7) zHRrZUII=9Z)q_E6_?xL30=d$rrjphFkZdboc`Np({R6D*VdMNri9|fLf=k6RCea+9 zKr%|xJpZ`USodB0thoNHm<;vUSr23J9h$r7mhI)KAD9+5R(ATQEH$_7r z*SWhRp+D*jZf|a-w(5LxtRE?esM&TD^hd)hTg=*XKaJh-G|!QOh*evM!Te}W zbNqLjgNZcH5rJI1TjS01@pH;rt7s0M9x%?26hxG$Iuz!4t%EOF{YlQQontiwat#g* z0)5qGZhfm2>5^e(Q~gLmMB>@OpsyaGcKV3g>nWP&h(NBisRKdpPN8;c);^E*bNyr* zyB-A*XLb()z1x%eyD#oy&`z zQQm7}(0Yv4+RVeeI(rmEVBFVd{~aO^k3AOF3~FxUtVNCSZ-mk^h~Q`SyMu84O`>zQ zJ^g|5&>GMYNI?YZuKOd93pJ{aKnm)1l&vF>f(TrF?vFq&v{7^fQV@X_hmOEfVLxN5 z>j_ z1rez2?vFq&)WJFeTM@Mnwz`f$3L>zC_eUTXuA({uDTu%}r2NFkgVyZm#j*PrI@-sH zehdjnfT zyO4`D_`=QkrHpE%cg?*)qcr&jNI?X*I(>U3Wt^H5Z~b$5i;+E9M<5rkM9tPbB4x~@ z^`l>D(^z?Bw7o_t3L>~~jJ#YbuE%?{4i^75r#L`11HKEnczsM+A1!5&Z(xycASRK` zfD}Y<-{gOmOT{wCH?YVz5NtCb0=d{SXn#7NltI3MMZSS3G25{K6a^7Hj(c|IQn8F) zWG|FB_>wqAz5%`qx!7L$v!aqgzJW!)fneVNDTv_t5ueEjmT{i!g{&{$7Zu57Km>BJ zr8aQV!?GUVkiGB$`BJ&bH$Vy^cs5t4reu_xyUu*Kd{^>nUHw01leML`6w)LuP?-yUGR zP3IuR4|2RBg+MO$$W*ISjBI#&!@snzxOjD>?W-vYB6ywm`lH>_kWr^>qBy*4l!ibq z_AI?|_2b$N#;(B$;sx>zkb(%d2E1|Qr#arR-}_=7&2vN`7kkLw{AkiXkJvu-9Wj;W zIZ_b8Yoa$lM$;VMKyiRLn&*f>F81`*Y$i=j{yF&u;w$nEkb($aMZI}WzJVa$Knx_` z01?Q=5erXWk#8W#HxRYRH$Vy^cy;#l)w2{Yxp6&@=ulfnAQ#(2O1qN1OYL-ybYft) z6Jb#lM6fO4>D_S@_Zc>4ow4zE9f4dNLGsp*s0E4QZYk3^L%so05WyCXw|-oy&{qU# zjT%P20V0r#BVlT_qr41?UnTw=G~OcL04a#zntSWuiv@#Xa^A(U+uqO-I+qvsrIS2+ zQGA!y+C0ad$emi95yAGO+OzQSUs8yoskx^)nnBHNoVB_xiA4ODW?;1X-IgK$NfOwi zSQ2an9f1@?V4ZXX&QwI;?9vfPK?Kef9f9@0k%(v=ffPjGT)962x&BqEBnOv7x21XO z1g_`YgQ{nB8Aw3{S{(OBAQ##}Is*FxH8r+~jz9_`a8%wOfm~>1>IkGD0$b$%2;@R5 zQ%4{L5%+H^>_M~uuy1r3NI?YJDECJo7g}dJ0x5_b0t`{QbyBSTZT^9 zJ4~AYC;C?EX*$j55akRW63=~CjJ}FLLwjpE>rtL1MR0OaB&XSoaelsYzR`)3lvj{J z=ZqAmb2v}f&pP7CkHcp0?*wc5juhG%_1=XD+P9?>vyPK(vX*vLrmWs>*327kUD`QX zLm=0>SD&D>kd6m_EVGUfhn`q%_O8**no(+@AIr{P@>3lG^j&yFJ@W70Y1SwoXI-w@ zQs$xHNE^Ilf7}^}y^Q8Unfai~D_j4h9x&zDNl1amegcEzuf~ znd-;#zLu|Kz2<9<20F%A5v4l0ezUplM7&kFe6l735mUOBr2aS>$l(i-jO2=2%uYYV zTkSXLZG~KX17?Y;zXV#I+C+%Yo2)lucP3aLE=ctwg-@y9@xu{{Zf=Pv)hiEeHdp3J zu$J!9ON9tNOZMUYhbg0K7s1us3Wu{gSmoS8e(8dZ3* zhCr^A_2nqP^FUznwWxs4=Wn#pEZr%=ie5I>kCdkmm!s1J4p5XfR{+N}eZAbnGMBbb zv}S#rrpZ7A-EBZ$)*aw@ILW9Tx6yp&g9K}QuCW>dx%eE&N8dRp=iv2i7tF0aKe9?! z(B~kQWY^6ibSCF1dp^>a#o3q4_pf|tZS6D8j}%1kY|dHvtdwzTLNROd#>Up2qB;V( zx;<8e@&->!8J!66J0W%v0x5{N)U+tg+H+FI*9HC7vooHxs-%w95XiNsN#S~*pFbmI zyk5Ajb;YV`wcN;AtR4jsd@tbghUcY>UspA=hBSTD8n#zQAQyjiHhtqcDdYEBEv&|O zqph=d$NG_ih+EBz)U#$?kTPOu_v)9bJIn_+=?LU%T&obBy?a5*DDyxU>&v}+%;DiN zexx8GI;seL%W_G|7}>qIwej8{^I++*8Une-UoJ@B!(Eax2L0N{nsc?Q`OV&OexxAc z%7Y;q*wZ<61afg{dv?AiWxP@@Xw`2tFZQuCr&Nl92$siR1UfR>P+Rr5H^?}Y zq9c%tTYb=^>r%!pYO9u4yBhvqoc>T0L~y&+Z2P;E(Te)~&#F6&x3B34tJcDseu>29<* z{IxUM6$KGIjx$Q$kTTZL95iM;E3Q%|AifK^cs5@se_P6^PIGX-RaLy5<;+?|K?Ki7 z(%4dl)Fon{)C>xNT&$Ju4!9#_NL?bLiaFXxQ4qm;rDM!ZDdPg^M62gVVt1mBKrVhR zJ@l@W@$e6q%+6On6zfO^QV_xVF^BJ#lp$>&!L|=uFG+EK+3!NMj)+MPn^$fmi0hkE zGz4-bo-IL{(SOsI7i|0Ve|@7lu496D?GMhH z>tMO>)|y{_&{b@2_0T$`@X0(+t%$1Ebj(eGZ{l+%j+pNi=_ICanG=f?M3ksnuHMxJ zQ9M$p9_48rtndM~Rh6+?%@K|5M(3A&5;jZSO%QQg9LrMm6e8GW>{sxfv@DN)Yndh9 zP85SyrP^z)LLiskA4i8CGsT*Y;@IayVzIn@BKOCgqUtrNbzZOq_`usy)F*5wPFlOx ze4v!*Sx98GrbZf{5_;r^u4YK~}(S zlCh(3viahdpNXn}jmbg;a$y@%_D+wp=DZdiM6g;zKex}-1!F_pqFnP;TgQgDeR!0R z7ZGw+gDXocvv{jS@ppQvpZj^(#nce{8QjmWk??boI zl2>$iPP}-IGy@`#i?zhb&nHSm(;8dMr?+=6bhY_I z#ctxM*dL^oN0e&e+J)w{IX%R@xodY!hLEuV}R1s)hc>xV~SP2D4QREYJt zK1%i#D{j_Y-%q6VY!ZtUM6d>Dw4>Yqd25fEUZIO<{&@?n=E#MkhrW@i9yXhjA64V= z$$s{oW*1MVcE3ZXzn7!arPA&9vh1{}i{u<^C_Tt;$b)3B@r4@ShuF_#fAG(WEA1B5 z5s@CGdKV(tgRDP#Wr!mddSn9M#q%zsLyP9czs`~T{K)v~Q3V=}6S9M?Gh<|=!HWBuq> zM2dd&q3Fkaihdvk5ggaZp0g^%kr29vHzN9>N`+iJALlk$ZO>qzFKWm(vou9N)=V4X zM+*DuRfew)al`{<)2-8^(#-;wyIT7`S*(=`5$vn?J-^!4MD&Gpgh#JRg^VpMaGtt3`B6O#(!*i$cZg|M^T(ND2n5!C=R|0xmX7;Z}@G9BRW3c z(a>hI=E-;~N5y2@B2yHO$)rvFHpGz|eAB<9II2{L;1%x7?(ggs&gUB_qd4y`&`X6} zIMXQ4IU;JH<^-06t?-FOm&o}M88uK8MDXmow|Ai&yJ-{y*g-J>pS0T*0=d|m7!q16 zWvHmZW$7Cz3L@C*_a9pzWvHk@0~s|?2;^cmojp% zZf3nnQG=;6YM>Cv#ok@Vm<3WsWYj=W5W!y9OYOdvG9se}3V~egrIx7rwUiMVHBb~p zus6Bu<~%7QGHRd@$i?1vbksa4BQk2BD2QOMxMthAQbuIdKp~KeV*~g0&XzJFqXvqC z2=?yz_y3L>CCv#q+$#!O2p_nL>4~ zilM6FGZ{5d6h!cR{9x@=DMRWKaaC#tg+MOWSGfmHk}`IX_L;x2vG|1$NI?YamFXLm zZ=j+E%VpF+y$iYc*|XbK)F4ep4HN|ttRIzaXWJ!)+A*OuDqJkrJ-fVw2xpZ1+kPfQ zIBn`})q@>38tD`fF7UD)HE^iY;1L!0=a0tqObIFgdM;3g}9AIIjZ?NCkEiw1JT$D zb_~Fv7=R^X0B$QHg4T8_)jzg1Kyik)^NixDpI9?rna%6Di$E^Db;(OL$V;`@OC^7R zzSE5gvj;(zK7E-NMQ2j)VxKCU^=VG}vY2BT&(6*?n*5z;{nauf25Z}2l%sRIa#GLT zWRDeUUOIW4QS0=l*0bg2W+4R;x{NWUe4=NY_pEXAiWrDMF7iLZbUt#JJ!GHno6NPw zx2?Ncm(!QVAO#UvC%UaU?U-@kyN*`6Bdb@TDX~aF1X-LQqfE(TM)&g_t%qOk zsUeVye8ceIP*m9QAjzgSXrEKaeNMV9;1}C#V0r6aJ)&&ta1P%&IR`1$$FtUs_3=zS z`T4K*OywCoXwoTn)_MrCMGe~L6mp-_eHS8lj19YZLXJeTMGe~L6mp-_MIaYhnc)xC zo{+XE?Q=Xjc?OaJ#+V;jpyy zJ6}C-TwM8)m~rS<489Aw$cCq{qYv9wKixU`amXMGS+IqS<>fg@ySlbjjBlcwFlYa4 zG@aR5q^#+$l?oAb^AOeJxNR+y=k?VFWBc#~F*c7K>vL;}Ts#X_Z9OV2RdGfu|G#`%I{-<85dppC%*|U^KR(pb0DnyX~8UDKaFER!|UaCR+oI>t% zx}`!c@>1RLM$y{jbYuUeuA-l_&*`=z`8Z+PaaS=J_f0-u%X2>)n^HQ9ttISzPM3lR z@`}QD2OP4!aG&pN^{~-AH}wbk=U7AJVy)C8_K=JLkSA=ACoH5V>{7_DrSEAEDNopy zZF|B7?Q;sb&*@SS!RyM>Ck}a@utEErD*E9fkc&Lw@Hf{E$vH?Ki$VLGLhf_Aa}eu9 z>wGx(z|;18^!f5-UoxgqOn+`4+X8SYh~U{wyM3OOA;@D9Cmfr>MIaYhGT|Zz&qx_$ zWeB!1es}B=mx2hgIB3?Mmoms>5wy=Kq{reSkc%v`@Z*EeNg4kXsw)IpOK&+Ii%UTS zSx#ZPsY%Kpk42EjA_hAii;F-mvMfpGUXU`%+-@PF$m+Z3>~p#lM34nY=YL$1GRR{Q zw9jeCeNGpFTx21Klj1H(84o_tMHKvbkMXIq&*@SSL6&To_8_GU@>m4za~eCHeNGpF zTx98o$9{Q5${77?A5oC@?v^_HoGt|sWRZtE#{4E_44|)NzoI?JYv0)WgDwKO$YTlr zv-hf$QL|i7>}fbJwm~6#*V3gRf;3>mSOj@2 zmh@O$1ak5G7*Y7PltCVgAdkh89*avs1drq9YyOln$YT+-&uPhhP8We(JkN)O?noKr zu?X^5Ea|bh6h!cR^dGw^Wst|hy2O$mi;F-m)>jjY{v~BdU1CX(#ibyE^~yhcZ%G;C zu^6<^X-SX8MIaYHE6;+y?kAl%pK?FKj>qCs5W)J<^FJtN$(mYaq|j>VWPh=CR#_;l zwY?mcFF%}Tv`+cN>QsL=@3HWzAK*O}wxhrOjD=s>aX zH^y;%*QKx>O_l|nPoHg#X>|k{i4Yg7s7u%0s2Hn~kTa4)K@m6-ay?5tQ*m~u>KDl@2%Ko>P zs^$SAaNhWQ%g1am3XMq+)ki1O_a7z0J{ry(H|CP4ePAAA-X48rnzhf8eU(gkWIR(< z<^Z>k+UMq3tFj1uzE3xAFis{Vh;ep4gGWIG&nPcv;?C-gM#k$2LX4PaM-9}wkc%zA zzUQN8w>lT)8eG_1LF`x`BNk@VHh5g^Y2xgYvj?%QR}^KtToi1{dU*glsZ_?m1aV_U zvLD}tG9qezY>N?>5-)!G^bg+0aoYqDtR+;w2Hn>Dbl52SSfco~Q#B2NT-+aC=D_>g zvyIA&y=Jo?~q_Dz6-fpVC5v*6de18rd&x;-MoV^I`Dutn}=G_uEX9Xu9=KrZ$&yv)oO$YZHZS(Ejp$D$~R zV9&zK%rwYjc^y0!g+MN@hnHbGlRTE0l>IqFdMt{92$tt%m_~Xm3V~eQA6};HW@@XG zV&2p|K{^*Oy6i-MZ?gjJELaq$ zTtH`wRSj_%D`WXoM*~i zi73_8n!~etzeVTiUGz~~dF4e!%Tv|pd`^q*=SU|R4SH3H?Y1E(S`UoYN`+iU%T~7$ zJ!01qBF5+#GlANw!)xRGSl&`YowEP6Xi-F|F4ZdRA9$TkOMk)6t#w-w5#=6y)~@;F zm%kwyznw1auSAI8wq&hT$kkssh*CotvFCcF%pb1?MUUZW^nI;IsWSXITEneS?tx1q zO10#@M*a@egT+&aYBCVPHv==e<*B74BQ&8*X4Ex0AwOoChCr@EJFC)-Bvz_uO)dX$MppSQXG*}Ed5R68%F`+q$b6wkEitjR#c^S?Og$uECTGA1`1 zm35(UQ1qX1Lqi}Jjvk-y%AJ&~xi8Xf0xvuqi+ww{L3OeQEIQwAT|}vdZtEEP#FU`; zw7{eo99M{_+15d)U(cda@n1C>C3&;;(T-fvQ8nDLK-}`A!cSI>@iSM;Q&|Ci7jw~^ z2ylE4(f>wp*&`9U45wW7S;D?9sg)?|oJFTvFAR*oT+x26P;DXIFH*sN=J`)|W(2;- ze4Wn0W+YPbKUJ38%l+~a9}r0^U}S)WBGMy`&K zF)~-5%xWZ~YR6aBp$tT1&vDR{8AdYJPyHh1#^s>3c6OGnuecsuUgTLt5P|(fxoWe!XEY+jlzch@xm1fd zx@6z}KAB?)G57GTby(i%8=bNb34IsQR^J9@X8w99XjN~Qn2F^@1kW_Cxibg%9Ud9` zX{&x#&(b9gL?GAqGpdrcltpJ$FAvaNzXykA-lW!@c6MYYQdl!E7wZyy6JrapS7oTc@rs!IJ5g1TgHX*ysFi{_^{!lzZO&b(@ws(zH$lGx{IZy zowy&LARmwJ_I~Fzfh~%f8g)KJSV|s=jhpjjoi;-T2z(dLbCgYIdAIIkqn>DCJ#f_s?5kMkNX`#61ajfpPG5kgKPoDnFOZS^MKghvNQ5^( z20d`k*#2O#j5ivz)etyW^idLQmS03o@0T&FWD9{5MBsd+`)dQ0#oe_}*FDn6&=AOl zBa}|;eOuUL_xj_{&|UGKUP5aC?FXN)cv>+=Bz66vu|3+A^@8u!5id2aE(SmGK}NIh zYtem>p2dO))MIpRRIZldq2=T2?jx@r5y*w5rQ4ccHSAIu)B;#`YHgn{yT?1?xjj?rkL<@BV5a1rfNekoKYR z6t%lRos~4c5P@8}wbcJwM=>{bU`DOz6Lj~oXN4mI*Il2_T0zmr(;e!>l{Ym6a-l^| zT9oGA`Vx6--=MjOlt_fKA81_B{4P;=SnZR4tk)1|GoTGmcRTg`Ok}M{&Zz%cs(}?*bdtCj^?V(ELJA_(vtt3w`TPq}_;xW`b!eCi_fBy ztBZzb{+3RwWGZFVJZwMn@9cB(?~0rM?;sQf5wEPCKwpkHXdQu+l=YKBe8v*L_rGQE zxr0bSL~rv;@=*Uv1|pEF_QcRQ5i?2OmQ;@6v=;>5*=XbpxNI}GpWu}E1 z*NXOJBssVwfimcv8v61m|G(tvGVr~K=zD%6qyQzw-=MQc`)W>LYyE3QV>D$D;<$U zpd{7|Ji<4X=KH(T13dD3n-AO1jOIApf9oqnK?G|FIydhBgFr6UM0_T>CWA`Gb8sN# z?&Y^BJla(Wxm0|+s)pdI*cwhz5W#JzN=uByYX2L7KrVh(Z?btALH{Muy#UjBRNK;7 zM+M{Z+0U$%j+TAoKT3rZL}0t=G7y1Wth-c;=m?}B0(;g*@IOgh4<4_Thdb@gBVYBw zzgm~L9R&6iuRaR>zl{<^@MxdX75bcI(4W(Ts;7|aU!`&Y5j}_qo?V)Z|3n}cwuoew zHOKa0U7|+#|5hqQu{=dgAz6-f< zZaSsvK@s|jMGC|s0%s9xpFjRCYM+qcAi5Ev0wIus2-ag7f@^INmj3Uf`#7GalPV$>*p?q7I0r|{u%9It^@sbrBSRsO@-Kvg{y&yZ BIG6wc literal 0 HcmV?d00001 diff --git a/vitamins/microview/LICENSE.txt b/vitamins/microview/LICENSE.txt new file mode 100644 index 0000000..d6e0a5b --- /dev/null +++ b/vitamins/microview/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 geekammo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vitamins/module.scad b/vitamins/module.scad new file mode 100644 index 0000000..c3e4470 --- /dev/null +++ b/vitamins/module.scad @@ -0,0 +1,147 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Random screw down modules. Currently just DROK buck converters. +// +include <../core.scad> +include + +function mod_part(type) = type[1]; //! Description +function mod_length(type) = type[2]; //! Body length +function mod_width(type) = type[3]; //! Body width +function mod_height(type) = type[4]; //! Body height +function mod_screw(type) = type[5]; //! Screw type +function mod_screw_z(type)= type[6]; //! Thickness of screw lug +function mod_hole_r(type) = type[7] / 2; //! Screw hole radius +function mod_holes(type) = type[8]; //! Screw hole positions + +module mod(type) { //! Draw specified module + vitamin(str("mod(", type[0], "): ", mod_part(type))); + + module drok_buck() { + l = mod_length(type); + w = mod_width(type); + h = mod_height(type); + body_l = 31; + end_t = 1.5; + chamfer = 2; + lug_l = (l - body_l) / 2 - end_t; + lug_w = 19; + lug_w2 = 10; + lug_t = 2; + lug_r = 2.5; + hole_r = mod_hole_r(type); + vane_t = 1; + vane_h = 5; + vane_l = 7; + vane_p = 10; + boss_od = 7.5; + boss_id = 5; + boss_h = 2.3; + boss_chamfer = 1; + + + module profile() + hull() { + translate([-w / 2, 0]) + square([w, h - chamfer]); + + translate([-w / 2 + chamfer, 0]) + square([w - 2 * chamfer, h]); + } + + module lug() + difference() { + hull() { + for(side = [-1, 1]) + translate([side * lug_w2 / 2, lug_l - lug_r]) + circle(lug_r); + + translate([-lug_w / 2, -1]) + square([lug_w, 1]); + } + hull() + for(y = [hole_r, 100]) + translate([0, y]) + circle(hole_r); + } + + color("silver") + rotate([90, 0, 90]) + linear_extrude(height = body_l, center = true) + profile(); + + color(grey20) + for(end = [-1, 1]) + translate([end * body_l / 2, 0, 0]) + rotate([90, 0, end * 90]) + union() { + linear_extrude(height = end_t) // endcap + profile(); + + translate_z(end_t) + rotate([90, 0, 180]) + linear_extrude(height = lug_t) // lug + lug(); + + for(side = [-1, 1]) { + translate([side * vane_p / 2, lug_t, end_t]) // buttress vanes + rotate([0, -90, 0]) + linear_extrude(height = vane_t, center = true) + polygon([[0, 0], [0, vane_h - lug_t], [vane_l, 0]]); + + translate([side * vane_p / 2, h / 2, end_t]) // bosses + rotate_extrude() + difference() { + hull() { + square([boss_od / 2,boss_h - boss_chamfer]); + square([boss_od / 2 - boss_chamfer, boss_h]); + } + translate([0, boss_h - boss_chamfer]) + square([boss_id / 2, boss_h]); + } + } + } + } + drok_buck(); +} + +module mod_screw_positions(type) //! Position children at the screw positions + for(p = mod_holes(type)) + translate([p.x, p.y]) + children(); + +module module_assembly(type, thickness) { //! Module with its fasteners in place + screw = mod_screw(type); + washer = screw_washer(screw); + nut = screw_nut(screw); + screw_length = screw_longer_than(thickness + mod_screw_z(type) + 2 * washer_thickness(washer) + nut_thickness(nut, true)); + + mod(type); + + mod_screw_positions(type) { + translate_z(mod_screw_z(type)) + nut_and_washer(nut, true); + + translate_z(-thickness) + vflip() + screw_and_washer(screw, screw_length); + } +} diff --git a/vitamins/modules.scad b/vitamins/modules.scad new file mode 100644 index 0000000..f7df40b --- /dev/null +++ b/vitamins/modules.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Random modules +// + +// +// p l w h s s h h +// a e i e c c o o +// r n d i r r l l +// t g t g e e e e +// h h h w w s +// t d +// z +// +drok_buck = ["drok_buck", "Drok buck converter", 50, 25, 20.5, M4_dome_screw, 2, 4, [[-21, 0], [21, 0]]]; + +modules = [drok_buck]; + +use diff --git a/vitamins/nut.scad b/vitamins/nut.scad new file mode 100644 index 0000000..16c7e0e --- /dev/null +++ b/vitamins/nut.scad @@ -0,0 +1,140 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Default is steel but can be drawn as brass or nylon. A utility for making nut traps included. +//! +//! If a nut is given a child then it gets placed on its top surface. +// +include <../core.scad> +use +use +use <../utils/rounded_cylinder.scad> + +function nut_size(type) = type[1]; //! Diameter of the corresponding screw +function nut_radius(type) = type[2] / 2; //! Radius across the corners +function nut_thickness(type, nyloc = false) = nyloc ? type[4] : type[3]; //! Thickness of plain or nyloc version +function nut_washer(type) = type[5]; //! Corresponding washer +function nut_trap_depth(type) = type[6]; //! Depth of nut trap + +function nut_flat_radius(type) = nut_radius(type) * cos(30); //! Radius across the flats + +module nut(type, nyloc = false, brass = false, nylon = false) { //! Draw specified nut + hole_rad = nut_size(type) / 2; + outer_rad = nut_radius(type); + thickness = nut_thickness(type); + nyloc_thickness = type[4]; + desc = nyloc ? "nyloc" : brass ? "brass" : nylon ? "nylon" : ""; + vitamin(str("nut(", type[0], arg(nyloc, false, "nyloc"), arg(brass, false, "brass"), arg(nylon, false, "nylon"), + "): Nut M", nut_size(type), " ", desc)); + + if(exploded() && nyloc) + cylinder(r = 0.2, h = 10); + + color(brass? brass : nylon ? grey30: grey70) translate_z((exploded() && nyloc) ? 10 : 0) { + linear_extrude(height = thickness) + difference() { + circle(outer_rad, $fn = 6); + + circle(hole_rad); + } + if(nyloc) + translate_z(-eps) + rounded_cylinder(r = outer_rad * cos(30) , h = nyloc_thickness, r2 = (nyloc_thickness - thickness) / 2, ir = hole_rad); + } + if($children) + translate_z(thickness) + children(); +} + +module nut_and_washer(type, nyloc) { //! Draw nut with corresponding washer + washer = nut_washer(type); + + translate_z(exploded() ? 7 : 0) + washer(washer); + + translate_z(washer_thickness(washer)) + nut(type, nyloc); +} + +module wingnut(type) { //! Draw a wingnut + hole_rad = nut_size(type) / 2; + bottom_rad = nut_radius(type); + top_rad = type[4] / 2; + thickness = nut_thickness(type); + wing_span = type[7]; + wing_height = type[8]; + wing_width = type[9]; + wing_thickness = type[10]; + + top_angle = asin((wing_thickness / 2) / top_rad); + bottom_angle = asin((wing_thickness / 2) / bottom_rad); + + vitamin(str("wingnut(", type[0], "): Wingnut M", nut_size(type))); + + explode(10) color(grey70) { + rotate_extrude() + polygon([ + [hole_rad, 0], + [bottom_rad, 0], + [top_rad,, thickness], + [hole_rad, thickness] + ]); + for(rot = [0, 180]) + rotate([90, 0, rot]) linear_extrude(height = wing_thickness, center = true) + hull() { + translate([wing_span / 2 - wing_width / 2, wing_height - wing_width / 2]) + circle(wing_width / 2); + polygon([ + [bottom_rad * cos(top_angle) - eps, 0], + [wing_span / 2 - wing_width / 2, wing_height - wing_width / 2], + [top_rad * cos(top_angle) - eps, thickness], + ]); + } + } +} +function nut_trap_radius(nut, horizontal = false) = nut_radius(nut) + (horizontal ? layer_height / 4 : 0); //! Radius across the corners of a nut trap +function nut_trap_flat_radius(nut, horizontal = false) = nut_trap_radius(nut, horizontal) * cos(30); //! Radius across the flats of a nut trap + +module nut_trap(screw, nut, depth = 0, horizontal = false, supported = false, h = 200) { //! Make a nut trap + nut_r = is_list(nut) ? nut_trap_radius(nut, horizontal) : nut + (horizontal ? layer_height / 4 : 0); + nut_d = depth ? depth : nut_trap_depth(nut); + screw_r = is_list(screw) ? screw_clearance_radius(screw) : screw; + render(convexity = 5) union() { + if(horizontal) { + if(screw_r) + teardrop_plus(r = screw_r, h = h); + + cylinder(r = nut_r, h = nut_d * 2, center = true, $fn = 6); + } + else { + difference() { + union() { + if(screw_r) + poly_cylinder(r = screw_r, h = h, center = true); + + cylinder(r = nut_r, h = nut_d * 2, center = true, $fn = 6); + } + if(supported) + translate_z(nut_d - eps) + cylinder(r = nut_r + eps, h = layer_height, center = false); + } + } + } +} diff --git a/vitamins/nuts.scad b/vitamins/nuts.scad new file mode 100644 index 0000000..6de2459 --- /dev/null +++ b/vitamins/nuts.scad @@ -0,0 +1,56 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Nuts +// +M2_nut_trap_depth = 2.5; +M2p5_nut_trap_depth = 2.5; +M3_nut_trap_depth = 3; +M4_nut_trap_depth = 4; +M5_nut_depth = 4; +M6_nut_depth = 5; +M8_nut_depth = 6.5; + +// s r t n w t +// c a h y a r +// r d i l s a +// e i c o h p +// w u k c e +// s n r d +// e t e +// s h p +// s k t +// h +// +M2_nut = ["M2_nut", 2, 4.9, 1.6, 2.4, M2_washer, M2_nut_trap_depth]; +M2p5_nut = ["M2p5_nut", 2.5, 5.8, 2.2, 3.8, M2p5_washer, M2p5_nut_trap_depth]; +M3_nut = ["M3_nut", 3, 6.4, 2.4, 4, M3_washer, M3_nut_trap_depth]; +M4_nut = ["M4_nut", 4, 8.1, 3.2, 5, M4_washer, M4_nut_trap_depth]; +M5_nut = ["M5_nut", 5, 9.2, 4, 6.25, M5_washer, M5_nut_depth]; +M6_nut = ["M6_nut", 6, 11.5, 5, 8, M6_washer, M6_nut_depth]; +M6_half_nut = ["M6_half_nut", 6, 11.5, 3, 8, M6_washer, 3]; +M8_nut = ["M8_nut", 8, 15, 6.5, 8, M8_washer, M8_nut_depth]; +toggle_nut = ["toggle_nut", 6.1, 9.2, 1.5, 1.5, M6_washer, 1.5]; + +M4_wingnut = ["M4_wingnut", 4, 10, 3.75,8, M4_washer, 0, 22, 10, 6, 3]; + +nuts = [M2_nut, M2p5_nut, M3_nut, M4_nut, M5_nut, M6_nut, M8_nut]; + +use diff --git a/vitamins/o_ring.scad b/vitamins/o_ring.scad new file mode 100644 index 0000000..2f6c777 --- /dev/null +++ b/vitamins/o_ring.scad @@ -0,0 +1,40 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Nitrile rubber O-rings. +//! +//! Just a black torus specified by internal diameter, ```id``` and ```minor_d``` plus a BOM entry. +//! Can be shown stretched by specifying the ```actual_id```. +// +include <../core.scad> + +module O_ring(id, minor_d, actual_id = 0) { //! Draw O-ring with specified internal diameter and minor diameter. ```actual_id``` can be used to stretch it around something. + vitamin(str("O_ring(", id, ", ", minor_d, "): O-ring nitrile ", id, "mm x ", minor_d, "mm")); + + D = actual_id > id ? actual_id : id; // allow it to be stretched + // + // assume volume conserved when stretched. It is proportional to major diameter and square of minor diameter + // + r = (minor_d / 2) * sqrt(id / D); + R = D / 2 + r / 2; + color([0.2, 0.2, 0.2]) rotate_extrude() + translate([R, 0]) + circle(r = r); +} diff --git a/vitamins/opengrab.scad b/vitamins/opengrab.scad new file mode 100644 index 0000000..54957ef --- /dev/null +++ b/vitamins/opengrab.scad @@ -0,0 +1,93 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Nicodrone OpenGrab V3 electro-permananet magnet, see . +//! +//! A permanent magnet that can be magnatized and de-magnatized electronically. +// +include <../core.scad> + +pitch = 33.8; +width = 40; +depth = 18; +magnet = 4.3; +pcb = 0.8; +pillar = 6; +target = 1; +pole_w = 2; +pole_l = 36; +poles = 15; + +module opengrab_hole_positions() //! Position children at the screw positions + for(x = [-1, 1], y = [-1, 1]) + translate([x * pitch / 2, y * pitch / 2, 0]) + children(); + + +function opengrab_width() = width; //! Module width +function opengrab_depth() = depth; //! Module height +function opengrab_target_thickness() = target; //! Target sheet thickness + +module opengrab() { //! Draw OpenGrab module + vitamin("opengrab(): OpenGrab V3 electro permanent magnet"); + + color("grey") + translate_z(magnet / 2 + eps) + cube([width, width, magnet - eps], center = true); + + color(grey80) { + gap = (width - poles * pole_w + 3 * eps) / (poles - 1); + pitch = pole_w + gap; + for(i = [0 : poles - 1]) + translate([(i - floor(poles / 2)) * pitch - eps, 0, 0.5]) + cube([pole_w, pole_l, 1], center = true); + } + + color("darkgreen") + translate_z(depth - pillar - pcb / 2) + cube([width, width, pcb], center = true); + + color(brass) + translate_z(1) + opengrab_hole_positions() + linear_extrude(height = depth - 1) + difference() { + circle(d = 4.7 / cos(30), $fn = 6); + + circle(r = 3/2); + } +} + +module opengrab_target() { //! Draw OpenGrab target + vitamin("opengrab_target(): OpenGrab silicon steel target plate"); + + color(grey80) + linear_extrude(height = target) + difference() { + square([width, width], center = true); + + opengrab_hole_positions() + circle(d = 3.2); + + for(side = [-1, 1]) + translate([side * (width / 2 - 3.5), 0]) + circle(d = 4); + } +} diff --git a/vitamins/pcb.scad b/vitamins/pcb.scad new file mode 100644 index 0000000..5ab39e6 --- /dev/null +++ b/vitamins/pcb.scad @@ -0,0 +1,996 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! PCBs and perfboard with optional components. The shape can be a rectangle with optionally rounded corners or a polygon for odd shapes like Arduino. +// +panel_clearance = 0.2; + +include <../core.scad> +include +include +use <../utils/rounded_cylinder.scad> +use <../utils/dogbones.scad> +use <../utils/tube.scad> +use + +function pcb_name(type) = type[1]; //! Description +function pcb_length(type) = type[2]; //! Length +function pcb_width(type) = type[3]; //! Width +function pcb_thickness(type) = type[4]; //! Thickness +function pcb_radius(type) = type[5]; //! Corner radius +function pcb_hole_d(type) = type[6]; //! Mounting hole diameter +function pcb_land_d(type) = type[7]; //! Pad around mounting hole +function pcb_colour(type) = type[8]; //! Colour of the subtrate +function pcb_parts_on_bom(type) = type[9]; //! True if the parts should be separate BOM items +function pcb_holes(type) = type[10]; //! List of hole positions +function pcb_components(type) = type[11]; //! List of components +function pcb_accessories(type) = type[12]; //! List of accessories to go on the BOM, SD cards, USB cables, etc. +function pcb_grid(type) = type[13]; //! Grid if a perfboard +function pcb_polygon(type) = type[14]; //! Optional outline polygon for odd shaped boards +function pcb_screw(type, cap = hs_cap) = Len(type[15]) ? type[15] : find_screw(cap, screw_smaller_than(pcb_hole_d(type))); //! Mounting screw type + +module pcb_grid(type, x, y, z = 0) //! Positions children at specified grid positions + translate([-pcb_length(type) / 2 + pcb_grid(type).x + 2.54 * x, + -pcb_width(type) / 2 + pcb_grid(type).y + 2.54 * y, pcb_thickness(type) + z]) + children(); + +// allows negative ordinates to represent offsets from the far edge +function pcb_coord(type, p) = let(l = pcb_length(type), w = pcb_width(type)) //! Convert offsets from the edge to coordinates relative to the centre + [(p.x > 0 ? p.x : l + p.x) - l / 2, + (p.y > 0 ? p.y : w + p.y) - w / 2]; + +module pcb_screw_positions(type) { //! Positions children at the mounting hole positions + holes = pcb_holes(type); + + if(len(holes)) + for($i = [0 : len(holes) - 1]) { + p = pcb_coord(type, holes[$i]); + translate([p.x, p.y, 0]) + children(); + } +} +// p p b p p b +// i i e i i a +// t n l n n s +// c o e +// h l w w c +// c +// +2p54header = ["2p54header", 2.54, 12, 3.2, 0.66, "gold", grey20, 8.5]; + +function hdr_pitch(type) = type[1]; //! Header pitch +function hdr_pin_length(type) = type[2]; //! Header pin length +function hdr_pin_below(type) = type[3]; //! Header pin length underneath +function hdr_pin_width(type) = type[4]; //! Header pin size +function hdr_pin_colour(type) = type[5]; //! Header pin colour +function hdr_base_colour(type) = type[6]; //! Header insulator colour +function hdr_socket_depth(type) = type[7]; //! Socket depth for female housing + +module pin(type = 2p54header, length = undef) { //! Draw a header pin + w = hdr_pin_width(type); + l = length == undef ? hdr_pin_length(type) : length; + chamfer = w / 2; + color(hdr_pin_colour(type)) + translate_z(l / 2 -hdr_pin_below(type)) + hull() { + cube([w, w, l - 2 * chamfer], center = true); + + cube([w - chamfer, w - chamfer, l], center = true); + } + } + +module pin_header(type = 2p54header, cols = 1, rows = 1, smt = false, cutout = false) { //! Draw pin header + pitch = hdr_pitch(type); + h = pitch; + + if(cutout) + dogbone_rectangle([cols * pitch + 2 * panel_clearance, rows * pitch + 2 * panel_clearance, 100], center = false); + else + vitamin(str("pin_header(", type[0], cols, rows, arg(smt, false, "smt"), "): Pin header ", cols, " x ", rows)); + + translate_z(smt ? 3.5 - h : 0) { + for(x = [0 : cols - 1], y = [0 : rows - 1]) + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) + pin(type); + + color(hdr_base_colour(type)) + linear_extrude(height = h) + for(x = [0 : cols - 1], y = [0 : rows - 1]) + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), pitch / 2]) + hull() { + chamfer = pitch / 4; + square([pitch + eps, pitch - chamfer], center = true); + + square([pitch - chamfer, pitch + eps], center = true); + } + } +} + +module idc_transition(type, cols = 5, skip = [], cutout = false) { //! Draw IDC transition header + rows = 2; + pitch = hdr_pitch(type); + height = 7.4; + width = 6; + length = cols * pitch + 5.08; + if(cutout) + ; + else { + vitamin(str("idc_transition(", type[0], ", ", cols, "): IDC transition header ", cols, " x ", rows)); + + color(hdr_base_colour(type)) + rotate([90, 0, 0]) + linear_extrude(height = width, center = true, convexity = cols * rows) + difference() { + translate([0, height / 2]) + square([length, height], center = true); + + for(i = [0 : cols * rows - 1]) + translate([pitch / 2 * (i - (cols * rows - 1) / 2), height / 2]) + circle(d = pitch / 2 + eps); + + slot = pitch / 3; + translate([0, height / 2 - pitch / 4 + slot / 2]) + square([cols * pitch, slot], center = true); + } + + for(x = [0 : cols - 1], y = [0 : rows -1]) + if(!in(skip, x)) + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) + pin(type, 2); + } +} + +module pin_socket(type = 2p54header, cols = 1, rows = 1, right_angle = false, height = 0, cutout = false) { //! Draw pin socket + pitch = hdr_pitch(type); + length = pitch * cols + 0.5; + width = pitch * rows - 0.08; + depth = max(hdr_socket_depth(type), height); + ra_offset = 1.5; + if(cutout) + ; + else { + vitamin(str("pin_socket(", type[0], ", ", cols, ", ", rows, arg(right_angle, false, "right_angle"), arg(height, 0, "height"), + "): Pin socket ", cols, " x ", rows, right_angle ? " right_angle" : "")); + color(hdr_base_colour(type)) + translate([0, right_angle ? -ra_offset - pitch / 2 : 0, right_angle ? width / 2 : 0]) + rotate([right_angle ? 90 : 0, 0, 0]) + translate_z(depth / 2) + linear_extrude(height = depth, center = true) + difference() { + square([length, width], center = true); + + for(x = [0 : cols - 1], y = [0 : rows -1]) + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2)]) + square(hdr_pin_width(type), center = true); + } + + color(hdr_pin_colour(type)) + for(x = [0 : cols - 1], y = [0 : rows -1]) { + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) + pin(type, hdr_pin_below(type) + width / 2 + (y - 0.5) * pitch); + + if(right_angle) { + rotate([-90, 0, 0]) + translate([pitch * (x - (cols - 1) / 2), -pitch * (y - (rows - 1) / 2) -width / 2, 0]) + pin(type, hdr_pin_below(type) + (y - 0.5) * pitch); + + w = hdr_pin_width(type); + translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2) - w / 2, pitch * (y - (rows - 1) / 2) + width / 2 - w / 2]) + rotate([0, -90, 0]) + rotate_extrude(angle = 90, $fn = 32) + translate([0, -w / 2]) + square(w); + } + } + } +} + +module chip(length, width, thickness, cutout = false) //! Draw a black cube to represent a chip + if(!cutout) + color(grey20) + translate_z(thickness / 2) cube([length, width, thickness], center = true); + +module usb_Ax2(cutout = false) { //! Draw USB type A dual socket + l = 17; + w = 13.25; + h = 15.6; + flange_t = 0.4; + h_flange_h = 0.8; + h_flange_l = 11; + v_flange_h = 1; + v_flange_l = 12.15; + bar = 3.4; + socket_h = (h - 2 * flange_t - bar) / 2; + + translate_z(h / 2) + if(cutout) + rotate([90, 0, 90]) + rounded_rectangle([w + 2 * v_flange_h + 2 * panel_clearance, + h + 2 * h_flange_h + 2 * panel_clearance, 100], r = cnc_bit_r, center = false); + else + color("silver") rotate([0, 90, 0]) { + linear_extrude(height = l, center = true) + difference() { + square([h, w], center = true); + + for(s = [-1, 1]) + translate([s * (bar / 2 + socket_h / 2), 0]) + square([socket_h, w - 2 * flange_t], center = true); + } + + translate_z(-l / 2 + 0.5) + cube([h, w, 1], center = true); + + translate_z(l / 2 - flange_t) + linear_extrude(height = flange_t) difference() { + union() { + square([h + 2 * h_flange_h, h_flange_l], center = true); + + square([v_flange_l, w + 2 * v_flange_h], center = true); + } + square([h - eps, w - eps], center = true); + } + } +} + +module rj45(cutout = false) { //! Draw RJ45 Ethernet connector + l = 21; + w = 16; + h = 13.5; + + plug_h = 6.8; + plug_w = 12; + plug_z = 4; + + tab_z = 0.8; + tab_w = 4; + + translate_z(h / 2) + if(cutout) + rotate([90, 0, 90]) + dogbone_rectangle([w + 2 * panel_clearance, h + 2 * panel_clearance, 100], center = false); + else { + rotate([0, 90, 0]) { + mouth = plug_z + plug_h - tab_z; + color("silver") { + linear_extrude(height = l, center = true) + difference() { + square([h, w], center = true); + + translate([h / 2 - tab_z - mouth / 2, 0]) + square([mouth + 0.1, plug_w + 0.1], center = true); + } + + translate_z(-l / 2) + cube([h, w, eps], center = true); + } + + color(grey30) { + linear_extrude(height = l - 0.2, center = true) + difference() { + square([h - 0.1, w - 0.1], center = true); + + translate([h / 2 - plug_z - plug_h / 2, 0]) + square([plug_h, plug_w - 0.1], center = true); + + translate([h / 2 - tab_z - plug_h / 2, 0]) + square([plug_h, tab_w], center = true); + } + + translate_z(-l / 2 + 1) + cube([h - 0.1, w - 0.1, 0.1], center = true); + } + } + } +} + +module jack(cutout = false) { //! Draw 3.5mm jack + l = 12; + w = 7; + h = 6; + d = 6; + ch = 2.5; + + translate_z(h / 2) + if(cutout) + rotate([0, 90, 0]) + cylinder(d = d + 2 * panel_clearance, h = 100); + else + color(grey20) + rotate([0, 90, 0]) { + linear_extrude(height = l / 2) + difference() { + square([h, w], center = true); + + circle(d = 3.5); + } + + tube(or = d / 2, ir = 3.5 / 2, h = l / 2 + ch, center = false); + + translate_z(-l / 4) + cube([h, w, l / 2], center = true); + } +} + +module hdmi(cutout = false) { //! Draw HDMI socket + l = 12; + iw1 = 14; + iw2 = 10; + ih1 = 3; + ih2 = 4.5; + h = 6.5; + t = 0.5; + + module D() { + hull() { + translate([-iw1 / 2, h - t - ih1]) + square([iw1, ih1]); + + translate([-iw2 / 2, h - t - ih2]) + square([iw2, ih2]); + } + } + + if(cutout) + rotate([90, 0, 90]) + linear_extrude(height = 100) + offset(t + panel_clearance) + D(); + else + color("silver") + rotate([90, 0, 90]) { + linear_extrude(height = l, center = true) + difference() { + offset(t) + D(); + D(); + } + + translate_z(-l / 2) + linear_extrude(height = 1) + offset(t) + D(); + } +} + +module usb_uA(cutout = false) { //! Draw USB micro A connector + l = 6; + iw1 = 7; + iw2 = 5.7; + ih1 = 1; + ih2 = 1.85; + h = 2.65; + t = 0.4; + flange_h = 3; + flange_w = 8; + + module D() { + hull() { + translate([-iw1 / 2, h - t - ih1]) + square([iw1, ih1]); + + translate([-iw2 / 2, h - t - ih2]) + square([iw2, ih2]); + } + } + + if(cutout) + rotate([90, 0, 90]) + linear_extrude(height = 100) + offset((flange_h - ih2) / 2 + 2 * panel_clearance) + D(); + else + color("silver") rotate([90, 0, 90]) { + linear_extrude(height = l, center = true) + difference() { + offset(t) + D(); + + D(); + } + + translate_z(-l / 2) + linear_extrude(height = 1) + offset(t) + D(); + + translate_z(l / 2 - t) + linear_extrude(height = t) difference() { + union() { + translate([0, h - t - ih1 / 2]) + square([flange_w, ih1], center = true); + + translate([0, h / 2 + flange_h / 4]) + square([iw1, flange_h / 2], center = true); + + translate([0, h / 2 - flange_h / 4]) + square([iw2, flange_h / 2], center = true); + } + D(); + } + } +} + +module usb_B(cutout = false) { //! Draw USB B connector + l = 16.4; + w = 12.2; + h = 11; + tab_w = 5.6; + tab_h = 3.2; + d_h = 7.78; + d_w = 8.45; + d_w2 = 5; + d_h2 = d_h - (d_w - d_w2) / 2; + + module D() + hull() { + translate([-d_w / 2, 0]) + square([d_w, d_h2]); + translate([-d_w2 /2, 0]) + square([d_w2, d_h]); + } + + + if(cutout) + translate([50, 0, h / 2 - panel_clearance]) + cube([100, w + 2 * panel_clearance, h + 2 * panel_clearance], center = true); + else + translate_z(h / 2) rotate([90, 0, 90]) { + color("silver") { + linear_extrude(height = l, center = true) + difference() { + square([w, h], center = true); + + translate([0, -d_h / 2]) + offset(delta = 0.2) + D(); + } + translate_z(-l / 2 + 0.1) + cube([w, h, 0.2], center = true); + } + + color("white") { + linear_extrude(height = l - 0.4, center = true) + difference() { + square([w - 0.2, h - 0.2], center = true); + + translate([0, -d_h / 2]) + difference() { + D(); + + translate([0, d_h / 2]) + square([tab_w, tab_h], center = true); + } + } + translate_z( -(l - 0.4) / 2 + 1) + cube([w - 0.2, h - 0.2, 2], center = true); + } + } +} + +module barrel_jack(cutout = false) { //! Draw barrel power jack + l = 13.2; + w = 8.89; + h = 11; + bore_d = 6.3; + bore_h = 6.5; + bore_l = 11.8; + pin_d = 2; + front = 3.3; + r = 0.5; + contact_d = 2; + contact_w = 4; + inset = 1; + if(cutout) + ; + else { + color(grey20) rotate([0, 90, 0]) { + linear_extrude(height = l, center = true) { + difference() { + translate([-h / 2, 0]) + rounded_square([h, w], r); + + translate([-bore_h, 0]) + circle(d = bore_d); + + translate([-h / 2 - bore_h, 0]) + square([h, w], center = true); + + } + } + translate_z(l / 2 - front) + linear_extrude(height = front) { + difference() { + translate([-h / 2, 0]) + rounded_square([h, w], r); + + translate([-bore_h, 0]) + circle(d = bore_d); + } + } + + translate([-bore_h, 0]) + tube(or = w / 2 - 0.5, ir = bore_d / 2, h = l); + + translate([-bore_h, 0, -l / 2]) + cylinder(d = w -1, h = l - bore_l); + } + color("silver") { + translate([l / 2 - inset - pin_d / 2, 0, bore_h]) + hull() { + sphere(pin_d / 2); + + rotate([0, -90, 0]) + cylinder(d = pin_d, h = bore_l - inset); + } + hull() { + translate([l / 2 - inset - contact_d / 2, 0, bore_h - bore_d / 2]) + rotate([90, 0, 0]) + cylinder(d = contact_d, h = contact_w, center = true); + + translate([l / 2 - bore_l, 0, bore_h - bore_d / 2 + contact_d / 4]) + cube([eps, contact_w, eps], center = true); + } + } + } +} + +module flex(cutout = false) { //! Draw flexistrip connector + l = 20.6; + w = 3; + h = 5.6; + top_l = 22.4; + top_t = 1.1; + tab_l = 13; + tab_w = 1; + slot_l = 16.4; + slot_w = 0.7; + slot_offset = 0.6; + + if(cutout) + ; + else { + color(grey30) { + translate_z(0.5) + cube([l, w, 1], center = true); + + linear_extrude(height = h) + difference() { + square([l, w], center = true); + + translate([0, -w / 2 + slot_offset + slot_w / 2]) + square([slot_l, slot_w], center = true); + } + + translate_z(h - top_t) + linear_extrude(height = top_t) + difference() { + union() { + square([top_l, w], center = true); + + hull() { + translate([0, -w / 2 + (w + tab_w) / 2]) + square([tab_l - 1, w + tab_w], center = true); + + square([tab_l, w], center = true); + } + } + + translate([0, -w / 2 + slot_offset + slot_w / 2]) + square([slot_l, slot_w], center = true); + + } + } + } +} + +module terminal_35(ways) { //! Draw 3.5mm terminal block + vitamin(str("terminal_35(", ways, "): Terminal block ", ways, " way 3.5mm")); + pitch = 3.5; + width = ways * pitch; + depth = 7; + height = 8.3; + chamfer_h = 3; + chamfer_d = 1; + box_z = 0.5; + box_w = 2.88; + box_h = 4.1; + wire_z = 2; + wire_d = 2; + pin_l = 4.2; + pin_d = 0.9; + + module single() { + screw_r = 1; + color("blue") { + rotate([90, 0, 0]) + linear_extrude(height = pitch, center = true) + polygon(points = [ + [ depth / 2, 0], + [ depth / 2, box_z], + [-depth / 2 + 1, box_z], + [-depth / 2 + 1, box_z + box_h], + [ depth / 2, box_z + box_h], + [ depth / 2, height - chamfer_h], + [ depth / 2 - chamfer_d, height], + [ -screw_r - eps, height], + [ -screw_r - eps, box_z + box_h], + [ screw_r + eps, box_z + box_h], + [ screw_r + eps, height], + [-depth / 2, height], + [-depth / 2, 0], + ]); + + linear_extrude(height = box_z + box_h) + difference() { + square([depth, pitch], center = true); + + translate([1, 0]) + square([depth, box_w], center = true); + + + } + + translate_z(box_z + box_h) + linear_extrude(height = height - box_z - box_h) + difference() { + square([2 * screw_r + 0.1, pitch], center = true); + + circle(screw_r); + } + } + color("silver") { + screw_z = box_z + box_h; + translate_z(screw_z) { + cylinder(r = screw_r, h = height - screw_z - 1); // screw + + linear_extrude(height = height - screw_z - 0.5) + difference() { + circle(1); + + square([4, 0.5], center = true); // screw slot + + square([0.5, 1.7], center = true); // second screw slot + } + + } + translate_z(box_z - pin_l) + cylinder(d = pin_d, h = pin_l + box_z, $fn = 16); // pin + + translate_z(box_z + box_h / 2) // terminal + rotate([0, -90, 0]) { + linear_extrude(height = depth - 2, center = true) + difference() { + square([box_h, box_w], center = true); + + translate([wire_z - box_z - box_h / 2, 0]) + circle(d = wire_d); + } + translate_z(depth / 2 - 1.5) + cube([box_h, box_w, 1], center = true); + } + } + } + for(i = [0: ways -1]) + translate([0, i * pitch - width / 2 + pitch / 2]) + single(); +} + +module terminal_254(ways, skip = []) { //! Draw 0.1" terminal block + vitamin(str("terminal_254(", ways, "): Terminal block ", ways, " way 0.1\"")); + pitch = 2.54; + width = ways * pitch; + depth = 6.2; + height = 8.5; + ledge_height = 5; + ledge_depth = 0.7; + top = 3; + back = 3; + module single(skip = false) { + screw_r = 1; + box_w1 = pitch - 0.4; + box_h1 = ledge_height - 0.4; + box_w2 = 2; + box_h2 = 2; + color("lime") { + rotate([90, 0, 0]) + linear_extrude(height = pitch, center = true, convexity = 5) + polygon(points = [ + [ depth / 2, 0], + [ depth / 2, ledge_height / 2 - box_h1 / 2], + [ depth / 2 - 0.5, ledge_height / 2 - box_h1 / 2], + [ depth / 2 - 2, ledge_height / 2 - box_h2 / 2], + [-depth / 2 + 1, ledge_height / 2 - box_h2 / 2], + [-depth / 2 + 1, ledge_height / 2 + box_h2 / 2], + [ depth / 2 - 2, ledge_height / 2 + box_h2 / 2], + [ depth / 2 - 0.5, ledge_height / 2 + box_h1 / 2], + [ depth / 2, ledge_height / 2 + box_h1 / 2], + [ depth / 2, ledge_height], + [ depth / 2 - ledge_depth, ledge_height], + [ top / 2, height], + [ screw_r + eps, height], + [ screw_r + eps, ledge_height / 2 + box_h2 / 2], + [-screw_r - eps, ledge_height / 2 + box_h2 / 2], + [-screw_r - eps, height], + [ -top / 2, height], + [-depth / 2, back], + [-depth / 2, 0], + ]); + + translate_z(ledge_height / 2 + box_h2 / 2) + linear_extrude(height = height - ledge_height / 2 - box_h2 / 2) + difference() { + square([screw_r * 2 + 0.1, pitch], center = true); + + circle(screw_r); + } + + linear_extrude(height = ledge_height) + difference() { + translate([0.5, 0]) + square([depth - 1, pitch], center = true); + + + translate([depth / 2, 0]) { + square([9, box_w2], center = true); + + hull() { + square([1, box_w1], center = true); + square([4, box_w2], center = true); + } + } + } + } + if(!skip) + color("silver") + translate_z(1) { + slot_depth = 1; + screw_top = height - 1.5; + pin_l = 3.3 + ledge_height / 2 - 2; + translate_z(ledge_height / 2) // screw + cylinder(r = 1, h = screw_top - slot_depth - ledge_height / 2); + + translate_z(screw_top - slot_depth) // screw head + linear_extrude(height = slot_depth) + difference() { + circle(1); + square([4, 0.5], center = true); + } + + translate_z(ledge_height / 2 - 1) + rotate([0, 90, 0]) + linear_extrude(height = 2, center = true) + difference() { + square([2, 2], center = true); + + square([1.5, 1.9], center = true); + + } + + translate([-1.5, 0, ledge_height / 2 - 1]) // terminal back + cube([1, 2, 2], center = true); + + translate_z(ledge_height / 2 - 2 - pin_l / 2) + cube([0.44, 0.75, pin_l], center = true); // pin + } + } + for(i = [0: ways -1]) + translate([0, i * pitch - width / 2 + pitch / 2]) + single(in(skip, i)); +} + +module molex_254(ways) { //! Draw molex header + vitamin(str("molex_254(", ways, "): Molex KK header ", ways, " way")); + pitch = 2.54; + width = ways * pitch - 0.1; + depth = 6.35; + height = 8.15; + base = 3.18; + back = 1; + below = 2.3; + above = 9; + color("white") + union() { + translate([ -depth / 2, -width / 2,]) + cube([depth, width, base]); + + w = width - pitch; + translate([- depth / 2, -w / 2]) + cube([back, w, height]); + } + + color("silver") + for(i = [0: ways -1]) + translate([0, i * pitch - width / 2 + pitch / 2, (above + below) / 2 - below]) + cube([0.44, 0.75, above + below], center = true); +} + +module pcb_component(comp, cutouts = false, angle = undef) { //! Draw pcb component from description + function show(comp, part) = (comp[3] == part || comp[3] == str("-",part)) && (!cutouts || angle == undef || angle == comp.z); + rotate(comp.z) { + if(show(comp, "2p54header")) pin_header(2p54header, comp[4], comp[5], len(comp) > 5 ? comp[6] : false, cutouts); + if(show(comp, "2p54socket")) pin_socket(2p54header, comp[4], comp[5], comp[6], len(comp) > 7 ? comp[7] : 0, cutouts); + if(show(comp, "chip")) chip(comp[4], comp[5], comp[6], cutouts); + if(show(comp, "rj45")) rj45(cutouts); + if(show(comp, "usb_Ax2")) usb_Ax2(cutouts); + if(show(comp, "usb_uA")) usb_uA(cutouts); + if(show(comp, "usb_B")) usb_B(cutouts); + if(show(comp, "jack")) jack(cutouts); + if(show(comp, "barrel_jack")) barrel_jack(cutouts); + if(show(comp, "hdmi")) hdmi(cutouts); + if(show(comp, "flex")) flex(cutouts); + if(show(comp, "D_plug")) if(!cutouts) translate_z(d_pcb_offset(comp[4])) d_plug(comp[4], pcb = true); + if(show(comp, "molex_hdr")) if(!cutouts) molex_254(comp[4]); + if(show(comp, "term254")) if(!cutouts) terminal_254(comp[4], comp[5]); + if(show(comp, "term35")) if(!cutouts) terminal_35(comp[4]); + if(show(comp, "transition")) if(!cutouts) idc_transition(2p54header, comp[4], comp[5]); + if(show(comp, "block")) + color(comp[7]) if(!cutouts) translate_z(comp[6] / 2) cube([comp[4], comp[5], comp[6]], center = true); + else if(comp[8]) translate([-50, 0, comp[6] / 2 - panel_clearance]) cube([100, comp[5] + 2 * panel_clearance, comp[6] + 2 * panel_clearance], center = true); + if(show(comp, "button_6mm")) square_button(button_6mm); + } +} + +module pcb_components(type, cutouts = false, angle = undef) { //! Draw list of PCB components on the PCB + not_on_bom(pcb_parts_on_bom(type)) + for(comp = pcb_components(type)) { + p = pcb_coord(type, [comp.x, comp.y]); + if(comp[3][0] == "-") + translate([p.x, p.y]) + vflip() + pcb_component(comp, cutouts, angle); + else + translate([p.x, p.y, pcb_thickness(type)]) + pcb_component(comp, cutouts, angle); + } +} + +module pcb_cutouts(type, angle = undef) pcb_components(type, true, angle); //! Make cut outs to clear components on a PCB + +module pcb_grid_positions(type) { + x0 = pcb_grid(type).x; + y0 = pcb_grid(type).y; + + cols = round((pcb_length(type) - 2 * x0) / inch(0.1)); + rows = round((pcb_width(type) - 2 * y0) / inch(0.1)); + for(x = [0 : cols], y = [0 : rows]) + pcb_grid(type, x, y) + children(); +} + +module pcb(type) { //! Draw specified PCB + grid = pcb_grid(type); + t = pcb_thickness(type); + if(pcb_name(type)) + vitamin(str("pcb(", type[0], "): ", pcb_name(type))); + + for(part = pcb_accessories(type)) + vitamin(part); + + pcb_components(type); + + color(pcb_colour(type)) linear_extrude(height = t) difference() { + if(Len(pcb_polygon(type))) + polygon(pcb_polygon(type)); + else + rounded_square([pcb_length(type), pcb_width(type)], r = pcb_radius(type)); + + pcb_screw_positions(type) + circle(d = pcb_hole_d(type) + eps); + + if(Len(grid)) + pcb_grid_positions(type) + circle(d = 1 + eps); + } + + color("silver") + translate_z(t / 2) + pcb_screw_positions(type) + tube(or = max(pcb_land_d(type), 1) / 2, ir = pcb_hole_d(type) / 2, h = t + 2 * eps); + + fr4 = pcb_colour(type) == "green"; + plating = 0.15; + color(fr4 ? "silver" : "gold") + translate_z(-plating) + linear_extrude(height = fr4 ? t + 2 * plating : plating) + if(Len(grid)) { + pcb_grid_positions(type) + difference() { + circle(d = 2); + + circle(d = 1); + } + if(fr4) { // oval lands at the ends + screw_x = pcb_coord(type, pcb_holes(type)[0]).x; + y0 = pcb_grid(type).y; + rows = round((pcb_width(type) - 2 * y0) / inch(0.1)); + for(end = [-1, 1], y = [1 : rows - 1]) + translate([end * screw_x, y0 + y * inch(0.1) - pcb_width(type) / 2]) + hull() + for(x = [-1, 1]) + translate([x * 1.6 / 2, 0]) + circle(d = 2); + } + } +} + +module pcb_spacer(screw, height, wall = 1.8) { //! Generate STL for PCB spacer + stl(str("pcb_spacer", round(screw_radius(screw) * 20), round(height * 10))); + + ir = screw_clearance_radius(screw); + or = corrected_radius(ir) + wall; + + linear_extrude(height = height) + poly_ring(or, ir); +} + +module pcb_base(type, height, thickness, wall = 2) { //! Generate STL for a base with PCB spacers + screw = pcb_screw(type); + ir = screw_clearance_radius(screw); + or = corrected_radius(ir) + wall; + + union() { + linear_extrude(height = thickness) + difference() { + hull() + pcb_screw_positions(type) + poly_ring(or, ir); + + pcb_screw_positions(type) + poly_circle(ir); + } + + linear_extrude(height = height) + pcb_screw_positions(type) + poly_ring(or, ir); + } +} + +module pcb_assembly(type, height, thickness) { //! Draw PCB assembly with spaces and fasteners in place + translate_z(height) + pcb(type); + + screw = pcb_screw(type); + if(!is_undef(screw)) { + washer = screw_washer(screw); + nut = screw_nut(screw); + screw_length = screw_longer_than(height + thickness + pcb_thickness(type) + washer_thickness(washer) + nut_thickness(nut, true)); + + taper = screw_smaller_than(pcb_hole_d(type)) > 2 * screw_radius(screw); // Arduino? + pcb_screw_positions(type) { + translate_z(height + pcb_thickness(type)) + screw(screw, screw_length); + + color(pp1_colour) + if(taper) { + h2 = max(0, height - 2); + if(h2) + pcb_spacer(screw, h2); + pcb_spacer(screw, height, 2 * extrusion_width); // Thin as can be at the top because there is no clearance around the holes. + } + else + pcb_spacer(screw, height); + + translate_z(-thickness) + vflip() + nut_and_washer(nut, true); + } + } +} diff --git a/vitamins/pcbs.scad b/vitamins/pcbs.scad new file mode 100644 index 0000000..e0f9942 --- /dev/null +++ b/vitamins/pcbs.scad @@ -0,0 +1,133 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include +// +// l w t r h l c b h +// e i h a o a o o o +// n d i d l n l m l +// g t c i e d o e +// t h k u u s +// h n s d d r +// e +// s +// s +// +DuetW = ["DuetW", "Duet WiFi electronics", + 123, 100, 1.6, 0, 4.2, 0, "mediumblue", false, [[119, 4], [119, 96], [4, 96],[4, 4]], + [], + []]; + +Melzi = ["Melzi", "Melzi electronics", 203.2, 49.53, 1.6, 3.81, 3.1, 6, "green", false, [[3.81, 3.81], [-3.81, 3.81], [-3.81, -3.81], [3.81, -3.81]], + [], + [": USB A to Mini B lead", ": Micro SD card"]]; + +RPI3 = ["RPI3", "Raspberry Pi 3", 85, 56, 1.4, 3, 2.75, 6, "green", false, [[3.5, 3.5], [61.5, 3.5], [61.5, -3.5], [3.5, -3.5]], + [[32.5, -3.5, 0, "2p54header", 20, 2], + [27, -24.6, 0, "chip", 14, 14, 1], + [60, -22.3, 0, "chip", 9, 9, 0.6], + [-8.5, 10.25, 0, "rj45"], + [-6.5, 29, 0, "usb_Ax2"], + [-6.5, 47, 0, "usb_Ax2"], + [53.5, 6, -90, "jack"], + [32, 4.4, -90, "hdmi"], + [10.6, 2, -90, "usb_uA"], + [3.6, 28, 90, "flex"], + [45, 11.5,-90, "flex"], + ], + [": Micro SD card"]]; + +ArduinoUno3 = ["ArduinoUno3", "Arduino Uno R3", 68.58, 53.34, 1.6, 0, 3.3, 0, "mediumblue", false, [[15.24, 50.8],[66.04, 35.56],[66.04, 7.62],[13.97, 2.54]], + [[30.226, -2.54, 0, "2p54socket", 10, 1], + [54.61, -2.54, 0, "2p54socket", 8, 1], + [36.83, 2.54, 0, "2p54socket", 8, 1], + [57.15, 2.54, 0, "2p54socket", 6, 1], + [64.91, 27.89, 0, "2p54header", 2, 3], + [18.796, -7.00, 0, "2p54header", 3, 2], + [ 6.5, -3.5, 0, "button_6mm"], + [4.7625, 7.62, 180, "barrel_jack"], + [1.5875, 37.7825,180,"usb_B"], + ], + [],[], + inch([ + [-1.35, -1.05], + [-1.35, 1.05], + [ 1.19, 1.05], + [ 1.25, 0.99], + [ 1.25, 0.54], + [ 1.35, 0.44], + [ 1.35, -0.85], + [ 1.25, -0.95], + [ 1.25, -1.05], + ]), + M2p5_pan_screw + ]; + +Keyes5p1 = ["Keyes5p1", "Keyes5.1 Arduino Uno expansion board", 68.58, 53.34, 1.6, 0, 3.3, 0, "mediumblue", false, [[15.24, 50.8],[66.04, 35.56],[66.04, 7.62],[13.97, 2.54]], + [[30.226, -2.54, 0, "-2p54header", 10, 1], + [54.61, -2.54, 0, "-2p54header", 8, 1], + [36.83, 2.54, 0, "-2p54header", 8, 1], + [57.15, 2.54, 0, "-2p54header", 6, 1], + ], + [],[], + inch([ + [-1.35, -1.05], + [-1.35, 1.05], + [ 1.19, 1.05], + [ 1.25, 0.99], + [ 1.25, 0.54], + [ 1.35, 0.44], + [ 1.35, -0.85], + [ 1.25, -0.95], + [ 1.25, -1.05], + ]), + M2p5_pan_screw + ]; + + +ExtruderPCB = ["ExtruderPCB", "Extruder connection PCB", + 33.02, 24.13, 1.6, 0, 0, 0, "green", true, [], + [[3 * 1.27, 24.13 / 2, 90, "D_plug", DCONN15], + [-(8.89 + 2.75 * 2.54), 2.5 * 1.27 + 24.13 / 2, 90, "molex_hdr", 3], + [-(8.89 - 1.5 * 2.54), -3.5 * 1.27 + 24.13 / 2, -90, "molex_hdr", 2], + [-(8.89 - 2.54), 2.5 * 1.27 + 24.13 / 2, 90, "term254", 4], + [-(8.89 + 2 * 2.54), -3.5 * 1.27 + 24.13 / 2, -90, "term254", 4], + ], []]; + +PI_IO = ["PI_IO", "PI_IO V2", 35.56, 25.4, 1.6, 0, 0, 0, "green", true, [], + [[(3.015 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.685) * 25.4, 90, "term35", 2], + [(3.46 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.69) * 25.4, 90, "term35", 2], + [(3.91 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.69) * 25.4, 90, "term35", 2], + [(3.4 - 2.7) * 25.4, (4.5 - 4.15) * 25.4, 0, "2p54socket", 13, 2, true], + ], []]; + +PERF80x20 = ["PERF80x20", "Perfboard 80 x 20mm", 80, 20, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; + +PERF70x50 = ["PERF70x50", "Perfboard 70 x 50mm", 70, 50, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; + +PERF70x30 = ["PERF70x30", "Perfboard 70 x 30mm", 70, 30, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; + +PERF60x40 = ["PERF60x40", "Perfboard 60 x 40mm", 60, 40, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; + +PERF74x51 = ["PERF74x51", "Perfboard 74 x 51mm", 74, 51, 1.0, 0, 3.0, 0, "sienna", true, [[3.0, 3.5], [-3.0, 3.5], [3.0, -3.5], [-3.0, -3.5]], [], [], [9.5, 4.5]]; + +PSU12V1A = ["PSU12V1A", "PSU 12V 1A", 67, 31, 1.7, 0, 3.9, 0, "green", true, [[3.5, 3.5], [-3.5, 3.5], [-3.5, -3.5], [3.5, -3.5]], [], []]; + +pcbs = [ExtruderPCB, PI_IO, RPI3, ArduinoUno3, Keyes5p1, PERF80x20, PERF70x50, PERF70x30, PERF60x40, PERF74x51, PSU12V1A, DuetW, Melzi]; + +use diff --git a/vitamins/pillar.scad b/vitamins/pillar.scad new file mode 100644 index 0000000..3eb72b5 --- /dev/null +++ b/vitamins/pillar.scad @@ -0,0 +1,76 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Threaded pillars. Each end can be male or female. +// +include <../core.scad> + +function pillar_name(type) = type[1]; //! Name of part +function pillar_thread(type) = type[2]; //! Thread diameter +function pillar_height(type) = type[3]; //! Body height +function pillar_od(type) = type[4]; //! Outer diameter of body +function pillar_id(type) = type[5]; //! Inner diameter of metal part +function pillar_ofn(type) = type[6]; //! Outer number of sides, 6 for hex, 0 for smooth cylinder +function pillar_ifn(type) = type[7]; //! Inner number of sides, 6 for hex, 0 for smooth cylinder +function pillar_o_colour(type) = type[8]; //! Colour of the outer part +function pillar_i_colour(type) = type[9]; //! Colour of the inner part +function pillar_top_thread(type) = type[10]; //! Top thread length, + for male, - for female +function pillar_bot_thread(type) = type[11]; //! Bottom thread length, + for male, - for female + +module pillar(type) { //! Draw specified pillar + function sex(thread) = thread > 0 ? "M" : "F"; + function fn(n) = n ? n : $fn; + + sex = str(sex(pillar_bot_thread(type)),"/", sex(pillar_top_thread(type))); + height = pillar_height(type); + thread_d = pillar_thread(type); + + vitamin(str("pillar(", type[0], "): Pillar ", pillar_name(type), " ", sex, " M", thread_d, "x", height)); + + color(pillar_i_colour(type)) { + if(pillar_bot_thread(type) > 0) + translate_z(-pillar_bot_thread(type)) + cylinder(h = pillar_bot_thread(type) + eps, d = pillar_thread(type)); + + if(pillar_top_thread(type) > 0) + translate_z(height - eps) + cylinder(h = pillar_top_thread(type) + eps, d = pillar_thread(type)); + + linear_extrude(height = height) + difference() { + circle(d = pillar_id(type), $fn = fn(pillar_ifn(type))); + circle(d = pillar_thread(type)); + } + + top = height + min(pillar_top_thread(type), 0); + bot = -min(pillar_bot_thread(type), 0); + + translate_z(bot) + cylinder(h = top - bot, d = pillar_thread(type) + eps); + } + if(pillar_od(type) > pillar_id(type)) + color(pillar_o_colour(type)) linear_extrude(height = height) + difference() { + circle(d = pillar_od(type), $fn = fn(pillar_ofn(type))); + + circle(d = pillar_id(type), $fn = fn(pillar_ifn(type))); + } + +} diff --git a/vitamins/pillars.scad b/vitamins/pillars.scad new file mode 100644 index 0000000..540ad2d --- /dev/null +++ b/vitamins/pillars.scad @@ -0,0 +1,43 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Nylon pillars +// +// n t h o i o i o i b t +// a h e d d f f +// m r i n n c c t t +// e e g o o h h +// a h l l r r +// d t o o e e +// u u a a +// d r r d d +// +M2x16_brass_pillar = ["M2x16_brass_pillar", "nurled", 2, 16, 3.17, 3.17, 0, 0, "gold", "gold", 3,-3]; +M3x13_hex_pillar = ["M3x13_hex_pillar", "hex", 3, 13, 5/cos(30), 5/cos(30), 6, 6, "silver", "silver", -6, 6]; +M3x20_hex_pillar = ["M3x20_hex_pillar", "hex", 3, 20, 5/cos(30), 5/cos(30), 6, 6, "silver", "silver", -6, 6]; +M3x20_nylon_pillar = ["M3x20_nylon_pillar", "nylon", 3, 20, 8, 5/cos(30), 0, 6, "white", "yellow", -6, 6]; +M4x17_nylon_pillar = ["M4x17_nylon_pillar", "nylon", 4, 20, 8, 5/cos(30), 0, 6, "white", "yellow", -6, 6]; +M3x20_nylon_hex_pillar = ["M3x20_nylon_hex_pillar", "nylon", 3, 20, 8/cos(30), 8/cos(30), 6, 6, grey20, grey20, -6, 6]; +M3x10_nylon_hex_pillar = ["M3x10_nylon_hex_pillar", "nylon", 3, 10,5.5/cos(30),5.5/cos(30),6, 6, grey20, grey20, -6, 6]; + + +pillars = [M2x16_brass_pillar, M3x13_hex_pillar, M3x20_hex_pillar, M3x20_nylon_pillar, M4x17_nylon_pillar, M3x10_nylon_hex_pillar, M3x20_nylon_hex_pillar]; + +use diff --git a/vitamins/psu.scad b/vitamins/psu.scad new file mode 100644 index 0000000..3920cd7 --- /dev/null +++ b/vitamins/psu.scad @@ -0,0 +1,339 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Powersupplies. Can be a simple cube or can be defined by a list of six faces, each with thickness, holes, cutouts, etc. +//! +//! Face order is bottom, top, left, right, front, back. +// +include <../core.scad> +use <../utils/maths.scad> +use <../utils/sector.scad> +use <../utils/round.scad> +include +use +use +use +include <../fan_guard.scad> + +function psu_face_holes(type) = type[0]; //! List of screw hole positions +function psu_face_thickness(type) = type[1]; //! The thickness +function psu_face_cutouts(type) = type[2]; //! List of polygons to remove +function psu_face_grill(type) = type[3]; //! Is this face a grill +function psu_face_fan(type) = type[4]; //! Fan x,y position and type +function psu_face_iec(type) = type[5]; //! IEC connector x,y, rotation and type +function psu_face_switch(type) = type[6]; //! Rocker switch x,y, rotation and type + +function psu_name(type) = type[1]; //! The part name +function psu_length(type) = type[2]; //! Length +function psu_width(type) = type[3]; //! Width +function psu_height(type) = type[4]; //! Height +function psu_screw(type) = type[5]; //! Screw type +function psu_screw_hole_radius(type) = type[6]; //! Clearance hole for screw, bigger than normal on ATX +function atx_psu(type) = type[7]; //! True if an ATX style PSU +function psu_left_bay(type) = type[8]; //! Bay for terminals +function psu_right_bay(type) = type[9]; //! Bay for heatsink +function psu_terminals(type) = type[10]; //! How many terminals and the y offset from the back +function psu_faces(type) = type[11]; //! List of face descriptions +function psu_accessories(type) = type[12]; //! Accessories to add to BOM, e.g. mains lead + +function terminal_block_pitch(type) = type[0]; //! Pitch between screws +function terminal_block_divider(type) = type[1]; //! Width of the dividers +function terminal_block_height(type) = type[2]; //! Height of the dividers +function terminal_block_depth(type) = type[3]; //! Total depth +function terminal_block_height2(type) = type[4]; //! Height under the contacts +function terminal_block_depth2(type) = type[5]; //! Depth of contact well + +function terminal_block_length(type, ways) = terminal_block_pitch(type) * ways + terminal_block_divider(type); //! Total length of terminal block + +module terminal_block(type, ways) { //! Draw a power supply terminal block + tl = terminal_block_length(type, ways); + depth = terminal_block_depth(type); + depth2 = terminal_block_depth2(type); + div = terminal_block_divider(type); + h = terminal_block_height(type); + h2 = terminal_block_height2(type); + pitch = terminal_block_pitch(type); + back_wall = depth - depth2; + contact_depth = depth2 - back_wall; + contact_width = pitch - div; + contact_h = 0.4; + washer_t = 1.2; + translate([0, -tl]) { + color(grey20) { + cube([depth, tl, h2]); + + translate([depth2, 0]) + cube([depth - depth2, tl, h]); + + for(i = [0 : ways]) + translate([0, i * pitch + div]) + rotate([90, 0, 0]) + linear_extrude(height = div) + hull() { + r = 2; + square([depth, eps]); + + translate([depth - eps, 0]) + square([eps, h]); + + translate([r, h - r]) + circle4n(r); + } + } + color("silver") + for(i = [0 : ways - 1]) translate([0, i * pitch + div, h2]) { + translate([back_wall, 1]) + cube([contact_depth, contact_width - 2, contact_h]); + + translate([back_wall + contact_depth / 2 - contact_width / 2, 0]) + cube([contact_width, contact_width, contact_h + washer_t]); + + translate([back_wall + contact_depth / 2, contact_width / 2, contact_h + washer_t]) + not_on_bom() no_explode() + screw(M3_pan_screw, 8); + } + } +} + +function psu_face_transform(type, face) = //! Returns a transformation matrix to get to the specified face + let(l = psu_length(type), + w = psu_width(type), + h = psu_height(type), + f = psu_faces(type)[face], + left = psu_left_bay(type), + right = psu_right_bay(type), + rotations = [[180, 0, 0], [0, 0, 0], [90, 0, -90], [90, 0, 90], [90, 0, 0], [-90, 0, 0]], + translations = [h / 2, h / 2, l / 2 - left, l / 2 - right, w / 2, w / 2] + ) translate([0, 0, h / 2]) * rotate(rotations[face]) * translate([0, 0, translations[face]]); + +grill_hole = 4.5; +grill_gap = 1.5; +module grill(width, height) { + nx = floor(width / (grill_hole + grill_gap)); + xpitch = width / nx; + ny = floor(height / ((grill_hole + grill_gap) * cos(30))); + ypitch = height / ny; + + for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) { + x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch; + y = -height / 2 + (y + 0.5) * ypitch; + translate([x, y]) + circle(d = grill_hole); + } +} + +module psu(type) { //! Draw a power supply + vitamin(str("psu(", type[0], "): PSU ", psu_name(type))); + + for(part = psu_accessories(type)) + vitamin(part); + + l = psu_length(type); + w = psu_width(type); + h = psu_height(type); + faces = psu_faces(type); + left = psu_left_bay(type); + right = psu_right_bay(type); + if(len(faces) < 2) + translate_z(h / 2) + color("silver") cube([l, w, h], center = true); + else { + for(i = [0 : 1 : len(faces) - 1]) { + f = faces[i]; + t = psu_face_thickness(f); + xw = [l, l - left - right, w, w, l, l - left - right][i]; + yw = [w, w, h, h, h, h][i]; + xo = [0, left / 2 - right / 2, 0, 0, 0, left / 2 - right / 2][i]; + fan = psu_face_fan(f); + iec = psu_face_iec(f); + switch = psu_face_switch(f); + + multmatrix(psu_face_transform(type, i)) + translate([xo, 0, -t]) { + color("silver") linear_extrude(height = t) + union() { + difference() { + square([xw, yw], center = true); + + cutouts = psu_face_cutouts(f); + if(cutouts) + for(cutout = cutouts) + polygon([for(p = cutout) p]); + + for(h = psu_face_holes(f)) + translate(h) + drill(screw_pilot_hole(psu_screw(type)), 0); + + if(psu_face_grill(f)) { + mx = 6; + my1 = i == f_top && psu_face_grill(faces[f_back]) ? 0 : 6; + my2 = i == f_back && psu_face_grill(faces[f_top]) ? 0 : 6; + translate([0, (my2 - my1) / 2]) + grill(xw - 2 * mx, yw - my1 - my2); + } + if(fan) + translate([fan.x, fan.y]) intersection() { + fan_holes(fan.z, h = 0); + + difference() { + square(inf, true); + + fan_guard(fan.z, thickness = 0, grill = true); + } + } + if(iec) + translate([iec.x, iec.y]) + rotate(iec.z) + iec_holes(iec[3], 0); + if(switch) + translate([switch.x, switch.y]) + rotate(switch.z) + rocker_hole(switch[3], 0); + } + } + + not_on_bom() no_explode() { + if(fan) + translate([fan.x, fan.y, -fan_depth(fan.z) / 2]) { + fan(fan.z); + + screw = alternate_screw(hs_cs_cap, fan_screw(fan.z)); + fan_hole_positions(fan.z) + translate_z(t + eps) + screw(screw, 8); + + } + + if(iec) + translate([iec.x, iec.y]) + rotate(iec.z) + iec_assembly(iec[3], t); + + if(switch) + translate([switch.x, switch.y, t]) + rotate(switch.z) + rocker(switch[3]); + } + } + } + } + // Special case for lighting type PSUs with teminals at the end + terminals = psu_terminals(type); + if(terminals) { + ft = psu_face_thickness(faces[f_front]); + bt = psu_face_thickness(faces[f_back]); + rt = psu_face_thickness(faces[f_right]); + lt = psu_face_thickness(faces[f_left]); + cutout = psu_face_cutouts(faces[f_left])[0]; + z = cutout[2].y + h / 2; + pw = w -ft - bt; + pl = l - right - rt; + pcb_thickness = 1.6; + heatsink_offset = 13.5; + color("#FCD67E") + translate([(-right - rt) / 2, (ft - bt) / 2, z - pcb_thickness]) + linear_extrude(height = pcb_thickness) + difference() { + square([pl, pw], center = true); + + translate([-pl / 2, -pw / 2]) + square(16, center = true); + } + + // earth strap + tab_w = w / 2 + cutout[2].x; + color("silver") + translate([-l / 2, w / 2 - tab_w, z]) + cube([left, tab_w - bt, lt]); + + // Earth pilar and screw + earth_inset = 4.5; + earth_d = 5; + translate([-l / 2 + earth_inset, w / 2 - tab_w / 2]) { + color("silver") + cylinder(d = earth_d, h = z - pcb_thickness); + + translate_z(z + lt) + not_on_bom() no_explode() + spring_washer(M3_washer) + screw(M3_pan_screw, 8); + } + + // terminal block + tb = terminals[2]; + if(tb) + translate([-l / 2, w / 2 - terminals.y, z]) + terminal_block(tb, terminals[0]); + + // Heatsink + // + heatsink_cutout = psu_face_cutouts(faces[f_right])[0]; + if(right && heatsink_cutout) { + z_top = heatsink_cutout[1].y + h / 2; + length = heatsink_cutout[2].x + w / 2 - 1.5; + + color("silver") + translate([l / 2, -w / 2]) + rotate([90, 0, 180]) + linear_extrude(height = length) { + translate([right + rt, z_top]) + rotate(135) + square([rt, right * sqrt(2)]); + + square([rt, z_top - right]); + + translate([rt, z_top - right]) + sector(rt, 135, 180); + } + } + } +} + +module psu_screw_positions(type, face = undef) { //! Position children at the screw positions on the preferred mounting face, which can be overridden. + faces = psu_faces(type); + f = is_undef(face) ? faces && psu_face_holes(faces[f_bottom]) ? f_bottom + : f_front + : face; + if(len(psu_faces(type)) > f) + multmatrix(psu_face_transform(type, f)) + for(point = psu_face_holes(psu_faces(type)[f])) + translate(point) + children(); +} + +module atx_psu_cutout(type) { //! Cut out for the rear of an ATX + holes = psu_face_holes(psu_faces(type)[f_front]); + translate([holes[0].x, -psu_width(type) / 2, psu_height(type) / 2 + holes[0].y]) + rotate([90, 0, 0]) + linear_extrude(height = 100, center = true) + round(5) + polygon([ // https://www.techpowerup.com/forums/threads/pc-component-dimensions.157239, tweaked + [18.7, -13], + [ 5.7, 0], + [ 5.7, 54], + [18.7, 67], + [127, 67], + [140, 67 - 13 / tan(52)], + [140, -5 + 11 / tan(52)], + [129, -5], + [81.3, -5], + [73.3, -13], + ]); +} diff --git a/vitamins/psus.scad b/vitamins/psus.scad new file mode 100644 index 0000000..f27f5e5 --- /dev/null +++ b/vitamins/psus.scad @@ -0,0 +1,104 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// +include +include +include +// +// Hole positions are looking from below / outside +// +// Face order is bottom, top, left, right, front, back. +// +// Meanwell RS-150-12 +mw_terminals = [9.525, 1.5, 15, 17.8, 7, 15]; + +PD_150_12 = + ["PD_150_12", "PD-150-12", 199, 98, 38, M3_pan_screw, M3_clearance_radius, false, 11, 4.5, [7, 11, mw_terminals], + [ + [[[82.5, -40], [82.5, 40], [-37.5, -40], [-37.5, 40]], 1.5, []], + [[], 0.5, [], true], + [[], 0.5, [[[-49, -19], [-49, -11.5], [-40, -11.5], [-40, 5], [47.5, 5], [47.5, -19]]]], + [[], 1.5, [[[-49, -19], [-49, -6], [37.5, -6], [37.5, -12.5], [49, -12.5], [49, -19]]]], + [[[-77.5, 1], [79.5, 9.5], [79.5, -9.5]], 1.5, []], + [[], 0.5, [], true], + ], + [] + ]; + +st_terminals = [9.666, 2, 13, 15, 8, 13.5]; +S_250_48 = + ["S_250_48", "S-250-48", 200, 110, 50, M3_pan_screw, M3_clearance_radius, false, 13, 5, [9, 11, st_terminals], + [ + [[[-39, -45.5], [-39, 39.5], [86, -45.5], [86, 39.5]], 1.5, []], + [[], 0.5, [], true], + [[], 0.5, [[[-55, -25], [-55, -17], [-46, -17], [-46, 11], [53.5, 11], [53.5, -25]]]], + [[], 1.5, [[[-55, -25], [-55, -13], [43.5, -13], [43.5, -15], [55, -15], [55, -25]]]], + [[[-79.5, 0], [78.5, 12.5], [78.5, -12.5]], 1.5, []], + [[], 0.5, [], true], + ], + [] + ]; + +// Single fan at back, wires exit opposite side from mains in +ATX500 = + ["ATX500", "ATX500", 150, 140, 86, No632_pan_screw, 5/2, true, 0, 0, [], + [ + [[], 0.8, []], + [[], 0.8, []], + [[], 0.8, []], + [[], 0.8, []], + [[[-69, -27], [-69, 37], [69, 37], [45, -37]], 0.8, [], false, [-25, 0, fan80x25], [45, -19.6, 180, IEC_inlet_atx], [45, 23, 90, small_rocker]], + [[], 0.8, [], true], + ], + [": IEC mains lead"] + ]; + +KY240W = + ["KY240W", "KY-240W-12-L", 199, 110, 50, M3_cap_screw, M3_clearance_radius, false, 0, 0, [], + [ + [[[ 199 / 2 - 12, 110 / 2 - 93], + [ 199 / 2 - 12, 110 / 2 - 9 ], + [ 199 / 2 - 138, 110 / 2 - 93], + [ 199 / 2 - 138, 110 / 2 - 9 ]]] + + ], + [] + ]; + +// This PSU, and ones very like it, are sold by LulzBot, and various sellers on eBay. +// The screw layout specified here uses the inner set of screw-mounts on the PSU, which are M4. +// The outer set don't appear to be M3, even though the datasheet claims they are. +S_300_12 = + ["S_300_12", "S-300-12", 215, 115, 50, M4_cap_screw, M4_clearance_radius, false, 0, 0, [], + [ [[[ 215 / 2 - 32.5, 115 / 2 - 82.5], + [ 215 / 2 - 32.5, 115 / 2 - 32.5], + [ 215 / 2 - 182.5, 115 / 2 - 82.5], + [ 215 / 2 - 182.5, 115 / 2 - 32.5]]] + ], + [] + ]; + +External = + ["External", "X Box", 0, 0, 0, false, false, false, 0, 0, [], + [], + [": IEC mains lead"] + ]; + +psus = [PD_150_12, S_250_48, ATX500, KY240W, S_300_12]; + +use diff --git a/vitamins/pulley.scad b/vitamins/pulley.scad new file mode 100644 index 0000000..32caa89 --- /dev/null +++ b/vitamins/pulley.scad @@ -0,0 +1,143 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Timing belt pulleys, both toothed and plain with internal bearings for idlers. +// +include <../core.scad> + +use +use +use <../utils/round.scad> + +function pulley_type(type) = type[1]; //! Part description +function pulley_teeth(type) = type[2]; //! Number of teeth +function pulley_od(type) = type[3]; //! Outer diameter +function pulley_belt(type) = type[4]; //! Belt type +function pulley_width(type) = type[5]; //! Width of teeth / belt channel +function pulley_hub_dia(type) = type[6]; //! Hub diameter +function pulley_hub_length(type) = type[7]; //! Hub length +function pulley_bore(type) = type[8]; //! Bore diameter for shaft +function pulley_flange_dia(type) = type[9]; //! Flange diameter +function pulley_flange_thickness(type) = type[10]; //! Flange thickness +function pulley_screw_length(type) = type[11]; //! Grup screw length +function pulley_screw_z(type) = type[12]; //! Grub screw position +function pulley_screw(type) = type[13]; //! Grub screw type +function pulley_screws(type) = type[14]; //! Number of grub screws + +function pulley_ir(type) = pulley_od(type) / 2 - (pulley_teeth(type) ? belt_tooth_height(pulley_belt(type)) : 0); //! Inside radius of the teeth +function pulley_pr(type) = let(belt = pulley_belt(type)) //! Pitch radius + pulley_teeth(type) ? pulley_teeth(type) * belt_pitch(belt) / 2 / PI + : pulley_ir(type) + belt_thickness(belt) - belt_pitch_height(belt); + +function pulley_offset(type) = -pulley_hub_length(type) - pulley_flange_thickness(type) - pulley_width(type) / 2; //! Offset of the belt path centre +function pulley_height(type) = pulley_hub_length(type) + 2 * pulley_flange_thickness(type) + pulley_width(type); //! Total height of pulley +function pulley_extent(type) = max(pulley_flange_dia(type), pulley_hub_dia(type)) / 2; //! Largest diameter + +T_angle = 40; +GT_r = 0.555; + +module pulley(type) { //! Draw a pulley + teeth = pulley_teeth(type); + + vitamin(str("pulley(", type[0], "): Pulley ", pulley_type(type), pulley_screws(type) ? " " : " idler ", teeth ? str(teeth, " teeth") : "smooth")); + + ft = pulley_flange_thickness(type); + tw = pulley_od(type) * PI / (teeth * 2); + hl = pulley_hub_length(type); + w = pulley_width(type); + r1 = pulley_bore(type) / 2; + + or = pulley_od(type) / 2; + ir = pulley_ir(type); + module core() { + translate_z(pulley_hub_length(type) + ft) + linear_extrude(height = w) let($fa = 1, $fs = 0.1) + difference() { + circle(or); + + circle(r1); + + for(i = [0 : 1 : teeth - 1]) + rotate(i * 360 / teeth) + if(pulley_type(type)[0] == "G") + translate([0, ir + GT_r]) + hull() { + circle(GT_r); + + translate([0, GT_r]) + square(2 * GT_r, center = true); + } + else + translate([0, (ir + or) / 2]) + hull() { + for(side = [-1, 1]) + translate([side * tw / 2, 0]) + rotate(-side * T_angle / 2) + square([eps, (or - ir)], center = true); + + translate([0, 1]) + square([tw, eps], center = true); + } + + } + } + + module screw_holes() { + if(pulley_screws(type)) + translate_z(pulley_screw_z(type)) + for(i = [0 : pulley_screws(type) - 1]) + rotate([-90, 0, i * -90]) + cylinder(r = screw_radius(pulley_screw(type)), h = 100); + } + + color("silver") { + render() difference() { + rotate_extrude() translate([r1, 0]) { + if(hl) + square([pulley_hub_dia(type) / 2 - r1, hl]); + + for(z = [pulley_hub_length(type), hl + ft + w]) + translate([0, z]) + square([pulley_flange_dia(type) / 2 - r1, ft]); + } + if(pulley_screw_z(type) < hl) + screw_holes(); + } + render() difference() { // T5 pulleys have screw through the teeth + core(); + + if(pulley_screw_z(type) > hl) + screw_holes(); + } + } +} + +module pulley_assembly(type) { //! Draw a pulley with its grub screws in place + translate_z(pulley_offset(type)) { + pulley(type); + + if(pulley_screws(type)) + translate_z(pulley_screw_z(type)) + for(i = [0 : pulley_screws(type) - 1]) + rotate([-90, 0, i * -90]) + translate_z(pulley_bore(type) / 2 + pulley_screw_length(type)) + screw(pulley_screw(type), pulley_screw_length(type)); + } +} diff --git a/vitamins/pulleys.scad b/vitamins/pulleys.scad new file mode 100644 index 0000000..74ff954 --- /dev/null +++ b/vitamins/pulleys.scad @@ -0,0 +1,50 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// n t o b w h h b f f s s s s +// a e d e i u u o l l c c c c +// m e l d b b r a a r r r r +// e t t t e n n e e e e +// h h d l g g w w w w +// e e s +// l z +// d t +// +T5x10_pulley = ["T5x10_pulley", "T5", 10, 15, T5x6, 11.6, 7.9, 7, 5, 19.3, 1.7, 3, 10.7, M3_grub_screw, 1]; +T2p5x16_pulley = ["T2p5x16_pulley", "T2.5", 16, 12.16, T2p5x6, 8, 16, 5.7, 5, 16.0, 1.0, 6, 3.75, M4_grub_screw, 1]; +GT2x20um_pulley = ["GT2x20um_pulley", "GT2UM", 20, 12.22, GT2x6, 7.5, 18, 6.5, 5, 18.0, 1.0, 6, 3.75, M3_grub_screw, 2]; +GT2x20ob_pulley = ["GT2x20ob_pulley", "GT2OB", 20, 12.22, GT2x6, 7.5, 16, 5.5, 5, 16.0, 1.0, 6, 3.25, M3_grub_screw, 2]; +GT2x12_pulley = ["GT2x12_pulley", "GT2RD", 12, 7.15, GT2x6, 6.5, 12, 5.5, 4, 12.0, 1.0, 4, 3.0, M3_grub_screw, 2]; +GT2x20_toothed_idler = ["GT2x20_toothed_idler", "GT2", 20, 12.22, GT2x6, 6.5, 18, 0, 4, 18.0, 1.0, 0, 0, false, 0]; +GT2x20_plain_idler = ["GT2x20_plain_idler", "GT2", 0, 12.0, GT2x6, 6.5, 18, 0, 4, 18.0, 1.0, 0, 0, false, 0]; +GT2x16_toothed_idler = ["GT2x16_toothed_idler", "GT2", 16, 9.75, GT2x6, 6.5, 14, 0, 3, 14.0, 1.0, 0, 0, false, 0]; +GT2x16_plain_idler = ["GT2x16_plain_idler", "GT2", 0, 9.63, GT2x6, 7.0, 13, 0, 3, 13.0, 1.0, 0, 0, false, 0]; + +pulleys = [T5x10_pulley, + T2p5x16_pulley, + GT2x20um_pulley, + GT2x20ob_pulley, + GT2x12_pulley, + GT2x20_toothed_idler, + GT2x20_plain_idler, + GT2x16_toothed_idler, + GT2x16_plain_idler]; + +use diff --git a/vitamins/rail.scad b/vitamins/rail.scad new file mode 100644 index 0000000..de75c28 --- /dev/null +++ b/vitamins/rail.scad @@ -0,0 +1,165 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . + +// +//! Linear rails with carriages. +// +include <../core.scad> + +use + +function rail_width(type) = type[1]; //! Width of rail section +function rail_height(type) = type[2]; //! Height of rail section +function rail_end(type) = type[3]; //! Minimum distance screw can be from the end +function rail_pitch(type) = type[4]; //! Distance between screws +function rail_bore(type) = type[5]; //! Counter bore diameter for screw head +function rail_hole(type) = type[6]; //! Screw hole diameter +function rail_bore_depth(type) = type[7]; //! Counter bore depth +function rail_screw(type) = type[8]; //! Screw type +function rail_carriage(type) = type[9]; //! Carriage type +function rail_end_screw(type) = type[10]; //! Screw used for ends only (Countersink used for better location) +function rail_screw_height(type, screw) = rail_height(type) - rail_bore_depth(type) + screw_head_depth(screw, rail_hole(type)); //! Position screw taking into account countersink into counterbored hole +function rail_travel(type, length) = length - carriage_length(rail_carriage(type)); //! How far the carriage can travel + +function carriage_length(type) = type[0]; //! Overall length +function carriage_block_length(type) = type[1]; //! Length of the metal part +function carriage_width(type) = type[2]; //! Width of carriage +function carriage_height(type) = type[3]; //! Height of carriage +function carriage_clearance(type) = type[4]; //! Gap under the carriage +function carriage_pitch_x(type) = type[5]; //! Screw hole x pitch +function carriage_pitch_y(type) = type[6]; //! Screw hole y pitch +function carriage_screw(type) = type[7]; //! Carriage screw type +function carriage_screw_depth(type) = 2 * screw_radius(carriage_screw(type)); //! Carriage thread depth + +module rail_hole_positions(type, length, first = 0, screws = 100, both_ends = true) { //! Position children over screw holes + pitch = rail_pitch(type); + holes = floor((length - 2 * rail_end(type)) / pitch) + 1; + for(i = [first : holes - 1 - first]) + if(i < screws || (holes - i <= screws && both_ends)) + translate([i * pitch - length / 2 + (length - (holes -1) * pitch) / 2, 0, 0]) + children(); +} + +module carriage_hole_positions(type) { //! Position children over screw holes + x_pitch = carriage_pitch_x(type); + y_pitch = carriage_pitch_y(type); + + for(x = [-1, 1], y = [-1, 1]) + if(x < 0 || x_pitch) + translate([x * x_pitch / 2, y * y_pitch / 2, carriage_height(type)]) + children(); +} + +module carriage(type, rail) { //! Draw the specified carriage + total_l = carriage_length(type); + block_l = carriage_block_length(type); + block_w = carriage_width(type); + block_h = carriage_height(type) - carriage_clearance(type); + end_w = block_w - 0.6; + end_h = block_h - 0.3; + end_l = (total_l - block_l)/ 2; + screw = carriage_screw(type); + screw_depth = carriage_screw_depth(type); + + module cutout() { + w = rail_width(rail) + 0.4; + translate([-w / 2, 0]) + square([w , rail_height(rail) + 0.2]); + } + + color(grey90) { + rotate([90, 0, 90]) + linear_extrude(height = block_l, center = true) + difference() { + translate([-block_w / 2, carriage_clearance(type)]) + square([block_w, block_h - screw_depth]); + + cutout(); + } + + translate_z(carriage_height(type) - screw_depth) + linear_extrude(height = screw_depth) + difference() { + square([block_l, block_w], center = true); + + carriage_hole_positions(type) + circle(screw_pilot_hole(screw)); + } + + } + color(grey20) + for(end = [-1, 1]) + translate([end * (block_l / 2 + end_l / 2), 0]) + rotate([90, 0, 90]) + linear_extrude(height = end_l, center = true) + difference() { + translate([-end_w / 2, carriage_clearance(type)]) + square([end_w, end_h]); + + cutout(); + } +} + +module rail(type, length) { //! Draw the specified rail + width = rail_width(type); + height = rail_height(type); + + vitamin(str("rail(", type[0], ", ", length, "): Linear rail ", type[0], " x ", length, "mm")); + + color(grey90) { + linear_extrude(height = height - rail_bore_depth(type)) difference() { + square([length, width], center = true); + + rail_hole_positions(type, length) + circle(d = rail_hole(type)); + } + + translate_z(rail_height(type) - rail_bore_depth(type)) + linear_extrude(height = rail_bore_depth(type)) difference() { + square([length, width], center = true); + + rail_hole_positions(type, length) + circle(d = rail_bore(type)); + } + } +} + +module rail_assembly(type, length, pos) { //! Rail and carriage assembly + rail(type, length); + + translate([pos, 0]) + carriage(rail_carriage(type), type); + +} + +module rail_screws(type, length, thickness, screws = 100) { //! Place screws in the rail + screw = rail_screw(type); + end_screw = rail_end_screw(type); + screw_len = screw_longer_than(rail_screw_height(type, screw) + thickness); + end_screw_len = screw_longer_than(rail_screw_height(type, end_screw) + thickness); + + index_screws = screws > 2 ? 1 : 2; + + translate_z(rail_screw_height(type, end_screw)) + rail_hole_positions(type, length, 0, index_screws) + screw(end_screw, end_screw_len); + + translate_z(rail_screw_height(type, screw)) + rail_hole_positions(type, length, index_screws, screws) + screw(screw, screw_len); +} diff --git a/vitamins/rails.scad b/vitamins/rails.scad new file mode 100644 index 0000000..1d82e75 --- /dev/null +++ b/vitamins/rails.scad @@ -0,0 +1,44 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Carriages +// +// L L1 W H H1 C B +MGN5_carriage = [ 16, 9.6, 12, 6, 1.5, 0, 8 , M2_cap_screw ]; +MGN7_carriage = [ 23, 14.3, 17, 8, 1.5, 8, 12, M2_cap_screw ]; +MGN9_carriage = [ 29.7, 20.8, 20, 10, 2, 10, 15, M3_cap_screw ]; +MGN12_carriage = [ 34.7, 21.7, 27, 13, 3, 15, 20, M3_cap_screw ]; +MGN15_carriage = [ 43.3, 27.7, 32, 16, 4, 20, 25, M3_cap_screw ]; +SSR15_carriage = [ 40.3, 23.3, 34, 24, 4.5, 0, 26, M4_cap_screw ]; +// +// Rails +// +// +// Wr Hr E P D d h +MGN5 = [ "MGN5", 5, 3.6, 5, 15, 3.5, 2.4, 0.8, M2_cs_cap_screw, MGN5_carriage, M2_cs_cap_screw ]; // Screw holes too small for M2 heads +MGN7 = [ "MGN7", 7, 5, 5, 15, 4.3, 2.4, 2.6, M2_cap_screw, MGN7_carriage, M2_cs_cap_screw ]; +MGN9 = [ "MGN9", 9, 6, 7.5, 20, 6.0, 3.5, 3.5, M3_cap_screw, MGN9_carriage, M3_cs_cap_screw ]; +MGN12= [ "MGN12", 12, 8, 10, 25, 6.0, 3.5, 4.5, M3_cap_screw, MGN12_carriage, M3_cs_cap_screw ]; +MGN15= [ "MGN15", 15, 10, 10, 40, 6.0, 3.5, 5.0, M3_cap_screw, MGN15_carriage, M3_cs_cap_screw ]; +SSR15= [ "SSR15", 15, 12.5,10, 60, 7.5, 4.5, 5.3, M4_cap_screw, SSR15_carriage, M4_cs_cap_screw ]; + +rails = [MGN5, MGN7, MGN9, MGN15, SSR15]; + +use diff --git a/vitamins/ring_terminal.scad b/vitamins/ring_terminal.scad new file mode 100644 index 0000000..058c391 --- /dev/null +++ b/vitamins/ring_terminal.scad @@ -0,0 +1,98 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Ring terminals and earth assemblies for DiBond panels. +// +include <../core.scad> +use +use +use +use <../utils/tube.scad> + +function ringterm_od(type) = type[1]; //! Outside diameter +function ringterm_id(type) = type[2]; //! Inside diameter +function ringterm_length(type) = type[3]; //! Length of the tail including the ring +function ringterm_width(type) = type[4]; //! Width of the tail +function ringterm_hole(type) = type[5]; //! Wire hole diameter +function ringterm_thickness(type) = type[6]; //! Metal thickness +function ringterm_screw(type) = type[7]; //! Screw type +function ringterm_extent(type) = ringterm_length(type) / sqrt(2); //! Space to leave + +module ring_terminal(type) { //! Draw specifeid ring terminal + screw = ringterm_screw(type); + d = 2 * screw_radius(screw); + vitamin(str("ring_terminal(", type[0], "): Ring terminal ",d,"mm")); + + t = ringterm_thickness(type); + w = ringterm_width(type); + od = ringterm_od(type); + id = ringterm_id(type); + l = ringterm_length(type); + angle = 45; + bend = washer_radius(screw_washer(screw)) + t * tan(angle / 2); + color("silver") union() { + tube(or = od / 2, ir = id / 2, h = t, center = false); + + translate([-w / 2, -bend, 0]) + cube([w, bend - id / 2, t]); + + translate([0, -bend]) + rotate([-angle, 0, 0]) + linear_extrude(height = t) + difference() { + length = l - od / 2 - bend; + hull() { + translate([-w / 2, -eps]) + square([w, eps]); + + translate([0, -length + w / 2]) + circle(d = w); + } + translate([0, -length + w / 2]) + circle(d = ringterm_hole(type)); + } + } + translate_z(ringterm_thickness(type)) + children(); +} + +module ring_terminal_hole(type, h = 0) //! Drill hole for the screw + drill(screw_clearance_radius(ringterm_screw(type)), h); + +module ring_terminal_assembly(type, thickness, top = false) { //! Earthing assembly for DiBond twin skins + screw = ringterm_screw(type); + washer = screw_washer(screw); + nut = screw_nut(screw); + screw_length = screw_longer_than(thickness + 2 * washer_thickness(washer) + nut_thickness(nut, true) + ringterm_thickness(type)); + + explode(10, true) star_washer(washer) + if(top) + ring_terminal(type) screw(screw, screw_length); + else + screw(screw, screw_length); + + vflip() + translate_z(thickness) + explode(10, true) star_washer(washer) + if(!top) + explode(10,true) ring_terminal(type) nut(nut, true); + else + nut(nut, true); +} diff --git a/vitamins/ring_terminals.scad b/vitamins/ring_terminals.scad new file mode 100644 index 0000000..f63aeaf --- /dev/null +++ b/vitamins/ring_terminals.scad @@ -0,0 +1,34 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Ring terminals +// +// o i l w h t s +// d d e i o h c +// n d l i r +// g t e c e +// t h k w +// h +// +M3_ringterm = ["M3_ringterm", 6, 3, 12, 3, 1.5, 0.2, M3_dome_screw]; + +ring_terminals = [M3_ringterm]; + +use diff --git a/vitamins/rocker.scad b/vitamins/rocker.scad new file mode 100644 index 0000000..e11f43c --- /dev/null +++ b/vitamins/rocker.scad @@ -0,0 +1,92 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Rocket switch. Also used for neon indicator in the same form factor. +// +include <../core.scad> +use + +function rocker_part(type) = type[1]; //! Part description +function rocker_slot_w(type) = type[2]; //! Panel slot width +function rocker_slot_h(type) = type[3]; //! Panel slow height +function rocker_flange_w(type) = type[4]; //! Flange width +function rocker_flange_h(type) = type[5]; //! Flange height +function rocker_flange_t(type) = type[6]; //! Flange thickness +function rocker_width(type) = type[7]; //! Body width +function rocker_height(type) = type[8]; //! Body height +function rocker_depth(type) = type[9]; //! Body depth +function rocker_bezel(type) = type[10]; //! Bezel width +function rocker_pivot(type) = type[11]; //! Pivot distance from the back of the flange +function rocker_button(type) = type[12]; //! How far the button extends from the bezel +function rocker_spades(type) = type[13]; //! Spade types and positions + +module rocker(type) { //! Draw the specified rocker switch + vitamin(str("rocker(", type[0], "): ", rocker_part(type))); + + bezel = rocker_bezel(type); + clearance = 0.2; + rocker_w = rocker_flange_w(type) - 2 * bezel - clearance; + rocker_h = rocker_flange_h(type) - 2 * bezel - clearance; + rocker_r = sqrt(sqr(rocker_h / 2) + sqr(rocker_flange_t(type) - rocker_pivot(type))); + y = rocker_button(type) + rocker_flange_t(type) - rocker_pivot(type); + x = sqrt(sqr(rocker_r) - sqr(y)); + + x2 = rocker_h /2 + x; + y2 = rocker_button(type); + rocker_r2 = (sqr(x2) + sqr(y2)) / (2 * y2); + + explode(30) { + color(grey20) { + linear_extrude(height = rocker_flange_t(type)) + difference() { + rounded_square([rocker_flange_w(type), rocker_flange_h(type)], 0.5); + + square([rocker_w + clearance, rocker_h + clearance], center = true); + } + + translate_z(-rocker_depth(type)) + rounded_rectangle([rocker_width(type), rocker_height(type), rocker_depth(type) + eps], 0.5, center = false); + } + if(rocker_pivot(type)) + color(grey30) + translate_z(rocker_pivot(type)) + rotate([90, 0, 90]) + linear_extrude(height = rocker_w, center = true) + difference() { + circle(rocker_r, $fa = 1); + + translate([rocker_h / 2, rocker_flange_t(type) + rocker_r2 - rocker_pivot(type)]) + circle(rocker_r2, $fa = 1); + + } + + else + color("red") cube([rocker_w, rocker_h, 2 * (rocker_flange_t(type) + rocker_button(type))], center = true); + + for(spade = rocker_spades(type)) + translate([spade[2], spade[3], -rocker_depth(type)]) + rotate([180, 0, spade[4]]) + spade(spade[0], spade[1]); + } +} + +module rocker_hole(type, h = 0) //! Make a hole to accept a rocker switch, by default 2D, set h for 3D + extrude_if(h) + rounded_square([rocker_slot_w(type), rocker_slot_h(type)], 1, center = true); diff --git a/vitamins/rockers.scad b/vitamins/rockers.scad new file mode 100644 index 0000000..b333733 --- /dev/null +++ b/vitamins/rockers.scad @@ -0,0 +1,43 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +include + +small_spades = [[spade4p8, 7.6, 0, 0, 0], + [spade4p8, 7.6, 0, 7.1, 0]]; + +neon_spades = [[spade4p8, 7.3, 0, -7, 0], + [spade4p8, 7.3, 0, 7, 0]]; + +// +// p s s f f f w h d b p b +// a l l l l l i e e e i u +// r o o a a a d i p z v t +// t t t n n n t g t e o t +// g g g h h h l t o +// w h e e e t n +// +// w h t +// +small_rocker = ["small_rocker", "Rocker Switch PRASA1-16F-BB0BW", 13, 19.8, 15, 21, 2, 12.8, 18.5, 11.8, 2.5, -1, 3.8, small_spades]; +neon_indicator = ["neon_indicator", "Neon Indicator H8630FBNAL", 13, 19.8, 15, 21, 2, 12.8, 18.5, 12.5, 2.5, 0, 0.3, neon_spades]; + +rockers = [small_rocker, neon_indicator]; + +use diff --git a/vitamins/rod.scad b/vitamins/rod.scad new file mode 100644 index 0000000..eb4fba5 --- /dev/null +++ b/vitamins/rod.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Steel rods, with optional chamfer. +// +include <../core.scad> + +rod_colour = grey80; + +module rod(d , l) { + vitamin(str("rod(", d, ", ", l, "): Smooth rod ", d, "mm x ", l, "mm")); + + chamfer = d / 10; + color(rod_colour) + hull() { + cylinder(d = d, h = l - 2 * chamfer, center = true); + + cylinder(d = d - 2 * chamfer, h = l, center = true); + } +} diff --git a/vitamins/screw.scad b/vitamins/screw.scad new file mode 100644 index 0000000..1ddc206 --- /dev/null +++ b/vitamins/screw.scad @@ -0,0 +1,257 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Machine screws and wood screws with various head styles. +// +include <../core.scad> + +use +use <../utils/rounded_cylinder.scad> + +function screw_head_type(type) = type[2]; //! Head style hs_cap, hs_pan, hs_cs, hs_hex, hs_grub, hs_cs_cap, hs_dome +function screw_radius(type) = type[3] / 2; //! Nominal radius +function screw_head_radius(type) = type[4] / 2; //! Head radius +function screw_head_height(type) = type[5]; //! Head height +function screw_socket_depth(type) = type[6]; //! Socket or slot depth +function screw_socket_af(type) = type[7]; //! Socket across flats +function screw_max_thread(type) = type[8]; //! Maximum thread length +function screw_washer(type) = type[9]; //! Default washer +function screw_nut(type) = type[10]; //! Default nut +function screw_pilot_hole(type) = type[11]; //! Pilot hole radius for wood screws, tap radius for machine screws +function screw_clearance_radius(type) = type[12]; //! Clearance hole radius +function screw_nut_radius(type) = screw_nut(type) ? nut_radius(screw_nut(type)) : 0; //! Radius of matching nut +function screw_boss_diameter(type) = max(washer_diameter(screw_washer(type)) + 1, 2 * (screw_nut_radius(type) + 3 * extrusion_width)); //! Boss big enough for nut trap and washer +function screw_head_depth(type, d) = screw_head_height(type) ? 0 : screw_head_radius(type) - d / 2; //! How far a counter sink head will go into a straight hole diameter d + +function screw_longer_than(x) = x <= 5 ? 5 : //! Returns shortest screw length longer or equal to x + x <= 8 ? 8 : + x <= 10 ? 10 : + x <= 12 ? 12 : + x <= 16 ? 16 : + ceil(x / 5) * 5; + +function screw_shorter_than(x) = x >= 20 ? floor(x / 5) * 5 : //! Returns longest screw length shorter than or equal to x + x >= 16 ? 16 : + x >= 12 ? 12 : + x >= 10 ? 10 : + x >= 8 ? 8 : + x >= 6 ? 6 : + 5; + +function screw_smaller_than(d) = d >= 2.5 && d < 3 ? 2.5 : floor(d); // Largest diameter screw less than or equal to specified diameter + +module screw(type, length, hob_point = 0, nylon = false) { //! Draw specified screw, optionally hobbed or nylon + description = str("Screw ", nylon ? "Nylon " : "", type[1], " x ", length, "mm", hob_point ? str(", hobbed at ", hob_point) : ""); + vitamin(str("screw(", type[0], "_screw, ", length, arg(hob_point, 0, "hob_point"), arg(nylon, false, "nylon"), "): ", description)); + + head_type = screw_head_type(type); + rad = screw_radius(type) - eps; + head_rad = screw_head_radius(type); + head_height = screw_head_height(type); + socket_af = screw_socket_af(type); + socket_depth= screw_socket_depth(type); + socket_rad = socket_af / cos(30) / 2; + max_thread = screw_max_thread(type); + thread = max_thread ? min(length, max_thread) : length; + shank = length - thread; + colour = nylon || head_type == hs_grub ? grey40 : grey80; + + + module shaft(headless = 0) { + point = screw_nut(type) ? 0 : 3 * rad; + color(colour * 0.9 ) + rotate_extrude() { + translate([0, -length + point]) + square([rad, length - headless - point]); + + if(point) + polygon([ + [0, -length], [0, point - length], [rad - 0.1, point - length] + ]); + } + if(shank >= 5) + color(colour) + translate_z(-shank) + cylinder(r = rad + eps, h = shank - headless); + + } + + explode(length + 10) { + if(head_type == hs_cap) { + color(colour) { + cylinder(r = head_rad, h = head_height - socket_depth); + + translate_z(head_height - socket_depth) + linear_extrude(height = socket_depth) + difference() { + circle(head_rad); + circle(socket_rad, $fn = 6); + } + + } + shaft(); + } + if(head_type == hs_grub) { + color(colour) { + translate_z(-socket_depth) + linear_extrude(height = socket_depth) + difference() { + circle(r = rad); + circle(socket_rad, $fn = 6); + } + + shaft(socket_depth); + } + } + if(head_type == hs_hex) { + color(colour) + cylinder(r = head_rad, h = head_height, $fn = 6); + + shaft(); + } + if(head_type == hs_pan) { + socket_rad = 0.6 * head_rad; + socket_depth = 0.5 * head_height; + socket_width = 1; + color(colour) { + rotate_extrude() + difference() { + rounded_corner(r = head_rad, h = head_height, r2 = head_height / 2); + + translate([0, head_height - socket_depth]) + square([socket_rad, 10]); + } + + linear_extrude(height = head_height) + difference() { + circle(socket_rad + eps); + + square([2 * socket_rad, socket_width], center = true); + square([socket_width, 2 * socket_rad], center = true); + } + } + shaft(); + } + + if(head_type == hs_dome) { + lift = 0.38; + color(colour) { + rotate_extrude() { + difference() { + intersection() { + translate([0, -head_height + lift]) + circle(2 * head_height); + + square([head_rad, head_height]); + } + translate([0, head_height - socket_depth]) + square([socket_rad, 10]); + } + } + linear_extrude(height = head_height) + difference() { + circle(socket_rad + eps); + circle(socket_rad, $fn = 6); + } + } + shaft(); + } + + if(head_type == hs_cs) { + head_height = head_rad; + socket_rad = 0.6 * head_rad; + socket_depth = 0.3 * head_rad; + socket_width = 1; + color(colour) { + rotate_extrude() + difference() { + polygon([[0, 0], [head_rad, 0], [0, -head_height]]); + + translate([0, -socket_depth + eps]) + square([socket_rad + 0.1, 10]); + } + + translate_z(-socket_depth) + linear_extrude(height = socket_depth) + difference() { + circle(socket_rad + 0.1); + + square([2 * socket_rad, socket_width], center = true); + square([socket_width, 2 * socket_rad], center = true); + } + } + shaft(socket_depth); + } + + if(head_type == hs_cs_cap) { + head_height = head_rad; + color(colour) { + rotate_extrude() + difference() { + polygon([[0, 0], [head_rad, 0], [0, -head_height]]); + + translate([0, -socket_depth + eps]) + square([socket_rad, 10]); + } + + translate_z(-socket_depth) + linear_extrude(height = socket_depth) + difference() { + circle(socket_rad + 0.1); + + circle(socket_rad, $fn = 6); + } + } + shaft(socket_depth); + } + } +} + +module screw_countersink(type) { //! Countersink shape + head_type = screw_head_type(type); + head_rad = screw_head_radius(type); + head_height = head_rad; + + if(head_type == hs_cs || head_type == hs_cs_cap) + translate_z(-head_height) + cylinder(h = head_height, r1 = 0, r2 = head_rad); +} + +module screw_and_washer(type, length, star = false, penny = false) { //! Screw with a washer which can be standard or penny and an optional star washer on top + washer = screw_washer(type); + + translate_z(exploded() * 6) + if(penny) + penny_washer(washer); + else + washer(washer); + + translate_z(washer_thickness(washer)) { + if(star) { + translate_z(exploded() * 8) + star_washer(washer); + + translate_z(washer_thickness(washer)) + screw(type, length); + } + else + screw(type, length); + } +} diff --git a/vitamins/screws.scad b/vitamins/screws.scad new file mode 100644 index 0000000..0f69022 --- /dev/null +++ b/vitamins/screws.scad @@ -0,0 +1,131 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Screws +// +include +include + +No2_pilot_radius = 1.7 / 2; // self tapper into ABS +No4_pilot_radius = 2.0 / 2; // wood screw into soft wood +No6_pilot_radius = 2.0 / 2; // wood screw into soft wood +No8_pilot_radius = 2.5 / 2; + +No2_clearance_radius = 2.5 / 2; +No4_clearance_radius = 3.5 / 2; +No6_clearance_radius = 4.0 / 2; +No8_clearance_radius = 4.5 / 2; + +M2_tap_radius = 1.6 / 2; +M2_clearance_radius = 2.4 / 2; + +M2p5_tap_radius = 2.05 / 2; +M2p5_clearance_radius= 2.8 / 2; // M2.5 + +M3_tap_radius = 2.5 / 2; +M3_clearance_radius = 3.3 / 2; + +M4_tap_radius = 3.3 / 2; +M4_clearance_radius = 2.2; + +M5_tap_radius = 4.2 / 2; +M5_clearance_radius = 5.3 / 2; + +M6_tap_radius = 5 / 2; +M6_clearance_radius = 6.4 / 2; + +M8_tap_radius = 6.75 / 2; +M8_clearance_radius = 8.4 / 2; + +// d h d h h s s m +// e e i e e o o a +// s a a a a c c x +// c d m d d k k +// r e e e t +// i t t d h t t h +// p y e i e r +// t p r a i d a e +// i e m g e f a +// o e h p d +// n t t t +// e h +// r +// +M2_cap_screw = ["M2_cap", "M2 cap", hs_cap, 2, 3.8, 2, 1.0, 1.5, 16, M2_washer, M2_nut, M2_tap_radius, M2_clearance_radius]; +M2p5_cap_screw = ["M2p5_cap", "M2.5 cap", hs_cap, 2.5, 4.5, 2.5, 1.1, 2.0, 17,M2p5_washer, M2p5_nut, M2p5_tap_radius, M2p5_clearance_radius]; +M3_cap_screw = ["M3_cap", "M3 cap", hs_cap, 3, 5.5, 3, 1.3, 2.5, 18, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_cap_screw = ["M4_cap", "M4 cap", hs_cap, 4, 7.0, 4, 2.0, 3.0, 20, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; +M5_cap_screw = ["M5_cap", "M5 cap", hs_cap, 5, 8.5, 5, 2.5, 4.0, 22, M5_washer, M5_nut, M5_tap_radius, M5_clearance_radius]; +M6_cap_screw = ["M6_cap", "M6 cap", hs_cap, 6, 10, 6, 3.3, 5.0, 24, M6_washer, M6_nut, M6_tap_radius, M6_clearance_radius]; +M8_cap_screw = ["M8_cap", "M8 cap", hs_cap, 8, 13, 8, 4.3, 6.0, 28, M8_washer, M8_nut, M8_tap_radius, M8_clearance_radius]; + +M2_cs_cap_screw = ["M2_cs_cap","M2 cs cap", hs_cs_cap,2, 3.8, 0, 0.65,1.27,16, M2_washer, M2_nut, M2_tap_radius, M2_clearance_radius]; +M3_cs_cap_screw = ["M3_cs_cap","M3 cs cap", hs_cs_cap,3, 6.0, 0, 1.05,2.0, 18, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_cs_cap_screw = ["M4_cs_cap","M4 cs cap", hs_cs_cap,4, 8.0, 0, 1.49,2.5, 20, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; + +M3_dome_screw = ["M3_dome", "M3 dome", hs_dome, 3, 5.7, 1.65, 1.04,2.0, 18, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_dome_screw = ["M4_dome", "M4 dome", hs_dome, 4, 7.6, 2.2, 1.3, 2.5, 20, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; + +M2p5_pan_screw = ["M2p5_pan", "M2.5 pan", hs_pan, 2.5, 4.7, 1.7, 0, 0, 0, M2p5_washer, M2p5_nut, M2p5_tap_radius, M2p5_clearance_radius]; +M3_pan_screw = ["M3_pan", "M3 pan", hs_pan, 3, 5.4, 2.0, 0, 0, 0, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_pan_screw = ["M4_pan", "M4 pan", hs_pan, 4, 7.8, 3.3, 0, 0, 0, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; +M5_pan_screw = ["M5_pan", "M5 pan", hs_pan, 5, 10, 3.95, 0, 0, 0, M5_washer, M5_nut, M5_tap_radius, M5_clearance_radius]; +M6_pan_screw = ["M6_pan", "M6 pan", hs_pan, 6, 12, 4.75, 0, 0, 0, M6_washer, M6_nut, M6_tap_radius, M6_clearance_radius]; +No632_pan_screw = ["No632_pan", "6-32 pan", hs_pan, 3.5, 6.9, 2.5, 0, 0, 0, M4_washer, false, No6_pilot_radius, No6_clearance_radius]; + +M3_hex_screw = ["M3_hex", "M3 hex", hs_hex, 3, 6.4, 2.125, 0, 0, 0, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_hex_screw = ["M4_hex", "M4 hex", hs_hex, 4, 8.1, 2.925, 0, 0, 0, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; +M5_hex_screw = ["M5_hex", "M5 hex", hs_hex, 5, 9.2, 3.65, 0, 0, 0, M5_washer, M5_nut, M5_tap_radius, M5_clearance_radius]; +M6_hex_screw = ["M6_hex", "M6 hex", hs_hex, 6,11.5, 4.15, 0, 0, 0, M6_washer, M6_nut, M6_tap_radius, M6_clearance_radius]; +M8_hex_screw = ["M8_hex", "M8 hex", hs_hex, 8, 15, 5.65, 0, 0, 22, M8_washer, M8_nut, M8_tap_radius, M8_clearance_radius]; + +M3_low_cap_screw = ["M3_low_cap", "M3 low cap", hs_cap, 3, 5.5, 2, 1.5, 2.0, 18, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; + +M3_grub_screw = ["M3_grub", "M3 grub", hs_grub, 3, 0, 0, 2.5, 1.5, 0, M3_washer, M3_nut, M3_tap_radius, M3_clearance_radius]; +M4_grub_screw = ["M4_grub", "M4 grub", hs_grub, 4, 0, 0, 2.4, 2.5, 0, M4_washer, M4_nut, M4_tap_radius, M4_clearance_radius]; + +No2_screw = ["No2", "No2 pan wood", hs_pan, 2.2, 4.2, 1.7, 0, 0, 0, M2p5_washer, false, No2_pilot_radius, No2_clearance_radius]; +No4_screw = ["No4", "No4 pan wood", hs_pan, 3.0, 5.5, 2.0, 0, 0, 0 ,M3p5_washer, false, No4_pilot_radius, No4_clearance_radius]; +No6_screw = ["No6", "No6 pan wood", hs_pan, 3.5, 6.7, 2.2, 0, 0, 0 , M4_washer, false, No6_pilot_radius, No6_clearance_radius]; +No6_cs_screw = ["No6_cs", "No6 cs wood", hs_cs, 3.5, 7.0, 0, 0, 0, 0, M4_washer, false, No6_pilot_radius, No6_clearance_radius]; + +screw_lists = [ +[ M2_cap_screw, M2p5_cap_screw, M3_cap_screw, M4_cap_screw, M5_cap_screw, M6_cap_screw, M8_cap_screw], +[ 0, 0, M3_low_cap_screw], +[ 0, 0, M3_hex_screw, M4_hex_screw, M5_hex_screw, M6_hex_screw, M8_hex_screw], +[ 0, M2p5_pan_screw, M3_pan_screw, M4_pan_screw, No632_pan_screw, M5_pan_screw, M6_pan_screw], +[ 0, No2_screw, No4_screw, No6_screw, No6_cs_screw], +[ 0, M2_cs_cap_screw,M3_cs_cap_screw, M4_cs_cap_screw], +[ 0, 0, M3_dome_screw, M4_dome_screw], +[ 0, 0, M3_grub_screw, M4_grub_screw] +]; + +use + +screws = [for(list = screw_lists) for(screw = list) screw]; + +function find_screw(type, size, i = 0) = + i >= len(screws) ? undef + : screw_head_type(screws[i]) == type && screw_radius(screws[i]) == size / 2 ? screws[i] + : find_screw(type, size, i + 1); + +function alternate_screw(type, screw) = + let(alt_screw = find_screw(type, screw_radius(screw) * 2)) + alt_screw ? alt_screw :screw; diff --git a/vitamins/sealing_strip.scad b/vitamins/sealing_strip.scad new file mode 100644 index 0000000..f8fb077 --- /dev/null +++ b/vitamins/sealing_strip.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Sealing strip from B&Q used to seal around the door of 3D printers. +// +include <../core.scad> + +use <../utils/rounded_polygon.scad> + +r = 0.5; +h = 4; +h2 = 2; +h3 = 3; +w = 10; +w2 = 7; +w3 = 3; + +profile = [ + [ -w / 2 + r, r, r], + [ -w / 2 + r / 2, 2 * r + 0.1, -0.1], + [-w2 / 2, h - r, r], + [-w3 / 2, h2 + r,-r], + [ 0, h3 - r, r], + [ w3 / 2, h2 + r,-r], + [ w2 / 2, h - r, r], + [ w / 2 - r / 2, 2 * r + 0.1, -0.1], + [ w / 2 - r, r, r], +]; + +module sealing_strip(length) { //! Draw specified length of sealing strip + vitamin(str("sealing_strip(", length, "): Sealing strip 10mm x 4mm x ", length, "mm")); + rotate([90, 0, 90]) + color("Sienna") + linear_extrude(height = length, center = true) + rounded_polygon(profile); +} diff --git a/vitamins/sheet.scad b/vitamins/sheet.scad new file mode 100644 index 0000000..ac7cddc --- /dev/null +++ b/vitamins/sheet.scad @@ -0,0 +1,88 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Sheet materials. Rectangular with optional rounded corners. Negative radii make a chamfer. +//! +//! The "Soft" parameter can be used to determinesif the sheet material needs machine screws or wood screws, e.g.: +//! +//! * If soft, wood screws will be used, with a pilot hole. +//! * If not soft, either tapped holes or a clearance hole and nuts will be used to retain screws. +//! +//! The "Color" parameter is a quad-array: [R, G, B, Alpha], or can be a named color, see [OpenSCAD_User_Manual](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#color). +//! +//! For speed sheets should be modelled in 2D by subtracting holes from 2D templates made by ```sheet_2D()``` and then extruded to 3D with ```render_2D_sheet()```. +//! Note that modules that drill holes will return a 2D object if ```h``` is set to 0 to facilitate this. +// +include <../core.scad> + +function sheet_thickness(type) = type[2]; //! Thickness +function sheet_colour(type) = type[3]; //! Colour +function sheet_is_soft(type) = type[4]; //! Is soft enough for wood screws + +module corner(r) { + if(r > 0) + translate([r, -r]) + circle4n(r); + else + if(r < 0) + translate([-r, r]) + rotate(45) + square(-r * sqrt(2), -r * sqrt(2), center = true); + else + translate([0.5, -0.5]) + square(1, center = true); +} + +module sheet_2D(type, w, d, corners = [0, 0, 0, 0]) { //! 2D sheet template with specified size and optionally rounded corners + t = sheet_thickness(type); + vitamin(str("sheet(", type[0], ", ", w, ", ", d, arg(corners, [0, 0, 0, 0]), "): ", type[1], " ", round(w), "mm x ", round(d), "mm x ", t, "mm")); + + c = is_list(corners) ? corners : corners * [1, 1, 1, 1]; + + hull() { + translate([-w/2, d/2]) + corner(c[0]); + + translate([ w/2, d/2]) + rotate(-90) + corner(c[1]); + + translate([ w/2, -d/2]) + rotate(-180) + corner(c[2]); + + translate([-w/2, -d/2]) + rotate(-270) + corner(c[3]); + } +} + +module sheet(type, w, d, corners = [0, 0, 0, 0]) //! Draw speified sheet + linear_extrude(height = sheet_thickness(type), center = true) + sheet_2D(type, w, d, corners); + +module render_sheet(type, color = false) //! Render a sheet in the correct colour after holes have been subtracted + color(color ? color : sheet_colour(type)) + render() children(); + +module render_2D_sheet(type, color = false) //! Extrude a 2D sheet template and give it the correct colour + color(color ? color : sheet_colour(type)) + linear_extrude(height = sheet_thickness(type), center = true) + children(); diff --git a/vitamins/sheets.scad b/vitamins/sheets.scad new file mode 100644 index 0000000..8600ad1 --- /dev/null +++ b/vitamins/sheets.scad @@ -0,0 +1,50 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Flat sheets +// + +// +// [ Code, Description, Thickness, Color, Soft] +// +mdf_colour = "#BEA587"; // sampled from a photo + +MDF6 = [ "MDF6", "Sheet MDF", 6, mdf_colour, true]; // ~1/4" +MDF10 = [ "MDF10", "Sheet MDF", 10, mdf_colour, true]; // ~3/8" +MDF12 = [ "MDF12", "Sheet MDF", 12, mdf_colour, true]; // ~1/2" +MDF18 = [ "MDF18", "Sheet MDF", 18, mdf_colour, true]; +MDF19 = [ "MDF19", "Sheet MDF", 19, mdf_colour, true]; // ~3/4" +PMMA3 = [ "PMMA3", "Sheet acrylic", 3, [1, 1, 1, 0.5 ], false]; // ~1/8" +PMMA6 = [ "PMMA6", "Sheet acrylic", 6, [1, 1, 1, 0.5 ], false]; // ~1/4" +PMMA8 = [ "PMMA8", "Sheet acrylic", 8, [1, 1, 1, 0.5 ], false]; // ~5/16" +PMMA10 = [ "PMMA10", "Sheet acrylic", 10, [1, 1, 1, 0.5 ], false]; // ~3/8" +glass2 = [ "glass2", "Sheet glass", 2, [1, 1, 1, 0.25 ], false]; +DiBond = [ "DiBond", "Sheet DiBond", 3, [0.2, 0.2, 0.2, 1 ], false]; +DiBond6 = [ "DiBond6", "Sheet DiBond", 6, "RoyalBlue", false]; +Cardboard = [ "Cardboard", "Corrugated cardboard", 5, [0.8, 0.6, 0.3, 1 ], false]; +FoilTape = [ "FoilTape", "Aluminium foil tape", 0.05,[0.9, 0.9, 0.9, 1 ], false]; +Foam20 = [ "Foam20", "Foam sponge", 20,[0.3, 0.3, 0.3, 1 ], true]; +AL6 = [ "AL6", "Aluminium tooling plate", 6, [0.9, 0.9, 0.9, 1 ], false]; +AL8 = [ "AL8", "Aluminium tooling plate", 8, [0.9, 0.9, 0.9, 1 ], false]; +Steel06 = [ "Steel06", "Sheet mild steel", 0.6,"silver" , false]; + +sheets = [MDF6, MDF10, MDF12, MDF19, PMMA3, PMMA6, PMMA8, PMMA10, glass2, DiBond, DiBond6, Cardboard, FoilTape, Foam20, AL6, AL8, Steel06]; + +use diff --git a/vitamins/spade.scad b/vitamins/spade.scad new file mode 100644 index 0000000..9585195 --- /dev/null +++ b/vitamins/spade.scad @@ -0,0 +1,59 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Spade terminals used as parts of electrical components. +// +include <../core.scad> + +function spade_l(type) = type[0]; //! Length of the narrow part +function spade_w(type) = type[1]; //! Width +function spade_t(type) = type[2]; //! Thickness +function spade_hole(type) = type[3]; //! Hole diameter +function spade_shank(type) = type[4]; //! Shank width + +module spade(type, height = 14) { //! Draw a spade of the specified type and total length. The shank length is adjusted to make the length. + w = spade_w(type); + l = spade_l(type); + chamfer = w / 4; + shank_w = spade_shank(type); + shank_l = height - spade_l(type); + + color("silver") + rotate([90, 0, 0]) + linear_extrude(height = spade_t(type), center = true) + difference() { + union() { + if(shank_l > 0) + translate([-shank_w / 2, 0]) + square([shank_w, shank_l]); + + translate([0, shank_l]) + hull() { + translate([- w/ 2, 0]) + square([w, l - chamfer]); + + translate([- (w - chamfer) / 2, 0]) + square([w - chamfer, l]); + } + } + translate([0, shank_l + l / 2]) + circle(d = spade_hole(type)); + } +} diff --git a/vitamins/spades.scad b/vitamins/spades.scad new file mode 100644 index 0000000..5789a9f --- /dev/null +++ b/vitamins/spades.scad @@ -0,0 +1,28 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +spade3 = [3.8, 3.0, 0.4, 1.0, 3.9]; +spade6p4 = [8.3, 6.4, 0.8, 1.8, 8]; +spade4p8 = [6.4, 4.8, 0.5, 1.8, 8]; +spade4p8l = [7.0, 4.8, 0.5, 1.8, 8]; +spade4p8ll = [8.0, 4.8, 0.5, 1.8, 4.8]; + +spades = [spade3, spade6p4, spade4p8, spade4p8l, spade4p8ll]; + +use diff --git a/vitamins/spool.scad b/vitamins/spool.scad new file mode 100644 index 0000000..d820900 --- /dev/null +++ b/vitamins/spool.scad @@ -0,0 +1,59 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +//! Filament spool models + +include <../core.scad> + +function spool_diameter(type) = type[1]; //! Outside diameter +function spool_width(type) = type[2]; //! Internal width +function spool_depth(type) = type[3]; //! Internal depth +function spool_rim_thickness(type) = type[4]; //! Thickness at the outside rim +function spool_hub_thickness(type) = type[5]; //! Thickness at the hub +function spool_hub_bore(type) = type[6]; //! Bore through the hub +function spool_hub_diameter(type) = type[7]; //! Diameter of the thicker hub +function spool_hub_taper(type) = type[8]; //! Diameter at which it tapers down to rim thickness +function spool_height(type) = spool_width(type) + 2 * spool_hub_thickness(type); //! Outside width + +module spool(type) { //! Draw specified spool + vitamin(str("spool(", type[0], "): Filament spool ", spool_diameter(type), " x ", spool_width(type))); + + h = spool_height(type); + r1 = spool_hub_bore(type) / 2; + r2 = spool_hub_diameter(type) / 2; + r3 = spool_hub_taper(type) / 2; + r4 = spool_diameter(type) / 2; + r5 = r4 - spool_depth(type); + + color([0.2, 0.2, 0.2]) translate_z(-h / 2) rotate_extrude(convexity = 5) + polygon([ + [r1, h], + [r1, 0], + [r2, 0], + [r3, spool_hub_thickness(type) - spool_rim_thickness(type)], + [r4, spool_hub_thickness(type) - spool_rim_thickness(type)], + [r4, spool_hub_thickness(type)], + [r5, spool_hub_thickness(type)], + [r5, h - spool_hub_thickness(type)], + [r4, h - spool_hub_thickness(type)], + [r4, h - spool_hub_thickness(type) + spool_rim_thickness(type)], + [r3, h - spool_hub_thickness(type) + spool_rim_thickness(type)], + [r2, h], + ]); +} diff --git a/vitamins/spools.scad b/vitamins/spools.scad new file mode 100644 index 0000000..59551d9 --- /dev/null +++ b/vitamins/spools.scad @@ -0,0 +1,32 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// Filament spool models + +// d w d r h b h h +// i i e i u o u u +// a d p m b r b b +// t t e +// h h t t d t +spool_300x85 = ["spool_300x85", 300, 85, 60, 4, 8, 52, 250, 280]; +spool_200x55 = ["spool_200x55", 200, 55, 40, 5, 5, 52, 200, 200]; + +spools = [spool_200x55, spool_300x85]; + +use diff --git a/vitamins/spring.scad b/vitamins/spring.scad new file mode 100644 index 0000000..a3a99b4 --- /dev/null +++ b/vitamins/spring.scad @@ -0,0 +1,92 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Compression springs. Can be tapered, have open, closed or ground ends. Ground ends will render a lot slower. +// +include <../core.scad> + +use <../utils/sweep.scad> + +function spring_od(type) = type[1]; //! Outside diameter +function spring_gauge(type) = type[2]; //! Wire gauge +function spring_length(type) = type[3]; //! Uncompressed length +function spring_turns(type) = type[4]; //! Number of turns +function spring_closed(type) = type[5]; //! Are the ends closed +function spring_ground(type) = type[6]; //! Are the ends ground flat +function spring_od2(type) = type[7] ? type[7] : spring_od(type); //! Second diameter for spiral springs +function spring_colour(type) = type[8]; //! The colour +function spring_mesh(type) = type[9]; //! Optional pre-computed mesh + +function comp_spring(type, l = 0) = //! Calculate the mesh for spring + let(wire_d = spring_gauge(type), + r1 = (spring_od(type) - wire_d) / 2, + r2 = (spring_od2(type) - wire_d) / 2, + h = l ? l : spring_length(type), + H = spring_ground(type) ? h + wire_d * 0.75 : h - wire_d, + turns = spring_turns(type), + closed = spring_closed(type), + open = turns - 2 * closed, + open_H = H - 2 * closed * wire_d, + offset = (h - H) / 2, + sides = r2sides(r1), + path = [for(i = [0 : sides * turns], + t = i / sides, + top = t > turns / 2, + u = top ? turns - t : t, + v = (u < closed ? u < 0.25 ? 0 + : u < 1 ? wire_d * (u - 0.25) / 0.75 + : wire_d * u + : wire_d * closed + open_H / open * (u - closed) + ) + offset, + z = top ? h - v : v, + r = t <= closed ? r2 + : t >= turns - closed ? r1 + : r2 + (r1 - r2) * (t - closed) / open, + a = t * 360 + ) [r * sin(a), -r * cos(a), z]], + profile = circle_points(wire_d / 2 - eps, $fn = 16) + ) concat(type, [concat(sweep(path, profile), [l])]); + +module comp_spring(type, l = 0) { //! Draw specified spring, l can be set to specify the compressed length. + length = spring_length(type); + closed = spring_closed(type); + od = spring_od(type); + od2 = spring_od2(type); + gauge = spring_gauge(type); + ground = spring_ground(type); + vitamin(str("comp_spring(", type[0], arg(l, 0), + "): Spring ", od, od != od2 ? str(" - ", od2, "mm spiral") : "mm", " OD, ", gauge, "mm gauge x ", + length, "mm long, ", + closed ? "closed" : "open", + ground ? " ground" : "", " end" )); + + mesh = len(type) > 9 ? spring_mesh(type) : spring_mesh(comp_spring(type, l)); + assert(l == mesh[2], "can't change the length of a pre-generated spring"); + color(spring_colour(type)) + if(ground) + clip(zmin = 0, zmax = h) + polyhedron(mesh[0], mesh[1]); + else + polyhedron(mesh[0], mesh[1]); + + if($children) + translate_z(l) + children(); +} diff --git a/vitamins/springs.scad b/vitamins/springs.scad new file mode 100644 index 0000000..2972c24 --- /dev/null +++ b/vitamins/springs.scad @@ -0,0 +1,25 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +peg_spring = ["peg_spring", 6.4, 0.9, 15.5, 8, 1, false, 0, "silver"]; +batt_spring = ["batt_spring", 5, 0.5, 8, 5, 1, false, 6, "silver"]; + +springs = [peg_spring, batt_spring]; + +use diff --git a/vitamins/ssr.scad b/vitamins/ssr.scad new file mode 100644 index 0000000..4d8d190 --- /dev/null +++ b/vitamins/ssr.scad @@ -0,0 +1,88 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Solid state relays. +// +include <../core.scad> + +function ssr_part(type) = type[1]; //! Description +function ssr_length(type) = type[2]; //! Length +function ssr_width(type) = type[3]; //! Width +function ssr_height(type) = type[4]; //! Height +function ssr_base_t(type) = type[5]; //! Thickness of metal base +function ssr_hole_d(type) = type[6]; //! Screw hole diameter +function ssr_hole_pitch(type) = type[7]; //! Difference between screw centres +function ssr_slot_w(type) = type[8]; //! Width of the screw slot in the body + +module ssr_hole_positions(type) //! Place children at the screw positions + for(end = [-1, 1]) + translate([end * ssr_hole_pitch(type) / 2, 0]) + children(); + +module ssr(type) { //! Draw specified SSR + vitamin(str("ssr(", type[0], "): Solid state relay ", ssr_part(type))); + + l = ssr_length(type); + w = ssr_width(type); + t = ssr_base_t(type); + h = ssr_height(type); + + color("silver") linear_extrude(height = t) difference() { + square([l, w], center = true); + + ssr_hole_positions(type) + circle(d = ssr_hole_d(type)); + } + color([242/255, 236/255, 220/255]) + translate_z(t) + linear_extrude(height = h - t) difference() { + square([l, w], center = true); + + for(end = [-1, 1]) + hull() { + translate([end * ssr_hole_pitch(type) / 2, 0]) + circle(d = ssr_slot_w(type)); + + translate([end * ssr_hole_pitch(type), 0]) + circle(d = ssr_slot_w(type)); + } + } +} + +use +use +use + +module ssr_assembly(type, screw, thickness) { //! Assembly with fasteners in place + nut = screw_nut(screw); + washer = screw_washer(screw); + screw_length = screw_longer_than(2 * washer_thickness(washer) + thickness + ssr_base_t(type) + nut_thickness(nut, true)); + + ssr(type); + + ssr_hole_positions(type) { + translate_z(ssr_base_t(type)) + nut_and_washer(nut, true); + + translate_z(-thickness) + vflip() + screw_and_washer(screw, screw_length); + } +} diff --git a/vitamins/ssrs.scad b/vitamins/ssrs.scad new file mode 100644 index 0000000..858486d --- /dev/null +++ b/vitamins/ssrs.scad @@ -0,0 +1,33 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// p l w h b h p s +// a e i e a o i l +// r n d i s l t o +// t g t g e e c t +// t h h h +// h t d w +// +SSR10DA = [ "SSR10DA", "Robodigg 10A", 58, 45, 33, 2.5, 5, 47, 9 ]; +SSR25DA = [ "SSR25DA", "Fotek 25A", 63, 45, 23, 4.6, 5, 47, 10 ]; + +ssrs = [SSR25DA, SSR10DA]; + +use diff --git a/vitamins/stepper_motor.scad b/vitamins/stepper_motor.scad new file mode 100644 index 0000000..616e547 --- /dev/null +++ b/vitamins/stepper_motor.scad @@ -0,0 +1,123 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! NEMA stepper motor model. +// +include <../core.scad> + +include +use +include +use <../utils/tube.scad> + +function NEMA_width(type) = type[1]; //! Width of the square face +function NEMA_length(type) = type[2]; //! Body length +function NEMA_radius(type) = type[3]; //! End cap radius +function NEMA_body_radius(type) = type[4]; //! Body radius +function NEMA_boss_radius(type) = type[5]; //! Boss around the spindle radius +function NEMA_boss_height(type) = type[6]; //! Boss height +function NEMA_shaft_dia(type) = type[7]; //! Shaft diameter +function NEMA_shaft_length(type)= type[8]; //! Shaft length above the face +function NEMA_hole_pitch(type) = type[9]; //! Screw hole pitch +function NEMA_holes(type) = [-NEMA_hole_pitch(type) / 2, NEMA_hole_pitch(type) / 2]; //! Screw positions for for loop +function NEMA_big_hole(type) = NEMA_boss_radius(type) + 0.2; //! Clearance hole for the big boss + +stepper_body_colour = "black"; +stepper_cap_colour = grey50; + +module NEMA_outline(type) //! 2D outline + intersection() { + side = NEMA_width(type); + square([side, side], center = true); + + circle(NEMA_radius(type)); + } + +module NEMA(type) { //! Draw specified NEMA stepper motor + side = NEMA_width(type); + length = NEMA_length(type); + body_rad = NEMA_body_radius(type); + boss_rad = NEMA_boss_radius(type); + boss_height =NEMA_boss_height(type); + shaft_rad = NEMA_shaft_dia(type) / 2; + cap = 8; + vitamin(str("NEMA(", type[0], "): Stepper motor NEMA", round(NEMA_width(type) / 2.54), " x ", length, "mm")); + + union() { + color(stepper_body_colour) // black laminations + translate_z(-length / 2) + linear_extrude(height = length - cap * 2, center = true) + intersection() { + square([side, side], center = true); + + circle(body_rad); + } + + color(stepper_cap_colour) { // aluminium end caps + tube(or = boss_rad, ir = shaft_rad + 2, h = boss_height * 2); // raised boss + + for(end = [-1, 1]) + translate_z(-length / 2 + end * (length - cap) / 2) + linear_extrude(height = cap, center = true) + difference() { + intersection() { + square([side, side], center = true); + circle(NEMA_radius(type)); + } + if(end > 0) + for(x = NEMA_holes(type), y = NEMA_holes(type)) + translate([x, y]) + circle(r = 3/2); + } + } + + color(NEMA_shaft_length(type) > 50 ? "silver" : stepper_cap_colour) + translate_z(-5) + cylinder(r = shaft_rad, h = NEMA_shaft_length(type) + 5); // shaft + + translate([0, side / 2, -length + cap / 2]) + rotate([90, 0, 0]) + for(i = [0 : 3]) + rotate(225 + i * 90) + color(["red", "blue","green","black"][i]) + translate([1, 0, 0]) + cylinder(r = 1.5 / 2, h = 12, center = true); + } +} + +module NEMA_screw_positions(type, n = 4) { //! Positions children at the screw holes + pitch = NEMA_hole_pitch(type); + + for($i = [0 : n - 1]) + rotate($i * 90) + translate([pitch / 2, pitch / 2]) + rotate($i * -90) + children(); +} + +module NEMA_screws(type, screw, n = 4, screw_length = 8, earth = undef) //! Place screws and optional earth tag + NEMA_screw_positions(type, n) + if($i != earth) + screw_and_washer(screw, screw_length, true); + else + rotate($i > 1 ? 180 : 0) + ring_terminal(M3_ringterm) + star_washer(screw_washer(screw)) + screw(screw, screw_length); diff --git a/vitamins/stepper_motors.scad b/vitamins/stepper_motors.scad new file mode 100644 index 0000000..1a233b1 --- /dev/null +++ b/vitamins/stepper_motors.scad @@ -0,0 +1,36 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// NEMA stepper motor model +// + +// corner body boss boss shaft +// side, length, radius, radius, radius, depth, shaft, length, holes +NEMA17 = ["NEMA17", 42.3, 47, 53.6/2, 25, 11, 2, 5, 24, 31 ]; +NEMA17M = ["NEMA17M", 42.3, 40, 53.6/2, 25, 11, 2, 5, 20, 31 ]; +NEMA17M8= ["NEMA17M8", 42.3, 40, 53.6/2, 25, 11, 2, 8, 280, 31 ]; +NEMA17S = ["NEMA17S", 42.3, 34, 53.6/2, 25, 11, 2, 5, 24, 31 ]; +NEMA16 = ["NEMA16", 39.5, 19.2, 50.6/2, 50.6/2, 11, 2, 5, 12, 31 ]; +NEMA14 = ["NEMA14", 35.2, 36, 46.4/2, 21, 11, 2, 5, 21, 26 ]; +NEMA23 = ["NEMA23", 56.4, 51.2, 75.7/2, 35, 38.1/2, 1.6, 6.35, 24, 47.1 ]; + +stepper_motors = [NEMA14, NEMA16, NEMA17S, NEMA17M, NEMA17, NEMA23]; + +use diff --git a/vitamins/toggle.scad b/vitamins/toggle.scad new file mode 100644 index 0000000..f531f9c --- /dev/null +++ b/vitamins/toggle.scad @@ -0,0 +1,157 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Toggle switches +// +include <../core.scad> +use +use + +function toggle_part(type) = type[1]; //! Part description +function toggle_width(type) = type[2]; //! Body width +function toggle_height(type) = type[3]; //! Body height +function toggle_depth(type) = type[4]; //! Body depth +function toggle_thickness(type)= type[5]; //! Metal thickness +function toggle_inset(type) = type[6]; //! How far the metal is inset into the body +function toggle_colour(type) = type[7]; //! Body colour +function toggle_od(type) = type[8]; //! Barrel outside diameter +function toggle_id(type) = type[9]; //! Barrel inside diameter +function toggle_thread(type) = type[10]; //! Length of threaded barrel +function toggle_collar_d(type) = type[11]; //! Collar diameter +function toggle_collar_t(type) = type[12]; //! Collar thickness +function toggle_pivot(type) = type[13]; //! Z offset of the pivot point above the top of the body +function toggle_angle(type) = type[14]; //! Angle of the paddle +function toggle_paddle_l(type) = type[15]; //! Length of the paddle +function toggle_paddle_d1(type)= type[16]; //! Diameter at the top of the paddle +function toggle_paddle_w(type) = type[17]; //! Width at the top for non-spherical end +function toggle_nut(type) = type[18]; //! Nut type +function toggle_washer(type) = type[19]; //! Washer type +function toggle_pins(type) = type[20]; //! Number of pins +function toggle_pin_l(type) = type[21][0]; //! Pin length +function toggle_pin_w(type) = type[21][1]; //! Pin width +function toggle_pin_t(type) = type[21][2]; //! Pin thickness +function toggle_pin_vp(type) = type[21][3]; //! Pin y pitch +function toggle_pin_hp(type) = type[21][4]; //! Pin x pitch + +function toggle_hole_radius(type) = toggle_od(type) / 2 + 0.1; //! Radius of the panel hole + +module toggle(type, thickness) { //! Draw specified toggle switch with the nuts and washers positioned for the specified panel thickness + vitamin(str("toggle(", type[0], ", 3): Toggle switch ", toggle_part(type))); + + inset = toggle_inset(type); + t = toggle_thickness(type); + h1 = toggle_depth(type) - t; + h2 = toggle_depth(type) - inset; + t2 = (toggle_od(type) - toggle_id(type)) / 2; + nut = toggle_nut(type); + washer = toggle_washer(type); + chamfer = t2 / 2; + thread = toggle_thread(type); + stack = chamfer + nut_thickness(nut) + 3 * washer_thickness(washer) + thickness + toggle_collar_t(type); + back_nut = thread - stack > nut_thickness(nut); + gap = back_nut ? thread - stack - nut_thickness(nut) : 0; + + module stack() + washer(washer) + translate_z(thickness) + explode(20, true) washer(washer) + explode(5, true) star_washer(washer) + explode(5) nut(nut); + + translate_z(-washer_thickness(washer) - (back_nut ? nut_thickness(nut) : 0) - gap - toggle_collar_t(type)) { + color(toggle_colour(type)) + translate_z(-h1 / 2 - t) + cube([toggle_width(type), toggle_height(type), h1], center = true); + + color("silver") { + if(toggle_collar_t(type)) + cylinder(d = toggle_collar_d(type), h = toggle_collar_t(type)); + + translate_z(-t / 2) + cube([toggle_width(type), toggle_height(type), t], center = true); + + translate_z(-h2 / 2) + cube([toggle_width(type) + 2 * eps, toggle_height(type) - 2 * inset, h2], center = true); + + rotate_extrude() + difference() { + hull() { + square([toggle_od(type) / 2, thread - chamfer]); + + square([toggle_od(type) / 2 - chamfer, thread]); + } + square([toggle_id(type) / 2, thread + 1]); + } + + translate_z(toggle_pivot(type)) { + angle = toggle_angle(type); + l1 = toggle_paddle_l(type); + l2 = toggle_thread(type) - toggle_pivot(type); + l = l1 + l2; + sphere(d = toggle_id(type)); + + d1 = toggle_paddle_d1(type); + d2 = toggle_id(type) - 2 * l2 * tan(abs(angle)); + d3 = d2 - (d1 - d2) * l2 / l1; + + hull() { + rotate([max(angle, 0), 0, 0]) + if(toggle_paddle_w(type)) + translate_z(l) + linear_extrude(height = eps, center = true) + intersection() { + circle(d = d1); + + square([d1 + 1, toggle_paddle_w(type)], center = true); + } + else + translate_z(l - d1 / 2) sphere(d = d1); + + rotate([max(angle, 0), 0, 0]) + translate_z(l2) + cylinder(d = d2, h = eps); + + sphere(d = d3); + } + } + } + pins = toggle_pins(type); + rows = pins > 3 ? 2 : 1; + color("gold") + translate_z(-toggle_depth(type) - toggle_pin_l(type) / 2) + linear_extrude(height = toggle_pin_l(type), center = true) + for(i = [0 : pins - 1]) { + x = rows < 2 ? 0 : (i % 2) - 0.5; + y = rows < 2 ? i - 1 : floor(i / 2) - 1; + translate([x * toggle_pin_hp(type), y * toggle_pin_vp(type)]) + square([toggle_pin_w(type), toggle_pin_t(type)], center = true); + } + + not_on_bom() + translate_z(gap + toggle_collar_t(type)) + if(back_nut) + nut(nut) stack(); + else + stack(); + } +} + +module toggle_hole(type, h = 100) //! Drill the hole in a panel + drill(toggle_hole_radius(type), h); diff --git a/vitamins/toggles.scad b/vitamins/toggles.scad new file mode 100644 index 0000000..2112990 --- /dev/null +++ b/vitamins/toggles.scad @@ -0,0 +1,43 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// +// p +// a +// r +// t +// +// +// l w t vp hp +CK7000_tag = [3.94, 2.03, 0.76, 4.7, 4.83]; +CK7000_pin = [6.1, 1.5, 0.76, 4.7, 4.83]; +AP5236_pin = [5.2 , 1.0, 0.8, 4.7, 4.83]; +MS332F_pin = [4.0 , 1.0, 1.0, 4.7, 4.83]; + +// w l d t i c od id thread pv a tl pd pw +CK7101 = ["CK7101", "CK7101", 6.86, 12.7, 8.89, 0.4, 1.7, "red", 6.1, 4, 8.89, 0, 0, 4.45, 25/2, 10.67, 2.92, 0, toggle_nut, toggle_washer, 3, CK7000_tag]; +CK7105 = ["CK7105", "CK7101", 6.86, 12.7, 8.89, 0.4, 1.7, "red", 6.1, 4, 8.89, 0, 0, 4.45,-25/2, 21.33, 5.08, 2.54,toggle_nut, toggle_washer, 3, CK7000_pin]; + +AP5236 = ["AP5236", "AP5236", 7.0, 13.6, 11, 0.4, 1.7, "blue",6.1, 4, 8.0 , 8, 1, 3.0 , 25/2, 22.2, 4.90, 2.50,toggle_nut, toggle_washer, 3, AP5236_pin]; +MS332F = ["MS332F", "MS332F", 12.6, 13.1, 9.5, 0.4, 1.7, "blue",6.1, 4, 8.0 , 8, 1, 4.0 , 25/2, 14.4, 5.0, 2.2 ,toggle_nut, toggle_washer, 6, MS332F_pin]; + +toggles = [CK7101, CK7105, AP5236, MS332F]; + +use diff --git a/vitamins/transformer.scad b/vitamins/transformer.scad new file mode 100644 index 0000000..b448577 --- /dev/null +++ b/vitamins/transformer.scad @@ -0,0 +1,76 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//!Iron core transformers. The grey shaded area is the keep out region where the terminals are. +// +include <../core.scad> +use + +function tx_part(type) = type[1]; //! Part description +function tx_width(type) = type[2]; //! Bounding width of the core +function tx_depth(type) = type[3]; //! Bounding depth of the bobbin +function tx_height(type) = type[4]; //! Bounding height of the transformer +function tx_x_pitch(type) = type[5]; //! Screw hole x pitch +function tx_y_pitch(type) = type[6]; //! Screw hole y pitch when four screws +function tx_screw(type) = type[7]; //! Screw type +function tx_foot_thickness(type) = type[8]; //! Thickness of the foot +function tx_foot_width(type) = type[9]; //! Width of the foot +function tx_foot_depth(type) = type[10]; //! Depth of the foot +function tx_lamination_depth(type) = type[11]; //! Lamination depth +function tx_lamination_height(type) = type[12]; //! Lamination height +function tx_bobbin_offset(type) = type[13]; //! Vertical offset of the bobbin from the centre of the laminations +function tx_bobbin_width(type) = type[14]; //! Bobbin width +function tx_bobbin_height(type) = type[15]; //! Bobbin height +function tx_bobbin_radius(type) = type[16]; //! Bobbin corner radius + +module transformer(type) { //! Draw specified transformer + vitamin(str("transformer(", type[0], "): Transformer ", tx_part(type))); + + color("silver") { + linear_extrude(height = tx_foot_thickness(type)) + difference() { + rounded_square([tx_foot_width(type), tx_foot_depth(type)], r = 2); + + transformer_hole_positions(type) + circle(screw_clearance_radius(tx_screw(type))); + } + translate_z(tx_lamination_height(type) / 2) + cube([tx_width(type), tx_lamination_depth(type), tx_lamination_height(type)], center = true); + + } + + color("white") { + translate_z(tx_lamination_height(type) / 2 + tx_bobbin_offset(type) / 2) + rounded_rectangle([tx_bobbin_width(type), tx_depth(type), tx_bobbin_height(type)], r = tx_bobbin_radius(type)); + } + terminal_height = tx_height(type) - tx_lamination_height(type); + + if(terminal_height) + %translate_z(tx_lamination_height(type) + terminal_height / 2) union() { + cube([tx_width(type), tx_lamination_depth(type), terminal_height], center = true); + + cube([tx_bobbin_width(type), tx_depth(type), terminal_height], center = true); + } +} + +module transformer_hole_positions(type) //! Position children at the mounting hole positions + for(x = [-1, 1], y = tx_y_pitch(type) ? [-1, 1] : 0) + translate([x * tx_x_pitch(type) / 2, y * tx_y_pitch(type) / 2]) + children(); diff --git a/vitamins/transformers.scad b/vitamins/transformers.scad new file mode 100644 index 0000000..5f83717 --- /dev/null +++ b/vitamins/transformers.scad @@ -0,0 +1,37 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// w d h x y s f f f l l b b b b +// i e e p p c o o o a a o o o o +// d p i i i r o o o m m b b b b +// t t g t t e t t t +// h h h c c w d h o w h r +// t h h t w d e e f i e a +// h i e p i f d i d +// i d p t g s t g i +// c t t h h e h h u +// k h h t t t s +// +CCM300 = ["CCM300", "Carroll & Meynell CCM300/230 isolation", 120, 88, 120, 90, 68, M5_pan_screw, 2, 111, 86, 42, 106, 6, 79, 62, 8]; +SMALLTX= ["SMALLTX", "Small mains", 38, 32, 33, 47, 0, M3_pan_screw,.8, 58, 12, 16, 33, 0, 25, 20, 2]; + +transformers = [SMALLTX, CCM300]; + +use diff --git a/vitamins/tubing.scad b/vitamins/tubing.scad new file mode 100644 index 0000000..6065aec --- /dev/null +++ b/vitamins/tubing.scad @@ -0,0 +1,45 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Tubing and sleeving. The internal diameter can be forced to stretch it over something. +// +include <../core.scad> + +function tubing_material(type) = type[1]; //! Material description +function tubing_od(type) = type[2]; //! Outside diameter +function tubing_id(type) = type[3]; //! Inside diameter +function tubing_colour(type) = type[4]; //! Colour + +module tubing(type, length = 15, forced_id = 0) { //! Draw specified tubing with optional forced internal diameter + original_od = tubing_od(type); + original_id = tubing_id(type); + id = forced_id ? forced_id : original_id; + od = original_od + id - original_id; + if(tubing_material(type) == "Heatshrink sleeving") + vitamin(str("tubing(", type[0], arg(length, 15), "): ", tubing_material(type), " ID ", original_id, "mm x ",length, "mm")); + else + vitamin(str("tubing(", type[0], arg(length, 15), "): ", tubing_material(type), " OD ", original_od, "mm ID ", original_id,"mm x ",length, "mm")); + color(tubing_colour(type)) + linear_extrude(height = length, center = true, convexity = 3) + difference() { + circle(d = od); + circle(d = id); + } +} diff --git a/vitamins/tubings.scad b/vitamins/tubings.scad new file mode 100644 index 0000000..68e004a --- /dev/null +++ b/vitamins/tubings.scad @@ -0,0 +1,39 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Tubing and sleeving +// +tubing_colour = [0.8, 0.8, 0.8, 0.75 ]; + +PVC64 = ["PVC64", "PVC aquarium tubing", 6, 4, tubing_colour]; +PVC85 = ["PVC85", "PVC aquarium tubing", 8, 5, tubing_colour]; +NEOP85 = ["NEOP85", "Neoprene tubing", 8, 5, [0.2,0.2,0.2]]; +PTFE07 = ["PTFE07", "PTFE sleeving", 1.2, 0.71, tubing_colour]; +PTFE20 = ["PTFE20", "PTFE sleeving", 2.6, 2, tubing_colour]; +PF7 = ["PF7", "PTFE tubing", 46/10, 3.84, tubing_colour]; +HSHRNK16 = ["HSHRNK16", "Heatshrink sleeving", 2.0, 1.6, "grey"]; +HSHRNK24 = ["HSHRNK24", "Heatshrink sleeving", 2.8, 2.4, "grey"]; +HSHRNK32 = ["HSHRNK32", "Heatshrink sleeving", 3.6, 3.2, "grey"]; +HSHRNK64 = ["HSHRNK64", "Heatshrink sleeving", 6.8, 6.4, "grey"]; +HSHRNK100 = ["HSHRNK100", "Heatshrink sleeving",10.4, 10.0, [0.2,0.2,0.2]]; + +tubings = [PVC64, PVC85, NEOP85, PTFE07, PTFE20, PF7, HSHRNK16, HSHRNK24, HSHRNK64, HSHRNK100]; + +use diff --git a/vitamins/variac.scad b/vitamins/variac.scad new file mode 100644 index 0000000..af8632e --- /dev/null +++ b/vitamins/variac.scad @@ -0,0 +1,108 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Variable auto transformers. +// +include <../core.scad> +use + +function variac_diameter(type) = type[2]; //! Body diameter +function variac_height(type) = type[3]; //! Body height +function variac_bulge_dia(type) = type[4]; //! Bulge to opposite edge +function variac_bulge_width(type) = type[5]; //! Width of the bulge +function variac_shaft_dia(type) = type[6]; //! Shaft diameter +function variac_shaft_length(type) = type[7]; //! Shaft length +function variac_screws(type) = type[8]; //! Number of screws +function variac_screw_pitch(type) = type[9]; //! Pitch of screws +function variac_screw(type) = type[10]; //! Screw type +function variac_dial_dia(type) = type[11]; //! Dial diameter +function variac_dial_thickness(type) = type[12]; //! Dial thickness +function variac_dial_hole_pitch(type)= type[13] ? type[13] : variac_screw_pitch(type); //! Screw pitch for the dial +function variac_dial_hole_r(type) = type[14] ? type[14] : screw_clearance_radius(variac_screw(type)); //! Dial screw hole radius +function variac_dial_big_hole(type) = type[15] ? type[15] : variac_shaft_dia(type) + 2; //! Central dial hole diameter + +function variac_radius(type) = variac_diameter(type) / 2; //! Body radius + +module variac_hole_positions(type, pitch = undef) //! Position children at the screw positions + for(i = [0 : variac_screws(type) - 1]) + rotate(360 * (i + 0.5) / variac_screws(type) - 90) + translate([pitch ? pitch : variac_screw_pitch(type), 0]) + children(); + +module variac_holes(type, h = 100) { //! Drill panel holes for specified variac + variac_hole_positions(type) + drill(screw_clearance_radius(variac_screw(type)), h); + + drill(variac_shaft_dia(type) / 2 + 1, h); +} + +module variac_dial(type) //! Draw the dial for the specified variac + color("silver") linear_extrude(height = variac_dial_thickness(type)) + difference() { + circle(d = variac_dial_dia(type)); + + circle(variac_dial_big_hole(type) / 2); + + variac_hole_positions(type, variac_dial_hole_pitch(type)) + circle(variac_dial_hole_r(type)); + } + +module variac(type, thickness = 3, dial = true) { //! Draw the specified variac with screws and possibly the dial when it is fixed and not rotating + vitamin(str("variac(", type[0], ", 3): Variac ", type[1])); + dia = variac_diameter(type); + w = variac_bulge_width(type); + h = variac_height(type); + + bulge_r = variac_bulge_dia(type) - dia / 2; + + module shape() { + circle(d = dia); + + translate([-w / 2, -bulge_r]) + square([w, bulge_r]); + } + + translate_z(-h) { + color("#A66955") { + linear_extrude(height = h) + difference() { + shape(); + + variac_hole_positions(type) + circle(screw_radius(variac_screw(type))); + } + + linear_extrude(height = h - 10) + shape(); + } + color("silver") + translate_z(1) + cylinder(d = variac_shaft_dia(type), h = h + variac_shaft_length(type) - 1); + + if(dial) + translate_z(thickness + h) + variac_dial(type); + + translate_z(thickness + h + (dial ? variac_dial_thickness(type) : 0)) + not_on_bom() + variac_hole_positions(type) + screw_and_washer(variac_screw(type), 16); + } +} diff --git a/vitamins/variacs.scad b/vitamins/variacs.scad new file mode 100644 index 0000000..f0e698e --- /dev/null +++ b/vitamins/variacs.scad @@ -0,0 +1,36 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// +// d h b b s s s p s d d +// i e u u h h c i c i i +// a i l l a a r t r a a +// m g g g f f e c e l l +// e h e e t t w h w +// t s d t +// e d w d l i h +// r a k +// +RAVISTAT1F1 = ["RAVISTAT1F1", "RAVISTAT 1F-1", 85, 75, 105, 32, 6, 22, 2, 14, M4_pan_screw, 65, 0.5]; +DURATRAKV5HM = ["DURATRAKV5HM", "DURATRAK V5HM", 125, 120, 148, 49, 9.6, 25, 3, 38.1,M5_pan_screw, 104, 1.66, 15.875, 2, 22]; + +variacs = [RAVISTAT1F1, DURATRAKV5HM]; + +use diff --git a/vitamins/veroboard.scad b/vitamins/veroboard.scad new file mode 100644 index 0000000..9b62ca9 --- /dev/null +++ b/vitamins/veroboard.scad @@ -0,0 +1,175 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Veroboard with mounting holes, track breaks, removed tracks, solder points and components. +// +include <../core.scad> +use +include <../vitamins/screws.scad> + +function vero_assembly(type) = type[1]; //! Name of the assembly +function vero_holes(type) = type[2]; //! Number of holes in each strip +function vero_strips(type) = type[3]; //! Number of strips +function vero_pitch(type) = type[4]; //! Hole pitch +function vero_fr4(type) = type[5]; //! True if FR4 rather than SRBP +function vero_screw(type) = type[6]; //! Mounting screw type +function vero_mounting_holes(type) = type[7]; //! Positions of the mounting holes +function vero_breaks(type) = type[8]; //! Breaks in the tracks +function vero_no_track(type) = type[9]; //! Missing tracks +function vero_components(type) = type[10]; //! List of named components and their positions +function vero_joints(type) = type[11]; //! List of solder joints + +function vero_thickness(type) = 1.6; //! Thickness of the substrate +function vero_track_thickness(type)= 0.035; //! Thickness of the tracks +function vero_track_width(type) = vero_pitch(type) * 0.8; //! The width of the tracks +function vero_length(type) = vero_holes(type) * vero_pitch(type); //! Length of the board +function vero_width(type) = vero_strips(type) * vero_pitch(type); //! Width of the board + +module solder_meniscus(type) { + h = 1; + r = vero_track_width(type) / 2; + + translate_z(vero_track_thickness(type)) + color("silver") rotate_extrude() + difference() { + square([r, h]); + + translate([r - eps, h + eps]) + ellipse(r , h); + } +} + +module vero_grid_pos(type, x, y) { //! Convert grid position to offset from the centre + holes = vero_holes(type); + strips = vero_strips(type); + translate([((x + holes) % holes) - holes / 2 + 0.5, + ((y + strips) % strips) - strips / 2 + 0.5] * vero_pitch(type)) + children(); +} + +module vero_mounting_hole_positions(type) //! Positions children at the mounting holes + for(p = vero_mounting_holes(type)) + vero_grid_pos(type, p.x, p.y) + children(); + +module vero_mounting_holes(type, h = 100) //! Drill mounting holes in a panel + vero_mounting_hole_positions(type) + drill(screw_clearance_radius(vero_screw(type)), h); + +module veroboard(type) { //! Draw specified veroboard with missing tracks and track breaks + holes = vero_holes(type); + strips = vero_strips(type); + pitch = vero_pitch(type); + length = holes * pitch; + width = strips * pitch; + hole_d = 1; + tw = vero_track_width(type); + colour = vero_fr4(type) ? "green" : "goldenrod"; + tc = vero_fr4(type) ? "silver" : "darkorange"; + no_track = vero_no_track(type); + + vitamin(str("veroboard(", type[0], "): Veroboard ", holes, " holes x ", strips, "strips")); + + color(colour) linear_extrude(height = vero_thickness(type)) + difference() { + rounded_square([length, width], r = 0.5, center = true); + + for(x = [0 : holes - 1], y = [0 : strips - 1]) + vero_grid_pos(type, x, y) + circle(d = hole_d); + + vero_mounting_hole_positions(type) + circle(r = screw_radius(vero_screw(type))); + } + + color(tc) vflip() linear_extrude(height = vero_track_thickness(type)) + difference() { + vflip() + for(y = [0 : strips -1]) + translate([0, y * pitch - (strips - 1) * pitch / 2]) + if(!in(no_track, y)) + difference() { + square([length - (pitch - tw), tw], center = true); + + for(x = [0 : holes - 1]) + translate([x * pitch - (holes - 1) * pitch / 2, 0]) + circle(d = hole_d); + } + vflip() { + vero_mounting_hole_positions(type) + for(y = [-1 : 1]) + hull() + for(x = [-1, 1]) + translate([x, y] * pitch) + circle(d = pitch * 1.1); + + for(p = vero_breaks(type)) + vero_grid_pos(type, p.x, p.y) + if(ceil(p.x) == p.x) + circle(d = pitch); + else + square([pitch * 0.2, pitch], center = true); + } + } +} + +module vero_components(type, cutouts = false, angle = undef) + for(comp = vero_components(type)) + vero_grid_pos(type, comp.x, comp.y) + translate_z(vero_thickness(type)) + pcb_component(comp, cutouts, angle); + +module vero_cutouts(type, angle = undef) vero_components(type, true, angle); //! Make cutouts to clear components + +module veroboard_assembly(type, height, thickness, flip = false) //! Draw the assembly with components and fasteners in place +assembly(vero_assembly(type)) { + screw = vero_screw(type); + washer = screw_washer(screw); + nut = screw_nut(screw); + screw_length = screw_longer_than(height + thickness + vero_thickness(type) + 2 * washer_thickness(washer) + nut_thickness(nut, true)); + + translate_z(height) { + veroboard(type); + + vero_components(type); + + for(r = vero_joints(type)) + for(x = r.x, y = r.y) + vero_grid_pos(type, x, y) + vflip() + solder_meniscus(type); + } + vero_mounting_hole_positions(type) { + translate_z(height + vero_thickness(type)) + if(flip) + nut_and_washer(nut, true); + else + screw_and_washer(screw, screw_length); + + color(pp1_colour) pcb_spacer(screw, height); + + translate_z(-thickness) + vflip() + if(flip) + screw_and_washer(screw, screw_length); + else + nut_and_washer(nut, true); + } +} diff --git a/vitamins/washer.scad b/vitamins/washer.scad new file mode 100644 index 0000000..fe2bf07 --- /dev/null +++ b/vitamins/washer.scad @@ -0,0 +1,130 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Washers, star washers, penny washers and printed washers. +//! +//! If a washer is given a child, usually a screw or a nut, then it is placed on its top surface. +// +include <../core.scad> +include <../utils/sweep.scad> + +soft_washer_colour = grey20; +hard_washer_colour = grey80; +star_washer_colour = brass; + +function washer_size(type) = type[1]; //! Noiminal size +function washer_diameter(type) = type[2]; //! External diameter +function washer_thickness(type) = type[3]; //! Thickness +function washer_soft(type) = type[4]; //! True if rubber +function star_washer_diameter(type) = type[5]; //! Star version size +function spring_washer_diameter(type) = type[6]; //! Spring washer size +function spring_washer_thickness(type)= type[7]; //! Spring washer thickness +function penny_washer(type) = type[8]; //! Corresponding penny washer +function washer_radius(type) = washer_diameter(type) / 2; //! Outside radius + +function washer_id(type) = washer_size(type) + 0.1; //! Inside diameter +function washer_colour(type) = washer_soft(type) ? soft_washer_colour : hard_washer_colour; //! Washer colour + +module washer(type) { //! Draw specified washer + hole = washer_size(type); + thickness = washer_thickness(type); + diameter = washer_diameter(type); + p = penny_washer(type); + penny = !is_undef(p) && !is_list(p); + if(washer_soft(type)) + vitamin(str("washer(", type[0], "_washer): Washer rubber M", hole, " x ", diameter, "mm x ", thickness, "mm")); + else + vitamin(str("washer(", type[0], "_washer): Washer ", penny ? "penny " : "", " M", hole, " x ", diameter, "mm x ", thickness, "mm")); + color(washer_colour(type)) + linear_extrude(height = thickness - 0.05) + difference() { + circle(d = diameter); + circle(d = washer_id(type)); + } + + if($children) + translate_z(thickness) + children(); +} + +module penny_washer(type) { //! Draw penny version of specified plain washer + penny = penny_washer(type); + assert(penny, "no penny version"); + washer(penny) + children(); +} + +module star_washer(type) { //! Draw star version of washer + hole = washer_size(type); + thickness = washer_thickness(type); + diameter = star_washer_diameter(type); + rad = diameter / 2; + inner = (hole / 2 + rad) / 2; + spoke = rad - hole / 2; + vitamin(str("star_washer(", type[0], "_washer): Washer star M", hole, " x ", thickness, "mm")); + color(star_washer_colour) + linear_extrude(height = thickness) + difference() { + circle(rad); + + circle(d = washer_id(type)); + + for(a = [0:30:360]) + rotate(a) + translate([inner + spoke / 2, 0, 0.5]) + square([spoke, 2 * PI * inner / 36], center = true); + } + if($children) + translate_z(thickness) + children(); +} + +module spring_washer(type) { //! Draw spring version of washer + hole = washer_size(type); + thickness = spring_washer_thickness(type); + diameter = spring_washer_diameter(type); + vitamin(str("spring_washer(", type[0], "_washer): Washer spring M", hole, " x ", thickness, "mm")); + ir = washer_id(type) / 2; + or = diameter / 2; + path = circle_points((ir + or) / 2, exploded() ? thickness / 2 : 0); + profile = rectangle_points(thickness, or - ir); + color(hard_washer_colour) + translate_z(thickness / 2) + rotate(180) + sweep(path, profile); + + if($children) + translate_z(thickness) + children(); +} + +module printed_washer(type, name = false) { //! Create printed washer + stl(name ? name : str("M", washer_size(type) * 10, "_washer")); + t = round_to_layer(washer_thickness(type)); + or = washer_radius(type); + ir = washer_id(type) / 2; + color(pp1_colour) + linear_extrude(height = t, center = false, convexity = 2) + poly_ring(or, ir); + + if($children) + translate_z(t) + children(); +} diff --git a/vitamins/washers.scad b/vitamins/washers.scad new file mode 100644 index 0000000..eae88d1 --- /dev/null +++ b/vitamins/washers.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +// Washers +// +// s d t s s s s p +// c i h o t p p e +// r a i f a r r n +// e m c t r i i n +// w e k n n y +// t n d g g +// e e i v +// r s a d t e +// s i h r +// a k +M3_penny_washer = ["M3_penny", 3, 12, 0.8, false, 5.8, 5.6, 1.0, true]; +M4_penny_washer = ["M4_penny", 4, 14, 0.8, false, 7.9, 7.0, 1.2, true]; +M5_penny_washer = ["M5_penny", 5, 20, 1.4, false, 9.0, 8.8, 1.6, true]; +M6_penny_washer = ["M6_penny", 6, 26, 1.5, false, 10.6, 9.9, 1.6, true]; +M8_penny_washer = ["M8_penny", 8, 30, 1.5, false, 13.8, 12.7, 2.0, true]; + +M2_washer = ["M2", 2, 5, 0.3, false, 4.5, 4.4, 0.5, undef]; +M2p5_washer = ["M2p5", 2.5, 5.9, 0.5, false, 5.4, 5.1, 0.6, undef]; +M3_washer = ["M3", 3, 7, 0.5, false, 5.8, 5.6, 1.0, M3_penny_washer]; +M3p5_washer = ["M3p5", 3.5, 8, 0.5, false, 6.9, 6.2, 1.0, undef]; +M4_washer = ["M4", 4, 9, 0.8, false, 7.9, 7.0, 1.2, M4_penny_washer]; +M5_washer = ["M5", 5, 10, 1.0, false, 9.0, 8.8, 1.6, M5_penny_washer]; +M6_washer = ["M6", 6, 12.5, 1.5, false, 10.6, 9.9, 1.6, M6_penny_washer]; +M8_washer = ["M8", 8, 17, 1.6, false, 13.8, 12.7, 2.0, M8_penny_washer]; +toggle_washer = ["toggle", 6.1,12, 0.6, false, 10, 0, 0, undef]; + +M3_rubber_washer= ["M3_rubber",3, 10, 1.5, true, 5.8, M3_penny_washer]; + +washers = [M2_washer, M2p5_washer, M3_washer, M3p5_washer, M4_washer, M5_washer, M6_washer, M8_washer, M3_rubber_washer]; + +use diff --git a/vitamins/wire.scad b/vitamins/wire.scad new file mode 100644 index 0000000..9550ece --- /dev/null +++ b/vitamins/wire.scad @@ -0,0 +1,76 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Wires. Just a BOM entry at the moment and cable bundle size fuctions for holes. See +//! . +// +include <../core.scad> +include + +module wire(color, strands, length, strand = 0.2) + vitamin(str(": Wire ", color, " ", strands, "/", strand, " length ",length, "mm")); + +module ribbon_cable(ways, length) + vitamin(str(": Ribbon cable ", ways, " way ", length, "mm")); + +// +// Cable sizes +// +function cable_wires(cable) = cable[0]; +function cable_wire_size(cable) = cable[1]; + +// numbers from http://mathworld.wolfram.com/CirclePacking.html +function cable_radius(cable) = ceil([0, 1, 2, 2.15, 2.41, 2.7, 3, 3, 3.3][cable_wires(cable)] * cable_wire_size(cable)) / 2; // radius of a bundle + +function wire_hole_radius(cable) = cable_radius(cable) + 0.5; + +// arrangement of bundle in flat cable clip +function cable_bundle(cable) = [[0,0], [1,1], [2,1], [2, 0.5 + sin(60)], [2,2], [3, 0.5 + sin(60)], [3,2]][cable_wires(cable)]; + +function cable_width(cable) = cable_bundle(cable)[0] * cable_wire_size(cable); // width in flat clip +function cable_height(cable) = cable_bundle(cable)[1] * cable_wire_size(cable); // height in flat clip + +module mouse_hole(cable, h = 100) { + r = wire_hole_radius(cable); + + rotate(90) slot(r, 2 * r, h = h); +} + +module cable_tie_holes(cable_r, h = 100) { + r = cnc_bit_r; + l = 3; + extrude_if(h) + for(side = [-1, 1]) + translate([0, side * (cable_r + r)]) + hull() + for(end = [-1, 1]) + translate([end * (l / 2 - r), 0]) + drill(r, 0); +} + +module cable_tie(cable_r, thickness) { + w = 2 * (cable_r + cnc_bit_r); + translate_z(thickness / 2) + rotate([-90, 0, 90]) + ziptie(small_ziptie, w / 2); +} + +//cable_tie_holes(6 / 2); +//cable_tie(6 / 2, 3); diff --git a/vitamins/ziptie.scad b/vitamins/ziptie.scad new file mode 100644 index 0000000..775a80f --- /dev/null +++ b/vitamins/ziptie.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// +//! Cable zipties. +// + +include <../core.scad> +use <../utils/tube.scad> + +function ziptie_width(type) = type[1]; //! Width +function ziptie_thickness(type) = type[2]; //! Thickness +function ziptie_latch(type) = type[3]; //! Latch dimensions +function ziptie_colour(type) = type[4]; //! Colour +function ziptie_tail(type) = type[5]; //! The length without teeth + +module ziptie(type, r) +{ + latch = ziptie_latch(type); + length = ceil(2 * PI * r + ziptie_tail(type) + latch.z + 1); + len = length <= 100 ? 100 : length; + vitamin(str("ziptie(", type[0], ", ", r, "): Ziptie ", len, "mm min length")); + + angle = (r > latch.x / 2) ? asin((latch.x / 2) / r) - asin(ziptie_thickness(type) / latch.x) : 0; + color(ziptie_colour(type)) union() { + tube(ir = r, or = r + ziptie_thickness(type), h = ziptie_width(type)); + translate([0, -r, - latch.y / 2]) + rotate([90, 0, angle]) { + union() { + cube(latch); + + translate([latch.x / 2, latch.y / 2, (latch.z + 1) / 2]) + cube([ziptie_thickness(type), ziptie_width(type), latch.z + 1], center = true); + } + } + } +} diff --git a/vitamins/zipties.scad b/vitamins/zipties.scad new file mode 100644 index 0000000..a098987 --- /dev/null +++ b/vitamins/zipties.scad @@ -0,0 +1,27 @@ +// +// NopSCADlib Copyright Chris Palmer 2018 +// 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 . +// + +// [width, thickness, [latch_x, latch_y, latch_z], color, tail] +small_ziptie = ["small_ziptie", 2.5, 1.0, [4.7, 4.25, 3.0], "white", 25]; +ziptie_3mm = ["ziptie_3mm", 3.0, 1.0, [5.4, 5.44, 4.5], "white", 25]; +ziptie_3p6mm = ["ziptie_3p6mm", 3.6, 1.2, [6.4, 6.62, 4.7], "white", 25]; + +zipties = [small_ziptie, ziptie_3mm, ziptie_3p6mm]; + +use