A ruler modular up to one unit! Not the smallest, but the bigger one :)
In the contest Measurement Devices
54
57
1
903
updated January 27, 2023

Description

PDF

Concept

A dead simple, but fully modular system to build an infinite ruler, a 3 axis ruler, a center finder or maybe a space station some time.

TLDR

Print the starter kit (a folder on the files tab) to get the concept. Just open your eyes big to see the small bumper in the hole :)

Background

The current (initial version) is created in a few hours and I post it now to enter in the measurement devices contest, but my plan is to improve it with time, so please print, comment, remix and set a reminder in your calendar to look back every month.

Design

With the help of the wonderful JSCAD web version and in a hundred lines of code the base concept is now kinda ready.

You can easily try it out, just copy the following code, set up a different module order and then export it.

Modules
NameCharcodeDescriptionPreview
ConnectorC6 holes to connect them all
FullFspacer to make it loooong
PlugPthe simplest 90° thread
HoleHA hole in the middle to fit an IKEA pencil
Examples

C,F*3,P is the light blue guy on the cover photo. You can read it as: connector, then 3 pieces of full and a plug at the end.

I've tried out it as a crazy color change print, just for fun, but I don't recommend it for now until the whole project is cleaned up.

C,F,H,F,C is the center finder.

Code

Please don't review it too strict, this is a PoC created in a few hours (at very-very late).

const {
  primitives: { circle, cube, cuboid, cylinder, roundedCuboid, sphere, torus },
  transforms: { translate, mirrorX, rotate },
  booleans: { union, subtract },
  hulls: { hull, hullChain },
  text: { vectorText },
  colors: { colorize, colorNameToRgb, hexToRgb },
  extrusions: { extrudeLinear, extrudeRotate },
} = require('@jscad/modeling')

const paint = (color, elem) => colorize(colorNameToRgb(color), elem)
const side = 10
const bumperRadius = 1

const plug = (opts) => {
  const height = side / 2 - 2
  const plug = cylinder({ radius: side / 4, height, ...opts.resolution })
  const bumper = sphere({ radius: bumperRadius, ...opts.resolution })
  const bumperCoords = { x: 0, y: side / 4, z: side / 2 - bumperRadius - (side / 4 - 2 * bumperRadius) / 2 }

  const y = (height - 10) / 2
  return [
    translate([0, 0, -5 + height / 2], subtract(
      plug,
      union(
        translate([0, side / 4, 0], bumper),
        extrudeRotate({ ...opts.resolution, angle: Math.PI / 2 }, circle({ size: bumperRadius, center: [side / 4, 0] })),
        translate([side / 4, 0, 0], bumper),
        translate([side / 4, 0, height / 4], cylinder({ radius: bumperRadius, height: height / 2, ...opts.resolution })),
      ),
    )),
  ]
}

const connector = (opts) => {
  const base = cuboid({ size: [side, side, side] })
  const hole = cylinder({ radius: side / 4, height: side, ...opts.resolution })
  const bumper = sphere({ radius: bumperRadius, ...opts.resolution })
  const bumperCoords = { x: 0, y: side / 4, z: side / 2 - bumperRadius - (side / 4 - 2 * bumperRadius) / 2 }
  return [
    paint('orange', subtract(
      base,
      union(
        hole,
        rotate([Math.PI / 2, 0, 0], hole),
        rotate([0, Math.PI / 2, 0], hole),
      )
    )),
    paint('brown', union(
      translate([bumperCoords.x, bumperCoords.y, bumperCoords.z], bumper),
      translate([bumperCoords.x, bumperCoords.y, -bumperCoords.z], bumper),
      translate([bumperCoords.x, bumperCoords.z, -bumperCoords.y], bumper),
      translate([bumperCoords.x, -bumperCoords.z, -bumperCoords.y], bumper),
      translate([bumperCoords.z, bumperCoords.x, -bumperCoords.y], bumper),
      translate([-bumperCoords.z, bumperCoords.x, -bumperCoords.y], bumper),
    ))
  ]
}

const centerFinder = (opts) => {
  return [
    subtract(
      cube({size: side}),
      rotate([0, Math.PI / 2, 0], cylinder({radius: 7.5 / 2, height: side, ...opts.resolution})),
    ),
  ]
}

const mark = (elem, height, padding = 0, margin = 0.5, width = 0.2) => {
  const length = 5 - margin - padding / 2
  const line = cuboid({ size: [length, width, width] })
  const line1 = translate([5 - margin - length / 2, -5 + width / 2, height], line)
  const line2 = translate([-(5 - margin - length / 2), -5 + width / 2, height], line)
  const lines = union(line1, line2)
  return subtract(
    elem,
    union(
      lines,
      rotate([0, 0, Math.PI / 2], lines),
      rotate([0, 0, -Math.PI / 2], lines),
      rotate([0, 0, Math.PI], lines),
    ),
  )
}

const main = (params) => {
  const opts = { params, resolution: { segments: params.hires ? 32 : 16 } }
  return [union(
    params.setup.split(',')
      .map((elem) => elem.trim())
      .flatMap((elem) => {
        switch (elem.charAt(0)) {
          case 'C': return [connector(opts)]
          case 'F': return Array.from({ length: elem.length === 1 ? 1 : parseInt(elem.substring(2)) }).map(() => cube({ size: side }))
          case 'P': return [plug(opts)]
          case 'H': return [centerFinder(opts)]
          default: return []
        }
      })
      .map((elem) => union(elem))
      .map((elem) => params.marks ? mark(elem, side / 10 * 5) : elem) // mark at top
      .map((elem) => params.marks ? mark(elem, side / 10 * 4, 6) : elem)
      .map((elem) => params.marks ? mark(elem, side / 10 * 3, 6) : elem)
      .map((elem) => params.marks ? mark(elem, side / 10 * 2, 6) : elem)
      .map((elem) => params.marks ? mark(elem, side / 10 * 1, 6) : elem)
      .map((elem) => params.marks ? mark(elem, 0, 2) : elem) // params.marks ? mark at center
      .map((elem) => params.marks ? mark(elem, -side / 10 * 1, 6) : elem)
      .map((elem) => params.marks ? mark(elem, -side / 10 * 2, 6) : elem)
      .map((elem) => params.marks ? mark(elem, -side / 10 * 3, 6) : elem)
      .map((elem) => params.marks ? mark(elem, -side / 10 * 4, 6) : elem)
      .map((elem) => params.marks ? mark(elem, -side / 10 * 5) : elem) // mark at bottom
      .map((elem, i) => translate([0, 0, i * side], elem))
  )]
}

const getParameterDefinitions = () => [
  { name: 'hires', type: 'checkbox', checked: true, initial: 0, caption: 'high resolution' },
  { name: 'setup', type: 'text', caption: 'setup', placeholder: 'C,F,H,F,P' },
  { name: 'marks', type: 'checkbox', checked: true, initial: 0, caption: 'marks' },
]

module.exports = { main, getParameterDefinitions }

Remarks

You might want to set high resolution for the export, but turn it of when you just tweak the settings or the code as it gets slow pretty quick.

There are issues with the generated model, but looks like that “Fix through the Netfabb” option of the Prusa Slicer get rid all of them.

Tags



Model origin

The author marked this model as their own original creation.

License


Highlighted models from creator

View more