/* Beveled_Box.inc 2013 Sam Benge Beveled_Box() is like a Rounded_Box(), but with flat edges instead of rounded ones. Beveled_Rounded_Box() is like Beveled_Box(), except with post-bevel rounding applied to its edges. Neither object can be used in CSG operations, but unions are fine. There is no internal geometry, so the objects can be transparent and filled with media. Clipping and proximity patterns have not been tested yet. Interior textures are OK. Both objects are built out of clipped objects, so they render fairly quickly. The Quad macros are used internally by the other macros. Macros: Beveled_Box(P1, P2, Bev) P1: 1st corner P2: 2nd corner Bev: amount of beveling Beveled_Rounded_Box(P1, P2, Bev1, Bev2) P1: 1st corner P2: 2nd corner Bev1: amount of beveling Bev2: amount of post-bevel rounding Beveled_Box_Sub(P1, P2, Bev, NULL) Beveled_Box_Sub is the same as Beveled_Box, but has the same number of parameters as Beveled_Rounded_Box. This is makes it easy to use a simpler object in place of a more complex one :) Quad(P1, P2) P1: 1st corner P2: 2nd corner Quad_Inset(P1, P2, Inset) P1: 1st corner P2: 2nd corner Inset: amount of inset */ // useful, fast substitute for Beveled_Rounded_Box() #macro Beveled_Box_Sub(P1, P2, Bev, NULL) Beveled_Box(P1, P2, Bev) #end #macro Beveled_Box(P1, P2, Bev) #if(P1.x > P2.x) #local Tmp = P1.x; #local P1 = ; #local P2 = ; #end #if(P1.y > P2.y) #local Tmp = P1.y; #local P1 = ; #local P2 = ; #end #if(P1.z > P2.z) #local Tmp = P1.z; #local P1 = ; #local P2 = ; #end #local MinX = P2.x-P1.x; #local MinY = P2.y-P1.y; #local MinZ = P2.z-P1.z; #local Min = min(MinX, MinY, MinZ); #local Bev = min(Bev, Min/2) + 1e-7; // <- this is why it's always beveled #macro Inverse(Do) #if(Do) inverse #end #end #local DoInverse = true; union{ object{Quad(, )} object{Quad(, )Inverse(DoInverse)} object{Quad(, )} object{Quad(, )Inverse(DoInverse)} object{Quad(, )} object{Quad(, )inverse} #local ChamLen = vlength(y*Bev-x*Bev); object{Quad(, ) rotate x*315 translate <0, P1.y, P1.z+Bev>} object{Quad(, ) rotate x*315 translate <0, P2.y-Bev, P2.z> Inverse(DoInverse)} object{Quad(, ) rotate x*45 translate <0, P2.y-Bev, P1.z>} object{Quad(, ) rotate x*45 translate <0, P1.y, P2.z-Bev> Inverse(DoInverse)} object{Quad(<0, P1.y+Bev, 0>, ) rotate y*45 translate } object{Quad(<0, P1.y+Bev, 0>, ) rotate y*45 translate Inverse(DoInverse)} object{Quad(<0, P1.y+Bev, 0>, ) rotate y*315 translate } object{Quad(<0, P1.y+Bev, 0>, ) rotate y*315 translate Inverse(DoInverse)} object{Quad(<0, 0, P1.z+Bev>, <0, ChamLen, P2.z-Bev>) rotate z*45 translate } object{Quad(<0, 0, P1.z+Bev>, <0, ChamLen, P2.z-Bev>) rotate z*45 translate Inverse(DoInverse)} object{Quad(<0, 0, P1.z+Bev>, <0, ChamLen, P2.z-Bev>) rotate z*315 translate } object{Quad(<0, 0, P1.z+Bev>, <0, ChamLen, P2.z-Bev>) rotate z*315 translate Inverse(DoInverse)} #local TriObj = mesh{triangle{, <0, Bev, Bev>, } inside_vector -1} object{TriObj translate P1 inverse} object{TriObj scale -1 translate P2 inverse} object{TriObj scale <1, 1, -1> translate inverse} object{TriObj scale <-1, 1, 1> translate inverse} object{TriObj scale <-1, 1, -1> translate inverse} object{TriObj scale <1, -1, 1> translate inverse} object{TriObj scale <1, -1, -1> translate inverse} object{TriObj scale <-1, -1, 1> translate inverse} } #end #macro Beveled_Rounded_Box(P1, P2, Bev1, Bev2) #if(P1.x > P2.x) #local Tmp = P1.x; #local P1 = ; #local P2 = ; #end #if(P1.y > P2.y) #local Tmp = P1.y; #local P1 = ; #local P2 = ; #end #if(P1.z > P2.z) #local Tmp = P1.z; #local P1 = ; #local P2 = ; #end #local MinX = P2.x-P1.x; #local MinY = P2.y-P1.y; #local MinZ = P2.z-P1.z; #local Min = min(MinX, MinY, MinZ); #local Bev1 = min(Bev1, Min/2) + 1e-7; // <- this is why it's always beveled #local Bev2 = min(Bev1/2, Bev2, Min/2-Bev1) + 1e-7; // <- this is why it's always rounded #macro Inverse(Do) #if(Do) inverse #end #end #local DoInverse = true; union{ // sides object{Quad_Inset(, , Bev2)} object{Quad_Inset(, , Bev2)Inverse(DoInverse)} object{Quad_Inset(, , Bev2)} object{Quad_Inset(, , Bev2)Inverse(DoInverse)} object{Quad_Inset(, , Bev2)} object{Quad_Inset(, , Bev2)inverse} // bevels #local ChamLen = vlength(y*Bev1-x*Bev1); object{Quad_Inset(, , Bev2) rotate x*315 translate <0, P1.y, P1.z+Bev1>} object{Quad_Inset(, , Bev2) rotate x*315 translate <0, P2.y-Bev1, P2.z> Inverse(DoInverse)} object{Quad_Inset(, , Bev2) rotate x*45 translate <0, P2.y-Bev1, P1.z>} object{Quad_Inset(, , Bev2) rotate x*45 translate <0, P1.y, P2.z-Bev1> Inverse(DoInverse)} object{Quad_Inset(<0, P1.y+Bev1, 0>, , Bev2) rotate y*45 translate } object{Quad_Inset(<0, P1.y+Bev1, 0>, , Bev2) rotate y*45 translate Inverse(DoInverse)} object{Quad_Inset(<0, P1.y+Bev1, 0>, , Bev2) rotate y*315 translate } object{Quad_Inset(<0, P1.y+Bev1, 0>, , Bev2) rotate y*315 translate Inverse(DoInverse)} object{Quad_Inset(<0, 0, P1.z+Bev1>, <0, ChamLen, P2.z-Bev1>, Bev2) rotate z*45 translate } object{Quad_Inset(<0, 0, P1.z+Bev1>, <0, ChamLen, P2.z-Bev1>, Bev2) rotate z*45 translate Inverse(DoInverse)} object{Quad_Inset(<0, 0, P1.z+Bev1>, <0, ChamLen, P2.z-Bev1>, Bev2) rotate z*315 translate } object{Quad_Inset(<0, 0, P1.z+Bev1>, <0, ChamLen, P2.z-Bev1>, Bev2) rotate z*315 translate Inverse(DoInverse)} // corners #local T1 = ; #local T2 = <0, Bev1, Bev1>; #local T3 = ; #local T1T2 = (T1+T2)/2; #local T2T3 = (T2+T3)/2; #local T1T3 = (T3+T1)/2; #local TrObj = plane{y, Bev2} #local Tr = trace(TrObj, Bev2*vnormalize(<1, -1, 0>), x+y); // Z #local Tr1 = <0, Tr.y, Tr.x>; // X #local Tr2 = ; // Y #local Len = vlength(Tr-y*Bev2); // beveled edge rounding // X #local CylSlice = cylinder{ x*(P1.x+Bev1+Bev2), x*(P2.x-Bev1-Bev2), Len open clipped_by{plane{y, 0}} clipped_by{plane{y, 0 inverse rotate -x*45}} } object{CylSlice translate (<0, P1.y+Bev1, P1.z> + Tr1)} object{CylSlice rotate x*45 translate (<0, P2.y-Bev1, P1.z> + vrotate(Tr1, x*45))} object{CylSlice rotate x*90 translate (<0, P2.y, P1.z+Bev1> + vrotate(Tr1, x*90))} object{CylSlice rotate x*135 translate (<0, P2.y, P2.z-Bev1> + vrotate(Tr1, x*135))} object{CylSlice rotate x*180 translate (<0, P2.y-Bev1, P2.z> + vrotate(Tr1, x*180))} object{CylSlice rotate x*225 translate (<0, P1.y+Bev1, P2.z> + vrotate(Tr1, x*225))} object{CylSlice rotate x*270 translate (<0, P1.y, P2.z-Bev1> + vrotate(Tr1, x*270))} object{CylSlice rotate x*315 translate (<0, P1.y, P1.z+Bev1> + vrotate(Tr1, x*315))} // Y #local CylSlice = cylinder{ y*(P1.y+Bev1+Bev2), y*(P2.y-Bev1-Bev2), Len open clipped_by{plane{x, 0}} clipped_by{plane{x, 0 inverse rotate y*45}} } object{CylSlice translate ( + Tr2)} object{CylSlice rotate y*45 translate ( + vrotate(Tr2, y*45))} object{CylSlice rotate y*90 translate ( + vrotate(Tr2, y*90))} object{CylSlice rotate y*135 translate ( + vrotate(Tr2, y*135))} object{CylSlice rotate y*180 translate ( + vrotate(Tr2, y*180))} object{CylSlice rotate y*225 translate ( + vrotate(Tr2, y*225))} object{CylSlice rotate y*270 translate ( + vrotate(Tr2, y*270))} object{CylSlice rotate y*315 translate ( + vrotate(Tr2, y*315))} // Z #local CylSlice = cylinder{ z*(P1.z+Bev1+Bev2), z*(P2.z-Bev1-Bev2), Len open clipped_by{plane{y, 0}} clipped_by{plane{y, 0 inverse rotate z*45}} } object{CylSlice translate + Tr} object{CylSlice rotate z*45 translate + vrotate(Tr, z*45)} object{CylSlice rotate z*90 translate + vrotate(Tr, z*90)} object{CylSlice rotate z*135 translate + vrotate(Tr, z*135)} object{CylSlice rotate z*180 translate + vrotate(Tr, z*180)} object{CylSlice rotate z*225 translate + vrotate(Tr, z*225)} object{CylSlice rotate z*270 translate + vrotate(Tr, z*270)} object{CylSlice rotate z*315 translate + vrotate(Tr, z*315)} // corner object, triangle edge rounding #local TrObj = plane{1, Len translate } #local Tr = trace(TrObj, , 1); #local YRClip = vrotate(y*Bev2, x*45).y; #local S1 = ; #local S2 = ; #local S3 = ; #local SphSlice = sphere{ 0, Len clipped_by{plane{x, 0}} clipped_by{plane{z, 0}} clipped_by{plane{S2-S1, 0}} clipped_by{plane{S3-S1, 0}} } #local CornerObj = union{ object{ SphSlice translate S1 } object{ SphSlice Axis_Rotate_Trans(<1, 1, 1>, 120) translate S2 } object{ SphSlice Axis_Rotate_Trans(<1, 1, 1>, 240) translate S3 } #local TP1 = S1 + Len*vnormalize(-1); #local TP2 = S2 + Len*vnormalize(-1); #local TP3 = S3 + Len*vnormalize(-1); #local CylSlice = cylinder{ 0, S2-S1, Len open clipped_by{plane{x, 0}} clipped_by{plane{TP3 - (TP1+TP2)/2, 0}} } object{ CylSlice translate S1 } object{ CylSlice Axis_Rotate_Trans(<1, 1, 1>, 120) translate S2 } object{ CylSlice Axis_Rotate_Trans(<1, 1, 1>, 240) translate S3 } mesh{triangle{TP1, TP2, TP3} inside_vector 1} translate -P1 } object{CornerObj translate P1} object{CornerObj scale -1 translate P2} object{CornerObj scale <-1, 1, 1> translate } object{CornerObj scale <1, -1, 1> translate } object{CornerObj scale <-1, -1, 1> translate } object{CornerObj scale <1, 1, -1> translate } object{CornerObj scale <-1, 1, -1> translate } object{CornerObj scale <1, -1, -1> translate } } #end #macro Quad(P1, P2) #switch((P1.x=P2.x) + (P1.y=P2.y)*2 + (P1.z=P2.z)*4) #case(1) // X mesh{ triangle{P1, , P2} triangle{P1, P2, } inside_vector x } #break #case(2) // Y mesh{ triangle{P1, P2, } triangle{P1, , P2} inside_vector y } #break #case(4) // Z mesh{ triangle{P1, P2, } triangle{P1, , P2} inside_vector z } #break #case(!1 & !2 & !4) // not a quad #debug "Quad Macro Warning: Object is not a quad, making a cube instead.\n" box{P1, P2} #break #end #end #macro Quad_Inset(P1, P2, Inset) #switch((P1.x=P2.x) + (P1.y=P2.y)*2 + (P1.z=P2.z)*4) #case(1) // X mesh{ triangle{P1 + <0, Inset, Inset>, + <0, -Inset, Inset>, P2 - <0, Inset, Inset>} triangle{P1 + <0, Inset, Inset>, P2 - <0, Inset, Inset>, + <0, Inset, -Inset>} inside_vector x } #break #case(2) // Y mesh{ triangle{P1 + , P2 - , + <-Inset, 0, Inset>} triangle{P1 + , + , P2 - } inside_vector y } #break #case(4) // Z mesh{ triangle{P1 + , P2 - , + } triangle{P1 + , + <-Inset, Inset, 0>, P2 - } inside_vector z } #break #case(!1 & !2 & !4) // not a quad #debug "Quad Macro Warning: Object is not a quad, making a cube instead.\n" box{P1, P2} #break #end #end #macro Quad(P1, P2) #switch((P1.x=P2.x) + (P1.y=P2.y)*2 + (P1.z=P2.z)*4) #case(1) // X mesh{ triangle{P1, , P2} triangle{P1, P2, } inside_vector x } #break #case(2) // Y mesh{ triangle{P1, P2, } triangle{P1, , P2} inside_vector y } #break #case(4) // Z mesh{ triangle{P1, P2, } triangle{P1, , P2} inside_vector z } #break #case(!1 & !2 & !4) // not a quad #debug "Quad Macro Warning: Object is not a quad, making a box instead.\n" box{P1, P2} #break #end #end #macro Quad_Inset(P1, P2, Inset) #switch((P1.x=P2.x) + (P1.y=P2.y)*2 + (P1.z=P2.z)*4) #case(1) // X mesh{ triangle{P1 + <0, Inset, Inset>, + <0, -Inset, Inset>, P2 - <0, Inset, Inset>} triangle{P1 + <0, Inset, Inset>, P2 - <0, Inset, Inset>, + <0, Inset, -Inset>} inside_vector x } #break #case(2) // Y mesh{ triangle{P1 + , P2 - , + <-Inset, 0, Inset>} triangle{P1 + , + , P2 - } inside_vector y } #break #case(4) // Z mesh{ triangle{P1 + , P2 - , + } triangle{P1 + , + <-Inset, Inset, 0>, P2 - } inside_vector z } #break #case(!1 & !2 & !4) // not a quad #debug "Quad Macro Warning: Object is not a quad, making a box instead.\n" box{P1, P2} #break #end #end // swiped from transforms.inc #macro Axis_Rotate_Trans(Axis, Angle) #local vX = vaxis_rotate(x,Axis,Angle); #local vY = vaxis_rotate(y,Axis,Angle); #local vZ = vaxis_rotate(z,Axis,Angle); transform { matrix < vX.x,vX.y,vX.z, vY.x,vY.y,vY.z, vZ.x,vZ.y,vZ.z, 0,0,0 > } #end