-
Notifications
You must be signed in to change notification settings - Fork 558
Feature (with implementation): alignTo function #1446
Description
Hello! First of all, I like jscad a lot. After using openscad quite a bit and trying cadquery, I found jscad and use it as my primary tool for desiging (simple) 3D objects.
I've made an alignTo-function that I like a lot; I use it in all my designs because I think it makes designing stuff align to how you reason about the design.
You can find it below. It takes 3 arguments: An options-object with the alignment-specification, the object you wish to align and the object you wish to align to. The options object can have x,y and z, keys. All optional. If one is specified, it can be a string 'min', 'max' or 'center', meaning the min/max/center of the object will in this dimension be aligned to the min/max/center of the alignTo object. It can also be an array with in the first element the position (min/max/center) you want to align to the position (min/max/center) of the alignTo object.
For example for a simple box:
inside = cuboid({size: [width_inside, depth_inside, height_inside]})
outside = cuboid({size: [width_inside + 2 * wall_thick, depth_inside + 2 * wall_thick, height_inside + wall_thick]})
inside = alignTo({x:'center', y:'center', z:'max'}, inside, outside)
simple_box = subtract(outside, inside)
When building slightly complexer objects, I like the fact that you can then reason about relative positions min/max/center.
Questions: Do you agree this is handy? Or am I missing some feature or way of thinking? If you think this is handy, would you accept a PR to put it in jscad, so I dont need a seperate library and others can use it out of the box? And if yes: where in the codebase you think it would be located best?
/**
* Translate {@link geom} to align with {@link alignToGeom} according to specified options.
*
* @param {Array} options.x - Array with in index 0 the x-option ('min'/'max'/'center') of {@link geom} to align to (in position 1) the x-option of {@link alignToGeom}. See example.
* @param {Array} options.y - See options.x
* @param {Array} options.z - See options.x
* @param geom The geometry to be aligned
* @param alignToGeom The geometry to align {@link geom} to
* @returns Translated geom
*
* @example
* // Align the x-minimum of geom to x-minimum of alignToGeom and y-center of geom to y-maximum of alignToGeom
*
* let alignedGeom = alignTo({x:['min', 'min'], y:['center', 'max']}, geom, alignToGeom)
*/
const alignTo = (options, geom, alignToGeom) => {
const bbGeom = measureAggregateBoundingBox(geom);
const bbAlignToGeom = measureAggregateBoundingBox(alignToGeom);
const translation = [0,0,0]
if ('x' in options) {
translation[0] = alignValue(options['x'], 0, bbGeom, bbAlignToGeom)
}
if ('y' in options) {
translation[1] = alignValue(options['y'], 1, bbGeom, bbAlignToGeom)
}
if ('z' in options) {
translation[2] = alignValue(options['z'], 2, bbGeom, bbAlignToGeom)
}
return translate(translation, geom)
}
const alignValue = (optionValue, dim, bbGeom, bbAlignToGeom) => {
let optionGeom = ''
let optionAlignToGeom = ''
if (typeof optionValue === 'string') {
optionGeom = optionValue
optionAlignToGeom = optionValue
} else {
optionGeom = optionValue[0]
optionAlignToGeom = optionValue[1]
}
return alignOptionValue(bbAlignToGeom, dim, optionAlignToGeom) - alignOptionValue(bbGeom, dim, optionGeom)
}
const alignOptionValue = (bb, dim, option) => {
if (option === 'min') {
return bb[0][dim]
} else if (option === 'max') {
return bb[1][dim]
} else if (option === 'center') {
return (bb[0][dim] + bb[1][dim]) / 2;
} else {
return 0;
}
}