# Joining rectangular blocks

17 messages
Open this post in threaded view
|

## Joining rectangular blocks

 This seems like it should be relatively easy, and I'm only coming up with hard ways to do it. I have several rectangular prisms ("cubes", in OpenSCAD) that meet at corners, like so: I want to extend them so that they meet at the "outside" corner instead of the "inside" corner.  (CorelDRAW calls this a "mitered" corner.) I have as inputs the coordinates of the "inside" corners.  I need the coordinates of those "outside" corners.  (Simply extending the blocks isn't enough - I need the dimensions of the "new" blocks.) Simplifying information:  the angles between the blocks is always more than 90 degrees.  (In fact, at the moment it's always 135 degrees, but I'd rather not assume that.)  I doubt this makes the geometry much simpler, but it means that I can just overlap cubes "inside" the line without having their corners protrude. Another way to look at it is that I have a list of points - *not* a closed polygon - describing the "inside" corners and I want to apply an operation a lot like offset( ) with a positive delta to them to get the points describing the "outside" corners, kind of like so: I'm not picky about the behavior at the ends, though moving the endpoints directly perpendicular to the line segments would be my first choice. The answers that I'm coming up with involve using trig to get the angles of each of them and then using more trig to get the "height" of the new point "above" the current point, and then still more trig to rotate that height into the right orientation to add to the original point to get to the final point.  It sure seems like it should be easier than that. If you're wondering why I want to do this:  the inside of the object needs to fit up against the outside of another object, and I want to lay down a texture on the outside so I need to know exactly where the "outside" is - or, more precisely, when I make the cubes I want to lay texture on top of each one before I rotate it into position.  It's the roof of a barn, and I want to put shingles on it. Two other solutions that I've played with: Turn it into a closed polygon, offset it, subtract the original, clip off the bottom, and extrude.  But then I can't easily put the shingles on. Fill the corners with clipped cylinders, so they're rounded.  But then I'd have to bend shingles around the corner. (It just goes to show how OCD I am that I've spent a couple of hours messing with this, when I only need to do it for a total of maybe four such intersections, and four degenerate cases at the ends.  It would have been much faster to just fudge the values into working.  But it wouldn't be *right*.) _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 Jordan Brown <[hidden email]> writes: > I want to extend them so that they meet at the "outside" corner instead of > the "inside" corner. (CorelDRAW calls this a "mitered" corner.) > > I have as inputs the coordinates of the "inside" corners. I need the > coordinates of those "outside" corners. > The answers that I'm coming up with involve using trig to get the angles > of each of them and then using more trig to get the "height" of the new Here is one way, using equal-angled triangles. There is a function to calculate the outer point list from the inner point list, and then some simple stuff to render the results as points and as a polyhedron. Not sure if this is exactly how you need it (you mentioned having separate cubes for each part), but from the list of outer points I assume you can get what you want. Hope this helps,  - Kristian. // A problem from the mailing list. // A roof on which to put shingles. // The inside is given py points p0, p1, p2, ... // The outside is a given thickness away. // Wants to compute the outside points. // // Given P0, P1, P2, find P'1. Assume |P0-P1| = |P1-P2|. // Introduce Q1 = (P0+P2)/2. The Point P'1 is on the line Q1-P1. // Introduce the point R1 which is the point on the outside of the roof // directly above P1 perpendicular to the side P0-P1. // Triangles P0-Q1-P1 and P1-R1-P'1 have equal angles, so we get: // //   |P1-P'1| / |P1-R1| = |P0-P1| / |P0-Q1| //   l := |P1-P'1| = T * |P0-P1| / |P0-Q1| //   P'1 = P1 + l*(P1-Q1)/|P1-Q1| // // where l is the length of P'1-P1 and T is the thickness of the roof. // // For an "end" point P0, we find a vector perpendicular to (P2-P1) and in // the same plane as P0-P1-P2, and use that direction to offset T from P0. thickness = 2; dot = 0.5; function vec_len(v) = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); function calc_outer_point(T, p0, p1, p2) =   // Normalise p0-p1 and p2-p1.   let (v01 = (p0-p1)/vec_len(p0-p1),        v21 = (p2-p1)/vec_len(p2-p1),        q1 = p1 + .5*(v01+v21),        l = T * 1 / vec_len(p1 + v01 - q1),        p1m = p1 + l*(p1-q1)/vec_len(p1-q1))   p1m; function calc_outer_point_end(T, p0, p1, p2) =   let (n = cross((p1-p2), (p0-p1)),        v = cross((p0-p1), n),        l = vec_len(v))   p0 + T * v/l; function outer_point_list(T, list) =   [calc_outer_point_end(T, list[0], list[1], list[2]),    for (i = [1 : len(list)-2])      calc_outer_point(T, list[i-1], list[i], list[i+1]),    calc_outer_point_end(T, list[len(list)-1], list[len(list)-2], list[len(list)-3])    ]; function as_polyhedron(ps, v) =   let   (n = len(ps),    faces =      [       // Front       [for (i = [n/2 : 1 : n-1]) i, for (i = [n/2-1 : -1 : 0]) i],       // Sides       for (i = [0 : 1 : n/2-2])  [i, i+1, n+i+1, n+i],                                  [n/2-1, n-1, n+n-1, n+n/2-1],       for (i = [n-2 : -1 : n/2]) [i+1, i, n+i, n+i+1],                                  [n/2, 0, n, n+n/2],       // Back       [for (i = [n : 1 : n+n/2-1]) i, for (i = [2*n-1 : -1 : n+n/2]) i],       ],    points = [for (i = [0 : n-1]) ps[i], for (i = [0 : n-1]) ps[i] + v]    )   [points, faces]; point_list = [[0,0,0], [10,10,0], [20,10,0], [30, 0, 0]]; outer_list = outer_point_list(thickness, point_list); polyhedron_data = as_polyhedron(concat(point_list, outer_list), [0, 0, -50]); for (i = [0 : len(point_list)-1]) {   color("red") {     translate(point_list[i])       cube([dot, dot, dot]);   } } for (i = [0 : len(outer_list)-1]) {   color("blue") {     translate(outer_list[i])       cube([dot, dot, dot]);   } } translate ([0, 15, 0]) {   polyhedron(points=polyhedron_data[0], faces=polyhedron_data[1]); } _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org calc.png (6K) Download Attachment render.png (12K) Download Attachment
Open this post in threaded view
|

## Re: Joining rectangular blocks

 This post was updated on . In reply to this post by JordanBrown Yeah, you can use trivial vector arithmetic, esp. if it is clear that the sequence is convex. See the following code. It constructs a barn with CiH (use your own coords for P) and calculates the desired points with offs(). n = 3; P = CiH(100, n);  // list with n inner points P_ = concat([undef], P, [undef]);  // add boundary cases X = [for(p = [len(P_)-3:-1:0])   (offs(P_[p], P_[p+1], P_[p+2], 30))]; // calc offsets polygon(concat(P, X)); function offs(A=[0,0], B=[0,1], C=undef, x=1) =  let(a = B-A, b = C-B)  let(an = a/norm(a))  let(bn = b/norm(b))  (A!=undef && C!=undef)?       let(w = acos(an*bn)/2)  // half angle     let(k = x/cos(w))       // distance from point     B+(an-bn)/norm(an-bn)*k:  (C!=undef)? // boundaries     B+x*[bn[1], -bn[0]]:     B+x*[an[1], -an[0]]; function CiH(r, N) = [for(i=[0:N]) let(a=180/N*i) r*[cos(a), sin(a)]]; edit: inital post didn't contain the full code. Update also uses k. -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list Discuss@lists.openscad.org http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 In reply to this post by JordanBrown I don't think there's a way to do this that doesn't, ultimately, involve finding the normals to the line segments so you can compute the shifted segments and then and then computing the intersection of the line segments. Note that this can be done without using any trig.  I wrote an offset() function that does this all and produces the offset point list.  Perhaps that does what you want?  It's in BOSL2.   include test = turtle(["move", "right", 35, "move", "right", 45, "move"]); otest = offset(test, delta=.2); stroke(test,width=.01); color("red")stroke(otest, width=0.01); -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

Open this post in threaded view
|

## Re: Joining rectangular blocks

 In reply to this post by adrianv Interesting. I used to have a great time with LOGO, and not just with the turtle graphics. Is there a way to get the position and angle from the state after a move or stroke? -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 Yes, the turtle() function can return its state if you request it.  (I did that so you could chain turtle commands if you wanted to.  I'm not sure what other benefit it would have.)   If you have any thoughts on improving it I'm open to suggestions.  The documentation is here: https://github.com/revarbat/BOSL2/wiki/shapes2d.scad#turtleThe stroke command is unrelated to turtle().  It takes any list of points and connects the dots.   lar3ry wrote > Interesting. I used to have a great time with LOGO, and not just with the > turtle graphics. > > Is there a way to get the position and angle from the state after a move > or > stroke? > > > > -- > Sent from: http://forum.openscad.org/> > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org-- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

Open this post in threaded view
|

## Re: Joining rectangular blocks

Open this post in threaded view
|

## Re: Joining rectangular blocks

 adrianv wrote > I guess I can document the state.  It didn't occur to me that people would > want to use it.   It is documented in a comment in the code...so I can > remember what it is.  :) > > It's this:  [ path, step_vector, default_angle] > > path is the list of points constructed so far by the turtle.  The turtle > position is the last point in the list, path[len(path)-1].   > > step_vector is the step vector produced by a "move" command, so it codes > both the current default scale factor and the direction. If you want an > actual angle you'll need to apply atan2 to it.   > > default_angle is the default angle you get with the turning commands. > > How do you want to position a rectangle?  It's not obvious that turtle is > the right solution to a problem like that.   To answer your question first, I wanted to find a solution for the problem posed in this thread. I could be all wet, but I figured I could use the turtle to find the point where the two boards would join together. It would go something like this: Use the turtle to draw a partial outline of a board, starting from 0/0, the upper left corner of the board. I would draw it clockwise, but instead of finishing up back at the origin, I would only make the first two moves, the second of which would be at an angle of <90 + next board angle> degrees. At this time the last two positions of the path would contain the point at which I want to join the next board and the point at which I would position the lower left point of the next board. Hmm... I just realized that I don't need the angle because I know the angle I want for the next board. I only have to preserve state for the next path. The next path would contain the state for position and angle, so a [right 180,move width] would take me back to the joining point, then I would do a [right 90, move length,right 90+next angle], giving me the same information as did the first board, returned in the path variable. I would then make the cubes, rotate/translate them to the positions found by the turtle, and plop them in there. The next board would be a repeat of the second one. I know I could use math to do this, but the turtle just seems WAY easier to me. So I know I don't really need to see the state, because I have the path, but just for kicks, how would I make the turtle path return the full state? -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 Here's the result of what I was trying to do. It's close, but not quite there, I think. I cheated by taking the coordinates from the output of the echo command, and pasting them into the second and third board translate()s. I did this because I am not sure how to extract the pertinent coordinates from the path. If you use "!" on the stroke command, you can see the path I made. include \$fn = 120; angle1 = 35; angle2 = 35; path = turtle(         [ // first board         "left",90,         "move", 3,         "right",90,         "move",30,         "right",90+angle1,         "move",3,         "right",180,         "move",3,         //second board         "right",90,         "move",30,         "right",90+angle2,         "move",3,         ]); union() {         board();         translate([28.2793, 0.542544,0 ])                 rotate([0,0,-angle1])                         board();         translate([51.7555, -15.2334,0 ])                 rotate([0,0,-(angle1+angle2)])                         board(); } module board() {         cube([30,3,2]); } stroke(path,width=.1); echo (path); -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

Open this post in threaded view
|

## Re: Joining rectangular blocks

 In reply to this post by lar3ry On 3/14/2020 10:34 PM, lar3ry wrote: Use the turtle to draw a partial outline of a board, starting from 0/0, the upper left corner of the board. I would draw it clockwise, but instead of finishing up back at the origin, I would only make the first two moves, the second of which would be at an angle of <90 + next board angle> degrees. At this time the last two positions of the path would contain the point at which I want to join the next board and the point at which I would position the lower left point of the next board. I had a similar problem, laying out walls for my house. Here's the function I used: ```function polaradd(origin, dir, length) =     [origin[0] + cos(dir)*length, origin[1] + sin(dir)*length];``` Given an original location in Cartesian coordinates and an offset in polar coordinates (angle and length), returns the new Cartesian location. Looking at that function again, it could be slightly simplified by using matrix arithmetic: ```function polaradd(origin, dir, length) =     origin + length * [ cos(dir), sin(dir) ]; ```  Actually, I used this one: `function vector(origin, dir, length) = polaradd(origin, 360-dir, length);` because I wanted to work in compass directions, and they're left-hand-rule.  (It didn't matter which axis North aligned with.  I don't think I ever thought about it. +X, it seems.  Once I had a start point I just turtled around.) _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 In reply to this post by adrianv adrianv wrote > If you want the full state info from turtle() pass full_state=true to it. > I > don't understand the remaining difficulty with your approach.  Isn't it > just > > union() { >         board(); >         translate(path[3]) >                 rotate([0,0,-angle1]) >                         board(); >         translate(path[6]) >                 rotate([0,0,-(angle1+angle2)]) >                         board(); > } Of course. That's what I did, but as I said, I cut and pasted the translation coordinates from the echo() output because I did not know how to extract them from the path. > One observation is that you are working inward instead of outward.  I > understood the problem to be fitting the rectangles onto a known surface, > which is more difficult than the problem you've solved.   Well, he did not specify anything except that he needed to fitgith it, and I only assumed that he knew the shape of what he wanted to enclose. I wasn't actually solving his problem specifically, only trying to explore a method of doing it. > Here's a different way to do what you did in BOSL2 that to me is a little > more intuitive: > > cube([30,3,2]) >   position(RIGHT+BACK) cuboid([30,3,2], anchor=LEFT+BACK, spin=-angle1) >   position(RIGHT+BACK) cuboid([30,3,2], anchor=LEFT+BACK, spin=-angle1); > > This code explicitly positions the cube corners on each other and applies > the desired rotation angle.   Yes, that works VERY well. I have yet to explore much of BOSL2. It's got a lot in it, and I am trying to get a good idea of what's available in it. I appreciate your work and the help. -- Sent from: http://forum.openscad.org/_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Open this post in threaded view
|

## Re: Joining rectangular blocks

 In reply to this post by JordanBrown On 3/14/2020 12:57 PM, Jordan Brown wrote: Thanks, all.  Good stuff to chew on. So I chewed.  The truly right answer is Adrian's - use a library - but I wanted to understand the math.  (And Adrian's implementation was too complete to easily extract just the math.) After some chewing, I understand some of the math. Kristian and Parkinbot's answers seemed similar, but even after throwing a fair amount of algebra at them I couldn't make them be the same. I ran some tests, and lo and behold, they aren't the same. Kristian's appears to be the one that I want, the "pointy" answer, the one equivalent to offset(delta=xxx, chamfer=false). Parkinbot's appears to be equivalent to offset(delta=xxx, chamfer=true) or offset(r=xxx).  Those are probably the more useful variants in general, but not for my particular case where I need the points connected by straight lines. Thanks! _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org