2017-07-22 11:50:59 +00:00
// Library: round-anything
// Version: 1.0
// Author: IrevDev
2017-08-01 10:04:29 +00:00
// Contributors: TLC123
2018-02-18 21:46:25 +00:00
// Copyright: 2020
// License: MIT
2017-07-22 11:50:59 +00:00
2020-08-08 17:52:15 +10:00
function addZcoord ( points , displacement ) = [ for ( i = [ 0 : len ( points ) - 1 ] ) [ points [ i ] . x , points [ i ] . y , displacement ] ] ;
function translate3Dcoords ( points , tran = [ 0 , 0 , 0 ] , mult = [ 1 , 1 , 1 ] ) = [ for ( i = [ 0 : len ( points ) - 1 ] ) [
( points [ i ] . x * mult . x ) + tran . x ,
( points [ i ] . y * mult . y ) + tran . y ,
( points [ i ] . z * mult . z ) + tran . z
] ] ;
2020-08-09 20:44:44 +10:00
function offsetPolygonPoints ( points , offset = 0 ) =
// Work sthe same as the offset does, except for the fact that instead of a 2d shape
2020-08-09 20:59:32 +10:00
// It works directly on polygon points
// It returns the same number of points just offset into or, away from the original shape.
2020-08-09 20:44:44 +10:00
// points= a series of x,y points[[x1,y1],[x2,y2],...]
// offset= amount to offset by, negative numbers go inwards into the shape, positive numbers go out
// return= a series of x,y points[[x1,y1],[x2,y2],...]
2020-08-08 17:52:15 +10:00
let (
isCWorCCW = sign ( offset ) * CWorCCW ( points ) * - 1 ,
lp = len ( points )
)
[ for ( i = [ 0 : lp - 1 ] ) parallelFollow ( [
points [ listWrap ( i - 1 , lp ) ] ,
points [ i ] ,
points [ listWrap ( i + 1 , lp ) ] ,
] , thick = offset , mode = isCWorCCW ) ] ;
2020-10-05 12:48:31 -04:00
function reverseList ( list ) = [ for ( i = [ len ( list ) - 1 : - 1 : 0 ] ) list [ i ] ] ;
2020-10-03 22:35:08 -04:00
2020-10-05 12:48:31 -04:00
// Apply `reverseList` to the array of vertex indices for an array of faces
function invertFaces ( faces ) = [ for ( f = faces ) reverseList ( f ) ] ;
2020-10-03 22:35:08 -04:00
2020-08-09 20:44:44 +10:00
function makeCurvedPartOfPolyHedron ( radiiPoints , r , fn , minR = 0.01 ) =
// this is a private function that I'm not expecting library users to use directly
// radiiPoints= serise of x, y, r points
// r= radius of curve that will be put on the end of the extrusion
// fn= amount of subdivisions
2020-08-09 20:59:32 +10:00
// minR= if one of the points in radiiPoints is less than r, it's likely to converge and form a sharp edge,
// the min radius on these converged edges can be controled with minR, though because of legacy reasons it can't be 0, but can be a very small number.
2020-08-09 20:44:44 +10:00
// return= array of [polyhedronPoints, Polyhedronfaces, theLength of a singe layer in the curve]
2020-08-08 17:52:15 +10:00
let (
2020-08-09 20:44:44 +10:00
lp = len ( radiiPoints ) ,
radii = [ for ( i = [ 0 : lp - 1 ] ) radiiPoints [ i ] . z ] ,
isCWorCCWOverall = CWorCCW ( radiiPoints ) ,
2020-08-08 17:52:15 +10:00
dir = sign ( r ) ,
absR = abs ( r ) ,
fractionOffLp = 1 - 1 / fn ,
allPoints = [ for ( fraction = [ 0 : 1 / fn : 1 ] )
let (
iterationOffset = dir * sqrt ( sq ( absR ) - sq ( fraction * absR ) ) - dir * absR ,
2020-08-09 20:44:44 +10:00
theOffsetPoints = offsetPolygonPoints ( radiiPoints , iterationOffset ) ,
2020-08-08 17:52:15 +10:00
polyRoundOffsetPoints = [ for ( i = [ 0 : lp - 1 ] )
let (
pointsAboutCurrent = [
theOffsetPoints [ listWrap ( i - 1 , lp ) ] ,
theOffsetPoints [ i ] ,
theOffsetPoints [ listWrap ( i + 1 , lp ) ]
] ,
isCWorCCWLocal = CWorCCW ( pointsAboutCurrent ) ,
isInternalRadius = ( isCWorCCWLocal * isCWorCCWOverall ) = = - 1 ,
// the radius names are only true for positive r,
// when are r is negative increasingRadius is actually decreasing and vice-vs
// increasingRadiusWithPositiveR is just to verbose of a variable name for my liking
increasingRadius = max ( radii [ i ] - iterationOffset , minR ) ,
decreasingRadius = max ( radii [ i ] + iterationOffset , minR )
)
[ theOffsetPoints [ i ] . x , theOffsetPoints [ i ] . y , isInternalRadius ? increasingRadius : decreasingRadius ]
] ,
2020-08-09 20:44:44 +10:00
pointsForThisLayer = polyRound ( polyRoundOffsetPoints , fn )
2020-08-08 17:52:15 +10:00
)
2020-08-09 20:44:44 +10:00
addZcoord ( pointsForThisLayer , fraction * absR )
2020-08-08 17:52:15 +10:00
] ,
2020-08-09 20:44:44 +10:00
polyhedronPoints = flatternArray ( allPoints ) ,
2020-08-08 17:52:15 +10:00
allLp = len ( allPoints ) ,
2020-08-09 20:44:44 +10:00
layerLength = len ( allPoints [ 0 ] ) ,
2020-08-08 17:52:15 +10:00
loopToSecondLastLayer = allLp - 2 ,
sideFaces = [ for ( layerIndex = [ 0 : loopToSecondLastLayer ] ) let (
2020-08-09 20:44:44 +10:00
currentLayeroffset = layerIndex * layerLength ,
nextLayeroffset = ( layerIndex + 1 ) * layerLength ,
layerFaces = [ for ( subLayerIndex = [ 0 : layerLength - 1 ] )
2020-08-08 17:52:15 +10:00
[
2020-08-09 20:44:44 +10:00
currentLayeroffset + subLayerIndex , currentLayeroffset + listWrap ( subLayerIndex + 1 , layerLength ) , nextLayeroffset + listWrap ( subLayerIndex + 1 , layerLength ) , nextLayeroffset + subLayerIndex ]
2020-08-08 17:52:15 +10:00
]
2020-08-09 20:44:44 +10:00
) layerFaces ] ,
polyhedronFaces = flatternArray ( sideFaces )
2020-08-08 17:52:15 +10:00
)
2020-08-09 20:44:44 +10:00
[ polyhedronPoints , polyhedronFaces , layerLength ] ;
2020-08-08 17:52:15 +10:00
2021-05-02 11:43:31 +02:00
function flatternRecursion ( array , init = [ ] , currentIndex = 0 ) =
2020-08-09 20:59:32 +10:00
// this is a private function, init and currentIndex are for the function's use
2020-08-09 20:44:44 +10:00
// only for when it's calling itself, which is why there is a simplified version flatternArray that just calls this one
// array= array to flattern by one level of nesting
// init= the array used to cancat with the next call, only for when the function calls itself
// currentIndex= so the function can keep track of how far it's progressed through the array, only for when it's calling itself
// returns= flatterned array, by one level of nesting
2020-08-08 17:52:15 +10:00
let (
shouldKickOffRecursion = currentIndex = = undef ? 1 : 0 ,
isLastIndex = currentIndex + 1 = = len ( array ) ? 1 : 0 ,
2020-08-09 20:44:44 +10:00
flatArray = shouldKickOffRecursion ? flatternRecursion ( array , [ ] , 0 ) :
2020-08-08 17:52:15 +10:00
isLastIndex ? concat ( init , array [ currentIndex ] ) :
flatternRecursion ( array , concat ( init , array [ currentIndex ] ) , currentIndex + 1 )
)
2020-08-09 20:44:44 +10:00
flatArray ;
function flatternArray ( array ) =
// public version of flatternRecursion, has simplified params to avoid confusion
// array= array to be flatterned
// return= array that been flatterend by one level of nesting
flatternRecursion ( array ) ;
2020-08-08 17:52:15 +10:00
function offsetAllFacesBy ( array , offset ) = [
2020-08-09 20:59:32 +10:00
// polyhedron faces are simply a list of indices to points, if your concat points together than you probably need to offset
2020-08-09 20:44:44 +10:00
// your faces array to points to the right place in the new list
// array= array of point indicies
// offset= number to offset all indecies by
// return= array of point indices (i.e. faces) with offset applied
2020-08-08 17:52:15 +10:00
for ( faceIndex = [ 0 : len ( array ) - 1 ] ) [
for ( pointIndex = [ 0 : len ( array [ faceIndex ] ) - 1 ] ) array [ faceIndex ] [ pointIndex ] + offset
]
] ;
2020-08-09 20:44:44 +10:00
function extrudePolygonWithRadius ( radiiPoints , h = 5 , r1 = 1 , r2 = 1 , fn = 4 ) =
// this basically calls makeCurvedPartOfPolyHedron twice to get the curved section of the final polyhedron
2020-08-09 20:59:32 +10:00
// and then goes about assmbling them, as the side faces and the top and bottom face caps are missing
2020-08-09 20:44:44 +10:00
// radiiPoints= series of [x,y,r] points,
// h= height of the extrude (total including radius sections)
// r1,r2= define the radius at the top and bottom of the extrud respectively, negative number flange out the extrude
// fn= number of subdivisions
// returns= [polyhedronPoints, polyhedronFaces]
2020-08-08 17:52:15 +10:00
let (
2020-08-09 20:44:44 +10:00
// top is the top curved part of the extrude
top = makeCurvedPartOfPolyHedron ( radiiPoints , r1 , fn ) ,
2020-10-06 05:39:58 +11:00
topRadiusPoints = translate3Dcoords ( top [ 0 ] , [ 0 , 0 , h - abs ( r1 ) ] ) ,
2020-08-09 20:44:44 +10:00
singeLayerLength = top [ 2 ] ,
topRadiusFaces = top [ 1 ] ,
radiusPointsLength = len ( topRadiusPoints ) , // is the same length as bottomRadiusPoints
// bottom is the bottom curved part of the extrude
bottom = makeCurvedPartOfPolyHedron ( radiiPoints , r2 , fn ) ,
// Z axis needs to be multiplied by -1 to flip it so the radius is going in the right direction [1,1,-1]
bottomRadiusPoints = translate3Dcoords ( bottom [ 0 ] , [ 0 , 0 , abs ( r2 ) ] , [ 1 , 1 , - 1 ] ) ,
// becaues the points will be all concatenated into the same array, and the bottom points come second, than
// the original indices the faces are points towards are wrong and need to have an offset applied to them
bottomRadiusFaces = offsetAllFacesBy ( bottom [ 1 ] , radiusPointsLength ) ,
// all of the side panel of the extrusion, connecting points from the inner layers of each
// of the curved sections
sideFaces = [ for ( i = [ 0 : singeLayerLength - 1 ] ) [
2020-08-08 17:52:15 +10:00
i ,
2020-08-09 20:44:44 +10:00
listWrap ( i + 1 , singeLayerLength ) ,
radiusPointsLength + listWrap ( i + 1 , singeLayerLength ) ,
radiusPointsLength + i
2020-08-08 17:52:15 +10:00
] ] ,
2020-08-09 20:44:44 +10:00
// both of these caps are simple every point from the last layer of the radius points
topCapFace = [ for ( i = [ 0 : singeLayerLength - 1 ] ) radiusPointsLength - singeLayerLength + i ] ,
bottomCapFace = [ for ( i = [ 0 : singeLayerLength - 1 ] ) radiusPointsLength * 2 - singeLayerLength + i ] ,
finalPolyhedronPoints = concat ( topRadiusPoints , bottomRadiusPoints ) ,
2020-10-05 12:48:31 -04:00
finalPolyhedronFaces = concat ( topRadiusFaces , invertFaces ( bottomRadiusFaces ) , invertFaces ( sideFaces ) , [ topCapFace ] , invertFaces ( [ bottomCapFace ] ) )
2020-08-08 17:52:15 +10:00
)
[
2020-08-09 20:44:44 +10:00
finalPolyhedronPoints ,
finalPolyhedronFaces
2020-08-08 17:52:15 +10:00
] ;
2020-08-09 20:44:44 +10:00
2020-08-16 08:35:03 +10:00
module polyRoundExtrude ( radiiPoints , length = 5 , r1 = 1 , r2 = 1 , fn = 10 , convexity = 10 ) {
2021-05-29 08:34:35 +10:00
assert ( len ( radiiPoints ) > 2 , str ( "There must be at least 3 radii points for polyRoundExtrude. " , radiiPoints , " is not long enough, you need " , 3 - len ( radiiPoints ) , " more point/s. Example: polyRoundExtrude([[11,0,1],[20,20,1.1],[8,7,0.5]],2,0.5,-0.8,fn=8);" ) ) ;
if ( len ( radiiPoints ) > 2 ) {
orderedRadiiPoints = CWorCCW ( radiiPoints ) = = 1
? reverseList ( radiiPoints )
: radiiPoints ;
2020-10-05 12:48:31 -04:00
2021-05-29 08:34:35 +10:00
polyhedronPointsNFaces = extrudePolygonWithRadius ( orderedRadiiPoints , length , r1 , r2 , fn ) ;
polyhedron ( points = polyhedronPointsNFaces [ 0 ] , faces = polyhedronPointsNFaces [ 1 ] , convexity = convexity ) ;
}
2020-08-09 20:44:44 +10:00
}
2020-08-08 17:52:15 +10:00
2020-07-26 18:18:28 +10:00
// testingInternals();
module testingInternals ( ) {
2018-02-18 21:46:25 +00:00
//example of rounding random points, this has no current use but is a good demonstration
random = [ for ( i = [ 0 : 20 ] ) [ rnd ( 0 , 50 ) , rnd ( 0 , 50 ) , /*rnd(0,30)*/ 1000 ] ] ;
R = polyRound ( random , 7 ) ;
translate ( [ - 25 , 25 , 0 ] ) {
polyline ( R ) ;
}
//example of different modes of the CentreN2PointsArc() function 0=shortest arc, 1=longest arc, 2=CW, 3=CCW
p1 = [ 0 , 5 ] ; p2 = [ 10 , 5 ] ; centre = [ 5 , 0 ] ;
translate ( [ 60 , 0 , 0 ] ) {
color ( "green" ) {
polygon ( CentreN2PointsArc ( p1 , p2 , centre , 0 , 20 ) ) ; //draws the shortest arc
2017-08-01 10:04:29 +00:00
}
2018-02-18 21:46:25 +00:00
color ( "cyan" ) {
polygon ( CentreN2PointsArc ( p1 , p2 , centre , 1 , 20 ) ) ; //draws the longest arc
2017-08-01 10:04:29 +00:00
}
2018-02-18 21:46:25 +00:00
}
translate ( [ 75 , 0 , 0 ] ) {
color ( "purple" ) {
polygon ( CentreN2PointsArc ( p1 , p2 , centre , 2 , 20 ) ) ; //draws the arc CW (which happens to be the short arc)
2017-09-14 12:56:50 +00:00
}
2018-02-18 21:46:25 +00:00
color ( "red" ) {
polygon ( CentreN2PointsArc ( p2 , p1 , centre , 2 , 20 ) ) ; //draws the arc CW but p1 and p2 swapped order resulting in the long arc being drawn
2017-09-14 12:56:50 +00:00
}
2018-02-18 21:46:25 +00:00
}
radius = 6 ;
radiipoints = [ [ 0 , 0 , 0 ] , [ 10 , 20 , radius ] , [ 20 , 0 , 0 ] ] ;
tangentsNcen = round3points ( radiipoints ) ;
2020-07-26 18:18:28 +10:00
translate ( [ 10 , 0 , 0 ] ) {
2018-02-18 21:46:25 +00:00
for ( i = [ 0 : 2 ] ) {
color ( "red" ) translate ( getpoints ( radiipoints ) [ i ] ) circle ( 1 ) ; //plots the 3 input points
color ( "cyan" ) translate ( tangentsNcen [ i ] ) circle ( 1 ) ; //plots the two tangent poins and the circle centre
}
translate ( [ tangentsNcen [ 2 ] [ 0 ] , tangentsNcen [ 2 ] [ 1 ] , - 0.2 ] ) circle ( r = radius , $fn = 25 ) ; //draws the cirle
% polygon ( getpoints ( radiipoints ) ) ; //draws a polygon
}
2017-07-22 11:50:59 +00:00
}
2018-02-18 21:46:25 +00:00
function polyRound ( radiipoints , fn = 5 , mode = 0 ) =
/*Takes a list of radii points of the format [x,y,radius] and rounds each point
2017-09-14 12:56:50 +00:00
with fn resolution
mode = 0 - automatic radius limiting - DEFAULT
mode = 1 - Debug , output radius reduction for automatic radius limiting
mode = 2 - No radius limiting * /
2018-02-18 21:46:25 +00:00
let (
p = getpoints ( radiipoints ) , //make list of coordinates without radii
Lp = len ( p ) ,
2020-07-22 19:56:49 +10:00
//remove the middle point of any three colinear points, otherwise adding a radius to the middle of a straigh line causes problems
radiiPointsWithoutTrippleColinear = [
for ( i = [ 0 : len ( p ) - 1 ] ) if (
// keep point if it isn't colinear or if the radius is 0
! isColinear (
p [ listWrap ( i - 1 , Lp ) ] ,
p [ listWrap ( i + 0 , Lp ) ] ,
p [ listWrap ( i + 1 , Lp ) ]
) ||
p [ listWrap ( i + 0 , Lp ) ] . z ! = 0
) radiipoints [ listWrap ( i + 0 , Lp ) ]
2018-02-18 21:46:25 +00:00
] ,
2020-07-22 19:56:49 +10:00
newrp2 = processRadiiPoints ( radiiPointsWithoutTrippleColinear ) ,
plusMinusPointRange = mode = = 2 ? 1 : 2 ,
2018-02-18 21:46:25 +00:00
temp = [
for ( i = [ 0 : len ( newrp2 ) - 1 ] ) //for each point in the radii array
let (
2020-07-22 19:56:49 +10:00
thepoints = [ for ( j = [ - plusMinusPointRange : plusMinusPointRange ] ) newrp2 [ listWrap ( i + j , len ( newrp2 ) ) ] ] , //collect 5 radii points
2018-02-18 21:46:25 +00:00
temp2 = mode = = 2 ? round3points ( thepoints , fn ) : round5points ( thepoints , fn , mode )
)
mode = = 1 ? temp2 : newrp2 [ i ] [ 2 ] = = 0 ?
[ [ newrp2 [ i ] [ 0 ] , newrp2 [ i ] [ 1 ] ] ] : //return the original point if the radius is 0
CentreN2PointsArc ( temp2 [ 0 ] , temp2 [ 1 ] , temp2 [ 2 ] , 0 , fn ) //return the arc if everything is normal
]
)
[ for ( a = temp ) for ( b = a ) b ] ; //flattern and return the array
function round5points ( rp , fn , debug = 0 ) =
2017-09-14 12:56:50 +00:00
rp [ 2 ] [ 2 ] = = 0 && debug = = 0 ? [ [ rp [ 2 ] [ 0 ] , rp [ 2 ] [ 1 ] ] ] : //return the middle point if the radius is 0
rp [ 2 ] [ 2 ] = = 0 && debug = = 1 ? 0 : //if debug is enabled and the radius is 0 return 0
let (
p = getpoints ( rp ) , //get list of points
2017-10-14 08:18:08 +00:00
r = [ for ( i = [ 1 : 3 ] ) abs ( rp [ i ] [ 2 ] ) ] , //get the centre 3 radii
2017-08-01 10:04:29 +00:00
//start by determining what the radius should be at point 3
//find angles at points 2 , 3 and 4
2017-09-14 12:56:50 +00:00
a2 = cosineRuleAngle ( p [ 0 ] , p [ 1 ] , p [ 2 ] ) ,
a3 = cosineRuleAngle ( p [ 1 ] , p [ 2 ] , p [ 3 ] ) ,
a4 = cosineRuleAngle ( p [ 2 ] , p [ 3 ] , p [ 4 ] ) ,
2017-08-01 10:04:29 +00:00
//find the distance between points 2&3 and between points 3&4
2017-09-14 12:56:50 +00:00
d23 = pointDist ( p [ 1 ] , p [ 2 ] ) ,
d34 = pointDist ( p [ 2 ] , p [ 3 ] ) ,
2017-08-01 10:04:29 +00:00
//find the radius factors
2017-09-14 12:56:50 +00:00
F23 = ( d23 * tan ( a2 / 2 ) * tan ( a3 / 2 ) ) / ( r [ 0 ] * tan ( a3 / 2 ) + r [ 1 ] * tan ( a2 / 2 ) ) ,
F34 = ( d34 * tan ( a3 / 2 ) * tan ( a4 / 2 ) ) / ( r [ 1 ] * tan ( a4 / 2 ) + r [ 2 ] * tan ( a3 / 2 ) ) ,
newR = min ( r [ 1 ] , F23 * r [ 1 ] , F34 * r [ 1 ] ) , //use the smallest radius
2018-02-18 21:46:25 +00:00
//now that the radius has been determined, find tangent points and circle centre
tangD = newR / tan ( a3 / 2 ) , //distance to the tangent point from p3
circD = newR / sin ( a3 / 2 ) , //distance to the circle centre from p3
//find the angle from the p3
an23 = getAngle ( p [ 1 ] , p [ 2 ] ) , //angle from point 3 to 2
an34 = getAngle ( p [ 3 ] , p [ 2 ] ) , //angle from point 3 to 4
//find tangent points
t23 = [ p [ 2 ] [ 0 ] - cos ( an23 ) * tangD , p [ 2 ] [ 1 ] - sin ( an23 ) * tangD ] , //tangent point between points 2&3
t34 = [ p [ 2 ] [ 0 ] - cos ( an34 ) * tangD , p [ 2 ] [ 1 ] - sin ( an34 ) * tangD ] , //tangent point between points 3&4
//find circle centre
tmid = getMidpoint ( t23 , t34 ) , //midpoint between the two tangent points
anCen = getAngle ( tmid , p [ 2 ] ) , //angle from point 3 to circle centre
cen = [ p [ 2 ] [ 0 ] - cos ( anCen ) * circD , p [ 2 ] [ 1 ] - sin ( anCen ) * circD ]
)
2017-09-14 12:56:50 +00:00
//circle center by offseting from point 3
2017-08-01 10:04:29 +00:00
//determine the direction of rotation
2017-09-14 12:56:50 +00:00
debug = = 1 ? //if debug in disabled return arc (default)
( newR - r [ 1 ] ) :
2018-02-18 21:46:25 +00:00
[ t23 , t34 , cen ] ;
function round3points ( rp , fn ) =
rp [ 1 ] [ 2 ] = = 0 ? [ [ rp [ 1 ] [ 0 ] , rp [ 1 ] [ 1 ] ] ] : //return the middle point if the radius is 0
2017-09-14 12:56:50 +00:00
let (
p = getpoints ( rp ) , //get list of points
2018-02-18 21:46:25 +00:00
r = rp [ 1 ] [ 2 ] , //get the centre 3 radii
2017-09-14 12:56:50 +00:00
ang = cosineRuleAngle ( p [ 0 ] , p [ 1 ] , p [ 2 ] ) , //angle between the lines
2017-08-01 10:04:29 +00:00
//now that the radius has been determined, find tangent points and circle centre
2018-02-18 21:46:25 +00:00
tangD = r / tan ( ang / 2 ) , //distance to the tangent point from p2
2017-09-14 12:56:50 +00:00
circD = r / sin ( ang / 2 ) , //distance to the circle centre from p2
2018-02-18 21:46:25 +00:00
//find the angles from the p2 with respect to the postitive x axis
2020-07-26 18:18:28 +10:00
angleFromPoint1ToPoint2 = getAngle ( p [ 0 ] , p [ 1 ] ) ,
angleFromPoint2ToPoint3 = getAngle ( p [ 2 ] , p [ 1 ] ) ,
2018-02-18 21:46:25 +00:00
//find tangent points
2020-07-26 18:18:28 +10:00
t12 = [ p [ 1 ] [ 0 ] - cos ( angleFromPoint1ToPoint2 ) * tangD , p [ 1 ] [ 1 ] - sin ( angleFromPoint1ToPoint2 ) * tangD ] , //tangent point between points 1&2
t23 = [ p [ 1 ] [ 0 ] - cos ( angleFromPoint2ToPoint3 ) * tangD , p [ 1 ] [ 1 ] - sin ( angleFromPoint2ToPoint3 ) * tangD ] , //tangent point between points 2&3
2017-08-01 10:04:29 +00:00
//find circle centre
2018-02-18 21:46:25 +00:00
tmid = getMidpoint ( t12 , t23 ) , //midpoint between the two tangent points
angCen = getAngle ( tmid , p [ 1 ] ) , //angle from point 2 to circle centre
cen = [ p [ 1 ] [ 0 ] - cos ( angCen ) * circD , p [ 1 ] [ 1 ] - sin ( angCen ) * circD ] //circle center by offseting from point 2
)
[ t12 , t23 , cen ] ;
function parallelFollow ( rp , thick = 4 , minR = 1 , mode = 1 ) =
2017-09-14 12:56:50 +00:00
//rp[1][2]==0?[rp[1][0],rp[1][1],0]://return the middle point if the radius is 0
thick = = 0 ? [ rp [ 1 ] [ 0 ] , rp [ 1 ] [ 1 ] , 0 ] : //return the middle point if the radius is 0
let (
p = getpoints ( rp ) , //get list of points
2018-02-18 21:46:25 +00:00
r = thick , //get the centre 3 radii
2017-09-14 12:56:50 +00:00
ang = cosineRuleAngle ( p [ 0 ] , p [ 1 ] , p [ 2 ] ) , //angle between the lines
//now that the radius has been determined, find tangent points and circle centre
2018-02-18 21:46:25 +00:00
tangD = r / tan ( ang / 2 ) , //distance to the tangent point from p2
sgn = CWorCCW ( rp ) , //rotation of the three points cw or ccw?let(sgn=mode==0?1:-1)
2017-09-14 12:56:50 +00:00
circD = mode * sgn * r / sin ( ang / 2 ) , //distance to the circle centre from p2
2018-02-18 21:46:25 +00:00
//find the angles from the p2 with respect to the postitive x axis
2020-07-26 18:18:28 +10:00
angleFromPoint1ToPoint2 = getAngle ( p [ 0 ] , p [ 1 ] ) ,
angleFromPoint2ToPoint3 = getAngle ( p [ 2 ] , p [ 1 ] ) ,
2018-02-18 21:46:25 +00:00
//find tangent points
2020-07-26 18:18:28 +10:00
t12 = [ p [ 1 ] [ 0 ] - cos ( angleFromPoint1ToPoint2 ) * tangD , p [ 1 ] [ 1 ] - sin ( angleFromPoint1ToPoint2 ) * tangD ] , //tangent point between points 1&2
t23 = [ p [ 1 ] [ 0 ] - cos ( angleFromPoint2ToPoint3 ) * tangD , p [ 1 ] [ 1 ] - sin ( angleFromPoint2ToPoint3 ) * tangD ] , //tangent point between points 2&3
2017-09-14 12:56:50 +00:00
//find circle centre
2018-02-18 21:46:25 +00:00
tmid = getMidpoint ( t12 , t23 ) , //midpoint between the two tangent points
angCen = getAngle ( tmid , p [ 1 ] ) , //angle from point 2 to circle centre
cen = [ p [ 1 ] [ 0 ] - cos ( angCen ) * circD , p [ 1 ] [ 1 ] - sin ( angCen ) * circD ] , //circle center by offseting from point 2
outR = max ( minR , rp [ 1 ] [ 2 ] - thick * sgn * mode ) //ensures radii are never too small.
)
concat ( cen , outR ) ;
2020-11-02 18:12:17 +11:00
function is90or270 ( ang ) = ang = = 90 ? 1 : ang = = 270 ? 1 : 0 ;
2018-02-18 21:46:25 +00:00
function findPoint ( ang1 , refpoint1 , ang2 , refpoint2 , r = 0 ) =
2020-10-05 20:41:09 +11:00
// finds the intersection of two lines given two angles and points on those lines
2018-02-18 21:46:25 +00:00
let (
2020-11-02 18:12:17 +11:00
overrideX = is90or270 ( ang1 ) ?
refpoint1 . x :
is90or270 ( ang2 ) ?
refpoint2 . x :
0 ,
2018-02-18 21:46:25 +00:00
m1 = tan ( ang1 ) ,
c1 = refpoint1 . y - m1 * refpoint1 . x ,
m2 = tan ( ang2 ) ,
c2 = refpoint2 . y - m2 * refpoint2 . x ,
2020-11-02 18:12:17 +11:00
outputX = overrideX ? overrideX : ( c2 - c1 ) / ( m1 - m2 ) ,
outputY = is90or270 ( ang1 ) ? m2 * outputX + c2 : m1 * outputX + c1
2018-02-18 21:46:25 +00:00
)
2017-09-14 12:56:50 +00:00
[ outputX , outputY , r ] ;
2018-02-18 21:46:25 +00:00
2020-08-06 07:41:49 +10:00
function beamChain ( radiiPoints , offset1 = 0 , offset2 , mode = 0 , minR = 0 , startAngle , endAngle ) =
/*This function takes a series of radii points and plots points to run along side at a consistant distance, think of it as offset but for line instead of a polygon
radiiPoints = radii points ,
offset1 & offset2 = The two offsets that give the beam it ' s thickness . When using with mode = 2 only offset1 is needed as there is no return path for the polygon
minR = min radius , if all of your radii are set properly within the radii points this value can be ignored
startAngle & endAngle = Angle at each end of the beam , different mode determine if this angle is relative to the ending legs of the beam or absolute .
2020-07-26 18:18:28 +10:00
mode = 1 - include endpoints startAngle & 2 are relative to the angle of the last two points and equal 90 deg if not defined
2020-08-06 07:41:49 +10:00
mode = 2 - Only the forward path is defined , useful for combining the beam with other radii points , see examples for a use - case .
2020-07-26 18:18:28 +10:00
mode = 3 - include endpoints startAngle & 2 are absolute from the x axis and are 0 if not defined
2018-02-18 21:46:25 +00:00
negative radiuses only allowed for the first and last radii points
As it stands this function could probably be tidied a lot , but it works , I ' ll tidy later * /
let (
2020-07-26 18:18:28 +10:00
offset2undef = offset2 = = undef ? 1 : 0 ,
offset2 = offset2undef = = 1 ? 0 : offset2 ,
2020-08-06 07:41:49 +10:00
CWorCCW1 = sign ( offset1 ) * CWorCCW ( radiiPoints ) ,
CWorCCW2 = sign ( offset2 ) * CWorCCW ( radiiPoints ) ,
2020-07-26 18:18:28 +10:00
offset1 = abs ( offset1 ) ,
offset2b = abs ( offset2 ) ,
2020-08-06 07:41:49 +10:00
Lrp3 = len ( radiiPoints ) - 3 ,
Lrp = len ( radiiPoints ) ,
2020-07-26 18:18:28 +10:00
startAngle = mode = = 0 && startAngle = = undef ?
2020-08-06 07:41:49 +10:00
getAngle ( radiiPoints [ 0 ] , radiiPoints [ 1 ] ) + 90 :
2020-07-26 18:18:28 +10:00
mode = = 2 && startAngle = = undef ?
2018-02-18 21:46:25 +00:00
0 :
mode = = 0 ?
2020-08-06 07:41:49 +10:00
getAngle ( radiiPoints [ 0 ] , radiiPoints [ 1 ] ) + startAngle :
2020-07-26 18:18:28 +10:00
startAngle ,
endAngle = mode = = 0 && endAngle = = undef ?
2020-08-06 07:41:49 +10:00
getAngle ( radiiPoints [ Lrp - 1 ] , radiiPoints [ Lrp - 2 ] ) + 90 :
2020-07-26 18:18:28 +10:00
mode = = 2 && endAngle = = undef ?
2018-02-18 21:46:25 +00:00
0 :
mode = = 0 ?
2020-08-06 07:41:49 +10:00
getAngle ( radiiPoints [ Lrp - 1 ] , radiiPoints [ Lrp - 2 ] ) + endAngle :
2020-07-26 18:18:28 +10:00
endAngle ,
2020-08-06 07:41:49 +10:00
OffLn1 = [ for ( i = [ 0 : Lrp3 ] ) offset1 = = 0 ? radiiPoints [ i + 1 ] : parallelFollow ( [ radiiPoints [ i ] , radiiPoints [ i + 1 ] , radiiPoints [ i + 2 ] ] , offset1 , minR , mode = CWorCCW1 ) ] ,
2020-11-02 18:12:17 +11:00
OffLn2 = [ for ( i = [ 0 : Lrp3 ] ) offset2 = = 0 ? radiiPoints [ i + 1 ] : parallelFollow ( [ radiiPoints [ i ] , radiiPoints [ i + 1 ] , radiiPoints [ i + 2 ] ] , offset2b , minR , mode = CWorCCW2 ) ] ,
2020-08-06 07:41:49 +10:00
Rp1 = abs ( radiiPoints [ 0 ] . z ) ,
Rp2 = abs ( radiiPoints [ Lrp - 1 ] . z ) ,
2020-11-02 18:12:17 +11:00
endP1aAngle = getAngle ( radiiPoints [ 0 ] , radiiPoints [ 1 ] ) ,
endP1a = findPoint ( endP1aAngle , OffLn1 [ 0 ] , startAngle , radiiPoints [ 0 ] , Rp1 ) ,
endP1bAngle = getAngle ( radiiPoints [ Lrp - 1 ] , radiiPoints [ Lrp - 2 ] ) ,
endP1b = findPoint ( endP1bAngle , OffLn1 [ len ( OffLn1 ) - 1 ] , endAngle , radiiPoints [ Lrp - 1 ] , Rp2 ) ,
endP2aAngle = getAngle ( radiiPoints [ 0 ] , radiiPoints [ 1 ] ) ,
endP2a = findPoint ( endP2aAngle , OffLn2 [ 0 ] , startAngle , radiiPoints [ 0 ] , Rp1 ) ,
endP2bAngle = getAngle ( radiiPoints [ Lrp - 1 ] , radiiPoints [ Lrp - 2 ] ) ,
endP2b = findPoint ( endP2bAngle , OffLn2 [ len ( OffLn1 ) - 1 ] , endAngle , radiiPoints [ Lrp - 1 ] , Rp2 ) ,
2018-02-18 21:46:25 +00:00
absEnda = getAngle ( endP1a , endP2a ) ,
absEndb = getAngle ( endP1b , endP2b ) ,
2020-08-06 07:41:49 +10:00
negRP1a = [ cos ( absEnda ) * radiiPoints [ 0 ] . z * 10 + endP1a . x , sin ( absEnda ) * radiiPoints [ 0 ] . z * 10 + endP1a . y , 0.0 ] ,
negRP2a = [ cos ( absEnda ) * - radiiPoints [ 0 ] . z * 10 + endP2a . x , sin ( absEnda ) * - radiiPoints [ 0 ] . z * 10 + endP2a . y , 0.0 ] ,
negRP1b = [ cos ( absEndb ) * radiiPoints [ Lrp - 1 ] . z * 10 + endP1b . x , sin ( absEndb ) * radiiPoints [ Lrp - 1 ] . z * 10 + endP1b . y , 0.0 ] ,
negRP2b = [ cos ( absEndb ) * - radiiPoints [ Lrp - 1 ] . z * 10 + endP2b . x , sin ( absEndb ) * - radiiPoints [ Lrp - 1 ] . z * 10 + endP2b . y , 0.0 ] ,
OffLn1b = ( mode = = 0 || mode = = 2 ) && radiiPoints [ 0 ] . z < 0 && radiiPoints [ Lrp - 1 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ negRP1a ] , [ endP1a ] , OffLn1 , [ endP1b ] , [ negRP1b ] )
2020-08-06 07:41:49 +10:00
: ( mode = = 0 || mode = = 2 ) && radiiPoints [ 0 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ negRP1a ] , [ endP1a ] , OffLn1 , [ endP1b ] )
2020-08-06 07:41:49 +10:00
: ( mode = = 0 || mode = = 2 ) && radiiPoints [ Lrp - 1 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ endP1a ] , OffLn1 , [ endP1b ] , [ negRP1b ] )
: mode = = 0 || mode = = 2 ?
concat ( [ endP1a ] , OffLn1 , [ endP1b ] )
:
OffLn1 ,
2020-08-06 07:41:49 +10:00
OffLn2b = ( mode = = 0 || mode = = 2 ) && radiiPoints [ 0 ] . z < 0 && radiiPoints [ Lrp - 1 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ negRP2a ] , [ endP2a ] , OffLn2 , [ endP2b ] , [ negRP2b ] )
2020-08-06 07:41:49 +10:00
: ( mode = = 0 || mode = = 2 ) && radiiPoints [ 0 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ negRP2a ] , [ endP2a ] , OffLn2 , [ endP2b ] )
2020-08-06 07:41:49 +10:00
: ( mode = = 0 || mode = = 2 ) && radiiPoints [ Lrp - 1 ] . z < 0 ?
2018-02-18 21:46:25 +00:00
concat ( [ endP2a ] , OffLn2 , [ endP2b ] , [ negRP2b ] )
: mode = = 0 || mode = = 2 ?
concat ( [ endP2a ] , OffLn2 , [ endP2b ] )
:
OffLn2
) //end of let()
2020-07-26 18:18:28 +10:00
offset2undef = = 1 ? OffLn1b : concat ( OffLn2b , revList ( OffLn1b ) ) ;
2017-09-14 12:56:50 +00:00
2018-02-18 21:46:25 +00:00
function revList ( list ) = //reverse list
let ( Llist = len ( list ) - 1 )
[ for ( i = [ 0 : Llist ] ) list [ Llist - i ] ] ;
function CWorCCW ( p ) =
2017-09-14 12:56:50 +00:00
let (
Lp = len ( p ) ,
2018-02-18 21:46:25 +00:00
e = [ for ( i = [ 0 : Lp - 1 ] )
2020-07-22 19:56:49 +10:00
( p [ listWrap ( i + 0 , Lp ) ] . x - p [ listWrap ( i + 1 , Lp ) ] . x ) * ( p [ listWrap ( i + 0 , Lp ) ] . y + p [ listWrap ( i + 1 , Lp ) ] . y )
2018-02-18 21:46:25 +00:00
]
)
2023-08-04 11:02:11 +03:00
sign ( polySum ( e ) ) ;
2018-02-18 21:46:25 +00:00
function CentreN2PointsArc ( p1 , p2 , cen , mode = 0 , fn ) =
/* This function plots an arc from p1 to p2 with fn increments using the cen as the centre of the arc.
the mode determines how the arc is plotted
mode = = 0 , shortest arc possible
mode = = 1 , longest arc possible
mode = = 2 , plotted clockwise
mode = = 3 , plotted counter clockwise
* /
2017-09-14 12:56:50 +00:00
let (
2020-08-17 05:41:46 +10:00
isCWorCCW = CWorCCW ( [ cen , p1 , p2 ] ) , //determine the direction of rotation
2017-08-01 10:04:29 +00:00
//determine the arc angle depending on the mode
2017-09-14 12:56:50 +00:00
p1p2Angle = cosineRuleAngle ( p2 , cen , p1 ) ,
arcAngle =
2018-02-18 21:46:25 +00:00
mode = = 0 ? p1p2Angle :
mode = = 1 ? p1p2Angle - 360 :
2020-08-17 05:41:46 +10:00
mode = = 2 && isCWorCCW = = - 1 ? p1p2Angle :
mode = = 2 && isCWorCCW = = 1 ? p1p2Angle - 360 :
mode = = 3 && isCWorCCW = = 1 ? p1p2Angle :
mode = = 3 && isCWorCCW = = - 1 ? p1p2Angle - 360 :
cosineRuleAngle ( p2 , cen , p1 ) ,
2017-09-14 12:56:50 +00:00
r = pointDist ( p1 , cen ) , //determine the radius
2018-02-18 21:46:25 +00:00
p1Angle = getAngle ( cen , p1 ) //angle of line 1
)
2020-08-17 05:41:46 +10:00
[ for ( i = [ 0 : fn ] )
let ( angleIncrement = ( arcAngle / fn ) * i * isCWorCCW )
[ cos ( p1Angle + angleIncrement ) * r + cen . x , sin ( p1Angle + angleIncrement ) * r + cen . y ] ] ;
2018-02-18 21:46:25 +00:00
2020-08-06 07:41:49 +10:00
function translateRadiiPoints ( radiiPoints , tran = [ 0 , 0 ] , rot = 0 ) =
[ for ( i = radiiPoints )
2017-09-14 12:56:50 +00:00
let (
2018-02-18 21:46:25 +00:00
a = getAngle ( [ 0 , 0 ] , [ i . x , i . y ] ) , //get the angle of the this point
h = pointDist ( [ 0 , 0 ] , [ i . x , i . y ] ) //get the hypotenuse/radius
)
2017-09-14 12:56:50 +00:00
[ h * cos ( a + rot ) + tran . x , h * sin ( a + rot ) + tran . y , i . z ] //calculate the point's new position
2018-02-18 21:46:25 +00:00
] ;
2017-10-14 08:18:08 +00:00
module round2d ( OR = 3 , IR = 1 ) {
2020-07-26 18:18:28 +10:00
offset ( OR , $fn = 100 ) {
offset ( - IR - OR , $fn = 100 ) {
offset ( IR , $fn = 100 ) {
2018-02-18 21:46:25 +00:00
children ( ) ;
}
}
}
2017-10-14 08:18:08 +00:00
}
2018-02-18 21:46:25 +00:00
2020-08-06 07:41:49 +10:00
module shell2d ( offset1 , offset2 = 0 , minOR = 0 , minIR = 0 ) {
2017-10-14 08:18:08 +00:00
difference ( ) {
2020-08-06 07:41:49 +10:00
round2d ( minOR , minIR ) {
2020-07-26 18:18:28 +10:00
offset ( max ( offset1 , offset2 ) ) {
2018-02-18 21:46:25 +00:00
children ( 0 ) ; //original 1st child forms the outside of the shell
}
}
2020-08-06 07:41:49 +10:00
round2d ( minIR , minOR ) {
2018-02-18 21:46:25 +00:00
difference ( ) { //round the inside cutout
2020-07-26 18:18:28 +10:00
offset ( min ( offset1 , offset2 ) ) {
2018-02-18 21:46:25 +00:00
children ( 0 ) ; //shrink the 1st child to form the inside of the shell
}
if ( $children > 1 ) {
for ( i = [ 1 : $children - 1 ] ) {
children ( i ) ; //second child and onwards is used to add material to inside of the shell
}
}
}
2017-10-14 08:18:08 +00:00
}
}
}
2018-02-18 21:46:25 +00:00
2017-10-14 08:18:08 +00:00
module internalSq ( size , r , center = 0 ) {
tran = center = = 1 ? [ 0 , 0 ] : size / 2 ;
translate ( tran ) {
2018-02-18 21:46:25 +00:00
square ( size , true ) ;
offs = sin ( 45 ) * r ;
for ( i = [ - 1 , 1 ] , j = [ - 1 , 1 ] ) {
translate ( [ ( size . x / 2 - offs ) * i , ( size . y / 2 - offs ) * j ] ) circle ( r ) ;
}
}
2017-10-14 08:18:08 +00:00
}
2018-02-18 21:46:25 +00:00
2020-08-06 07:41:49 +10:00
module extrudeWithRadius ( length , r1 = 0 , r2 = 0 , fn = 30 ) {
2018-02-18 21:46:25 +00:00
n1 = sign ( r1 ) ; n2 = sign ( r2 ) ;
r1 = abs ( r1 ) ; r2 = abs ( r2 ) ;
translate ( [ 0 , 0 , r1 ] ) {
2020-08-06 07:41:49 +10:00
linear_extrude ( length - r1 - r2 ) {
2018-02-18 21:46:25 +00:00
children ( ) ;
}
}
2020-08-08 17:52:15 +10:00
for ( i = [ 0 : fn - 1 ] ) {
translate ( [ 0 , 0 , i / fn * r1 ] ) {
linear_extrude ( r1 / fn + 0.01 ) {
offset ( n1 * sqrt ( sq ( r1 ) - sq ( r1 - i / fn * r1 ) ) - n1 * r1 ) {
2018-02-18 21:46:25 +00:00
children ( ) ;
}
}
2017-10-14 08:18:08 +00:00
}
2020-08-08 17:52:15 +10:00
translate ( [ 0 , 0 , length - r2 + i / fn * r2 ] ) {
linear_extrude ( r2 / fn + 0.01 ) {
offset ( n2 * sqrt ( sq ( r2 ) - sq ( i / fn * r2 ) ) - n2 * r2 ) {
2018-02-18 21:46:25 +00:00
children ( ) ;
}
}
}
}
2017-10-14 08:18:08 +00:00
}
2018-02-18 15:15:59 +11:00
2020-08-06 07:41:49 +10:00
function mirrorPoints ( radiiPoints , rot = 0 , endAttenuation = [ 0 , 0 ] ) = //mirrors a list of points about Y, ignoring the first and last points and returning them in reverse order for use with polygon or polyRound
2018-02-18 21:46:25 +00:00
let (
2020-08-06 07:41:49 +10:00
a = translateRadiiPoints ( radiiPoints , [ 0 , 0 ] , - rot ) ,
temp3 = [ for ( i = [ 0 + endAttenuation [ 0 ] : len ( a ) - 1 - endAttenuation [ 1 ] ] )
2018-02-18 21:46:25 +00:00
[ a [ i ] [ 0 ] , - a [ i ] [ 1 ] , a [ i ] [ 2 ] ]
] ,
2020-07-26 18:18:28 +10:00
temp = translateRadiiPoints ( temp3 , [ 0 , 0 ] , rot ) ,
2018-02-18 21:46:25 +00:00
temp2 = revList ( temp3 )
)
2020-08-06 07:41:49 +10:00
concat ( radiiPoints , temp2 ) ;
2018-02-18 21:46:25 +00:00
2018-02-18 15:15:59 +11:00
function processRadiiPoints ( rp ) =
2020-08-17 05:41:46 +10:00
[ for ( i = [ 0 : len ( rp ) - 1 ] )
2018-02-18 21:46:25 +00:00
processRadiiPoints2 ( rp , i )
] ;
2018-02-18 15:15:59 +11:00
function processRadiiPoints2 ( list , end = 0 , idx = 0 , result = 0 ) =
2018-02-18 21:46:25 +00:00
idx >= end + 1 ? result :
processRadiiPoints2 ( list , end , idx + 1 , relationalRadiiPoints ( result , list [ idx ] ) ) ;
2018-02-18 15:15:59 +11:00
function cosineRuleBside ( a , c , C ) = c * cos ( C ) - sqrt ( sq ( a ) + sq ( c ) + sq ( cos ( C ) ) - sq ( c ) ) ;
2018-02-18 21:46:25 +00:00
2018-02-18 15:15:59 +11:00
function absArelR ( po , pn ) =
2018-02-18 21:46:25 +00:00
let (
th2 = atan ( po [ 1 ] / po [ 0 ] ) ,
r2 = sqrt ( sq ( po [ 0 ] ) + sq ( po [ 1 ] ) ) ,
r3 = cosineRuleBside ( r2 , pn [ 1 ] , th2 - pn [ 0 ] )
)
[ cos ( pn [ 0 ] ) * r3 , sin ( pn [ 0 ] ) * r3 , pn [ 2 ] ] ;
2018-02-18 15:15:59 +11:00
function relationalRadiiPoints ( po , pi ) =
2018-02-18 21:46:25 +00:00
let (
p0 = pi [ 0 ] ,
p1 = pi [ 1 ] ,
p2 = pi [ 2 ] ,
pv0 = pi [ 3 ] [ 0 ] ,
pv1 = pi [ 3 ] [ 1 ] ,
pt0 = pi [ 3 ] [ 2 ] ,
pt1 = pi [ 3 ] [ 3 ] ,
pn =
( pv0 = = "y" && pv1 = = "x" ) || ( pv0 = = "r" && pv1 = = "a" ) || ( pv0 = = "y" && pv1 = = "a" ) || ( pv0 = = "x" && pv1 = = "a" ) || ( pv0 = = "y" && pv1 = = "r" ) || ( pv0 = = "x" && pv1 = = "r" ) ?
[ p1 , p0 , p2 , concat ( pv1 , pv0 , pt1 , pt0 ) ] :
[ p0 , p1 , p2 , concat ( pv0 , pv1 , pt0 , pt1 ) ] ,
n0 = pn [ 0 ] ,
n1 = pn [ 1 ] ,
n2 = pn [ 2 ] ,
nv0 = pn [ 3 ] [ 0 ] ,
nv1 = pn [ 3 ] [ 1 ] ,
nt0 = pn [ 3 ] [ 2 ] ,
nt1 = pn [ 3 ] [ 3 ] ,
temp =
pn [ 0 ] = = "l" ?
[ po [ 0 ] , pn [ 1 ] , pn [ 2 ] ]
: pn [ 1 ] = = "l" ?
[ pn [ 0 ] , po [ 1 ] , pn [ 2 ] ]
: nv0 = = undef ?
[ pn [ 0 ] , pn [ 1 ] , pn [ 2 ] ] //abs x, abs y as default when undefined
: nv0 = = "a" ?
nv1 = = "r" ?
nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ cos ( n0 ) * n1 , sin ( n0 ) * n1 , n2 ] //abs angle, abs radius
: absArelR ( po , pn ) //abs angle rel radius
: nt1 = = "r" || nt1 = = undef ?
[ po [ 0 ] + cos ( pn [ 0 ] ) * pn [ 1 ] , po [ 1 ] + sin ( pn [ 0 ] ) * pn [ 1 ] , pn [ 2 ] ] //rel angle, rel radius
: [ pn [ 0 ] , pn [ 1 ] , pn [ 2 ] ] //rel angle, abs radius
: nv1 = = "x" ?
nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ pn [ 1 ] , pn [ 1 ] * tan ( pn [ 0 ] ) , pn [ 2 ] ] //abs angle, abs x
: [ po [ 0 ] + pn [ 1 ] , ( po [ 0 ] + pn [ 1 ] ) * tan ( pn [ 0 ] ) , pn [ 2 ] ] //abs angle rel x
2018-02-18 15:15:59 +11:00
: nt1 = = "r" || nt1 = = undef ?
2018-02-18 21:46:25 +00:00
[ po [ 0 ] + pn [ 1 ] , po [ 1 ] + pn [ 1 ] * tan ( pn [ 0 ] ) , pn [ 2 ] ] //rel angle, rel x
: [ pn [ 1 ] , po [ 1 ] + ( pn [ 1 ] - po [ 0 ] ) * tan ( pn [ 0 ] ) , pn [ 2 ] ] //rel angle, abs x
: nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ pn [ 1 ] / tan ( pn [ 0 ] ) , pn [ 1 ] , pn [ 2 ] ] //abs angle, abs y
: [ ( po [ 1 ] + pn [ 1 ] ) / tan ( pn [ 0 ] ) , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //abs angle rel y
: nt1 = = "r" || nt1 = = undef ?
[ po [ 0 ] + ( pn [ 1 ] - po [ 0 ] ) / tan ( 90 - pn [ 0 ] ) , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //rel angle, rel y
: [ po [ 0 ] + ( pn [ 1 ] - po [ 1 ] ) / tan ( pn [ 0 ] ) , pn [ 1 ] , pn [ 2 ] ] //rel angle, abs y
: nv0 = = "r" ?
nv1 = = "x" ?
nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ pn [ 1 ] , sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] ) ) , pn [ 2 ] ] //abs radius, abs x
: [ po [ 0 ] + pn [ 1 ] , sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( po [ 0 ] + pn [ 1 ] ) ) , pn [ 2 ] ] //abs radius rel x
: nt1 = = "r" || nt1 = = undef ?
[ po [ 0 ] + pn [ 1 ] , po [ 1 ] + sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] ) ) , pn [ 2 ] ] //rel radius, rel x
: [ pn [ 1 ] , po [ 1 ] + sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] - po [ 0 ] ) ) , pn [ 2 ] ] //rel radius, abs x
: nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] ) ) , pn [ 1 ] , pn [ 2 ] ] //abs radius, abs y
: [ sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( po [ 1 ] + pn [ 1 ] ) ) , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //abs radius rel y
: nt1 = = "r" || nt1 = = undef ?
[ po [ 0 ] + sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] ) ) , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //rel radius, rel y
: [ po [ 0 ] + sign ( pn [ 0 ] ) * sqrt ( sq ( pn [ 0 ] ) - sq ( pn [ 1 ] - po [ 1 ] ) ) , pn [ 1 ] , pn [ 2 ] ] //rel radius, abs y
: nt0 = = "a" ?
nt1 = = "a" || nt1 = = undef ?
[ pn [ 0 ] , pn [ 1 ] , pn [ 2 ] ] //abs x, abs y
: [ pn [ 0 ] , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //abs x rel y
: nt1 = = "r" || nt1 = = undef ?
[ po [ 0 ] + pn [ 0 ] , po [ 1 ] + pn [ 1 ] , pn [ 2 ] ] //rel x, rel y
: [ po [ 0 ] + pn [ 0 ] , pn [ 1 ] , pn [ 2 ] ] //rel x, abs y
)
temp ;
function invtan ( run , rise ) =
let ( a = abs ( atan ( rise / run ) ) )
rise = = 0 && run > 0 ?
0 : rise > 0 && run > 0 ?
a : rise > 0 && run = = 0 ?
90 : rise > 0 && run < 0 ?
180 - a : rise = = 0 && run < 0 ?
180 : rise < 0 && run < 0 ?
a + 180 : rise < 0 && run = = 0 ?
270 : rise < 0 && run > 0 ?
360 - a : "error" ;
function cosineRuleAngle ( p1 , p2 , p3 ) =
let (
2017-09-14 12:56:50 +00:00
p12 = abs ( pointDist ( p1 , p2 ) ) ,
p13 = abs ( pointDist ( p1 , p3 ) ) ,
p23 = abs ( pointDist ( p2 , p3 ) )
2018-02-18 21:46:25 +00:00
)
acos ( ( sq ( p23 ) + sq ( p12 ) - sq ( p13 ) ) / ( 2 * p23 * p12 ) ) ;
2023-08-04 11:02:11 +03:00
function polySum ( list , idx = 0 , result = 0 ) =
idx >= len ( list ) ? result : polySum ( list , idx + 1 , result + list [ idx ] ) ;
2018-02-18 21:46:25 +00:00
2017-07-22 11:50:59 +00:00
function sq ( x ) = x * x ;
2017-09-14 12:56:50 +00:00
function getGradient ( p1 , p2 ) = ( p2 . y - p1 . y ) / ( p2 . x - p1 . x ) ;
2017-10-14 08:18:08 +00:00
function getAngle ( p1 , p2 ) = p1 = = p2 ? 0 : invtan ( p2 [ 0 ] - p1 [ 0 ] , p2 [ 1 ] - p1 [ 1 ] ) ;
2017-08-01 10:04:29 +00:00
function getMidpoint ( p1 , p2 ) = [ ( p1 [ 0 ] + p2 [ 0 ] ) / 2 , ( p1 [ 1 ] + p2 [ 1 ] ) / 2 ] ; //returns the midpoint of two points
function pointDist ( p1 , p2 ) = sqrt ( abs ( sq ( p1 [ 0 ] - p2 [ 0 ] ) + sq ( p1 [ 1 ] - p2 [ 1 ] ) ) ) ; //returns the distance between two points
function isColinear ( p1 , p2 , p3 ) = getGradient ( p1 , p2 ) = = getGradient ( p2 , p3 ) ? 1 : 0 ; //return 1 if 3 points are colinear
2020-08-17 05:41:46 +10:00
module polyline ( p , width = 0.3 ) {
2018-02-18 21:46:25 +00:00
for ( i = [ 0 : max ( 0 , len ( p ) - 1 ) ] ) {
2020-08-17 05:41:46 +10:00
color ( [ i * 1 / len ( p ) , 1 - i * 1 / len ( p ) , 0 , 0.5 ] ) line ( p [ i ] , p [ listWrap ( i + 1 , len ( p ) ) ] , width ) ;
2018-02-18 21:46:25 +00:00
}
2017-08-01 10:04:29 +00:00
} // polyline plotter
2017-09-14 12:56:50 +00:00
module line ( p1 , p2 , width = 0.3 ) { // single line plotter
2018-02-18 21:46:25 +00:00
hull ( ) {
translate ( p1 ) {
circle ( width ) ;
}
translate ( p2 ) {
circle ( width ) ;
2017-08-01 10:04:29 +00:00
}
2018-02-18 21:46:25 +00:00
}
2017-08-01 10:04:29 +00:00
}
2018-02-18 21:46:25 +00:00
2017-08-01 10:04:29 +00:00
function getpoints ( p ) = [ for ( i = [ 0 : len ( p ) - 1 ] ) [ p [ i ] . x , p [ i ] . y ] ] ; // gets [x,y]list of[x,y,r]list
2020-07-22 19:56:49 +10:00
function listWrap ( x , x_max = 1 , x_min = 0 ) = ( ( ( x - x_min ) % ( x_max - x_min ) ) + ( x_max - x_min ) ) % ( x_max - x_min ) + x_min ; // wraps numbers inside boundaries
2018-02-18 21:46:25 +00:00
function rnd ( a = 1 , b = 0 , s = [ ] ) =
2017-08-01 10:04:29 +00:00
s = = [ ] ?
2020-07-22 19:56:49 +10:00
( rands ( min ( a , b ) , max ( a , b ) , 1 ) [ 0 ] ) : ( rands ( min ( a , b ) , max ( a , b ) , 1 , s ) [ 0 ] ) ; // nice rands wrapper