= PLines in 3d — PLine3 = Field contains quite a bit of support for procedural 2d drawing. This page assumes that you are familiar with this "PLine" drawing system. If not, see BasicDrawing, and the series of pages around [wiki:DrawingPLines DrawingPLines] first. 2d is great and all, but what about 3d? For a long time !OpenEnded Group managed to get by with absolutely no 3d drawing support inside Field — after all, all of our 3d work was [BaseGraphicsSystem happening in another window]. Occasionally we did 3d work in 2d by just "doing the math" directly in Field. After all, you can write a function that filters the position of every part of a 2d {{{PLine}}} to make it appear to be in 3d. For various recent reasons this has changed. Most pressingly we're having much fun making [http://www.openendedgroup.com/index.php/in-progress/upend stereoscopic pieces]; we're also spending more time hanging out [http://hyposurface.org with architects] who are as a discipline constantly doing things that their tools are poorly designed for. Perhaps Field might grow into a platform that has something to say about architectural forms. == A start — support for PLine3 == As a start, we've included in beta7 (and the development tree) two pieces of the 3d drawing puzzle. Firstly there's {{{PLine3}}} class that's just like {{{PLine}}} but with 50% more dimension. Secondly, there's a "3d spline drawer" helper which is a lot like the normal "spline drawer" but adds the things that you typically need for doing 3d. Here's where we're aiming for right now: [[Image(target.png,430)]] This picture tells pretty much the complete story as of writing. Things to note: * '''{{{PLine}}} attributes''' work just the way that they normally do — see DrawingPLinesProperties. So you fill, color and so on in just the normal way. * Those dots you see are in fact the '''standard {{{PLine}}} editing tools''' (see BasicDrawing and LineBasedInteraction). You can drag nodes, split them, smooth them and so on just like you can in 2d. Mouse movement moves nodes at the "depth" that they are at, parallel to the viewing plane. * '''Fills work, but only when you fill planar things'''. Filling non-planar things gives you results that appear to be missing triangles. This is intentional. If you want to make complex, non flat surfaces, you need to use NURBS, see below. * Unlike the normal spline drawing helper, the contents of a 3d spline drawer is '''clipped to its frame'''. This means that you have one window per 3d spline drawer. Obviously you can have as many windows as you like. You can drag them around. * Associated with a 3d spline drawer is '''a camera'''. You can change the camera programmatically, or by using the mouse. Things that are not working that you might expect to work aren't shown in the above screenshot: * '''PDF export''' — pdf export is broken. You can't export 3d PLines that are placed inside a 3d spline drawer. This also breaks the rasterizer used to converts PLines into images. This is a high priority feature #191. * '''Cursor''' — (see [wiki:DrawingPLinesEditing), the {{{Cursor}}} class doesn't know anything about 3d, there needs to be a Cursor3 class. So, distances, intersections and so on based on PLines that happen to have depth will be computed as if {{{z=0}}} for all of the line. * '''{{{.stroke(...)}}}''' — exotic line styles generated using {{{.stroke(...)}}} don't work correctly in 3d. The stroking (thickness, and dash computation) is done as if {{{z=0}}} and then re-projected out to the correct z coordinate. This is only what you want if you are drawing in 2 and a half dimensions as a series of xy-planes that happen to have some depth. Things that are not working that will take some work and thought to implement: * '''A respectable surface class''' — lines in 3d is all well and good, but we need a {{{SurfaceCursor}}} class or something as an analog of the 2d line's {{Cursor}}}. We have some nascent NURBS support (and we'd like subdivision surfaces). == PLine3 basics == The example code largely speaks for itself: {{{ #!python _self.lines.clear() p1 = PLine3() p1.moveTo(0,0,0) p1.lineTo(1,0,4) p1.lineTo(1,1,4) p1.lineTo(0,1,0) p1.lineTo(0,0,0) p1.filled=1 p1.color=Vector4(0,0,0,0.5) p1 *= Vector3(10,10,10) _self.lines.add(p1) _self.camera.position[:]=(0,0,50) _self.camera.target[:]=(0,0,0) _self.camera.up[:]=(0,1,0) }}} Yields: [[Image(threed1.png,430)]] Things to note: * '''{{{.moveTo}}}''' and friends can now take 3 parameters rather than 2. {{{.cubicTo}}} takes 9 rather than 6. * ''' {{{*=}}}''' (rescale / rotate) and friends can now take {{{Vector3}}} rather than just {{{Vector2}}} and they rescale and rotate around the 3d center of the {{{PLine}}} by default * '''Pay close attention to where you put the camera'''. There's nothing worse than being lost in 3d. See [BaseGraphicsSystem#Thecamera here] for more information about 3d cameras. === Camera control === Two things might help in placing the camera. Firstly, the three-camera defaults to a position where drawing 2d things (at z=0) appears roughly where they would appear if you were drawing them into a "normal" spline drawer. That is with the origin in the top left in coordinates that are pixels. This is correct until you move the actual three-d spline drawer — then the camera comes along with the spline drawer. Secondly there's a mouse operated camera, very much like Maya. With the three-d spline viewer selected tap 'space'. Now (until you press space again), hold option. '''Left mouse rotates, middle mouse translates and right mouse pans.''' If all else is lost — '''press 'f' to frame all.''' [[Image(twoWindows.png,430)]] The above image shows two drawers (that happen to overlap) in camera navigation mode (with space pressed). == Early NURBS support == For a long time now NURBS (that's non-uniform rational b-splines) have been the ''de facto'' way of doing smoothly varying 3d pieces of geometry. Forget for a moment the textbook history of NURBS in [http://en.wikipedia.org/wiki/NURBS car and boat design] and just look at the influence that [http://www.rhino3d.com/ Rhino3d] has had on the field of Architecture in the last 15 years. NURBS share some features with the staple of 2d graphics — Field's {{{PLine}}} drawing model (and PDF's drawing model, and Quartz's drawing model and !NodeBox's drawing model etc...) of {{{.moveTo()}}} and {{{.cubicTo()}}}. Strictly speaking such cubic splines are uniform and non-rational. Non-uniform and rational are strictly superior, so anything you can {{{.cubicTo()}}} you can express in NURBS and many more things as well. Field builds its limited NURBS support out of {{{PLines}}}. We have both 2d NURBS curves and 3d NURBS surfaces. We'll start with curves. ==== Turn it on ==== '''First you have to turn on the "advancedGeometry" plugin in the [PaletteOverview#ThePluginandExtensionmanager plugin manager].''' Then. you can either go to [http://en.wikipedia.org/wiki/NURB wikipedia] or trust the following sentence: NURBS is just another way of interpolating the points that you put into a {{{PLine}}}. === NURBS in 2d === Let's start here, in a fresh, '''2d''' spline drawer, drawing a 2d spline. {{{ #!python line = PLine().moveTo(30,30).cubicTo(-150,30,-30,250,150,150) _self.lines.clear() _self.lines.add(line) }}} Yields: [[Image(n1.png)]] No surprises there. If we add {{{line.nurbs=1}}} to this you'll see exactly no change at all. (I told you that Field's PLine's were a subset of NURBS curves). Let's add another "cubic" segment: {{{ #!python line = PLine().moveTo(30,30).cubicTo(-150,30,-30,250,150,150).cubicTo(450,130,30,250,250,250) line.nurbs=0 _self.lines.clear() _self.lines.add(line) }}} Gives: [[Image(n2.png)]] Still, no surprises. Now we mark this {{{PLine}}} as being a NURBS, and change {{{line.nurbs=0}}} to {{{line.nurbs=1}}}. This gives us something different: [[Image(n3.png,430)]] It might be easier to see if we draw the "control points": [[Image(n4.png,430)]] Things to note: * Other than the first and last point the curve doesn't necessarily go through any of the points. This is in contrast to the way that {{{.cubicTo}}} and {{{.lineTo}}} normally work. * The curve is "completely" smooth (whereas with {{{.nurbs=0}}} it has a pointy bit between the two segments). * It's hard to put you're finger on it, but it feels different from a normal kind of cubic spline. * All of the red points in the image above (all of the ''control points'') are treated the same. It doesn't matter if you write {{{.cubicTo(cx1,cy1,cx2,cy2, x,y)}}} or {{{.lineTo(cx1,cy1).lineTo(cx2, cy2).lineTo(x,y)}}}. With that let's make that "simplification": {{{ #!python line = PLine().m(30,30).l(-150,30).l(-30,250).l(150,150).l(450,130).l(30,250).l(250,250) line.nurbs=1 _self.lines.clear() _self.lines.add(line) }}} (The line doesn't change). Here we're using the abbreviation {{{l}}} for {{{lineTo}}}. So far, so boring. What else have NURBS going for them? Two things: the 'R' (Rational) and the 'NU' (Non-Uniform). We'll look at the former here, and we'll level the latter for another update. ==== Node Weights ==== In addition to having a position, control points in NURBS can have weights as well. Just as we use a [wiki:DrawingPLines vertex property] ({{{z_v}}}) to set the depth as a per vertex thing, we use a vertex property {{{w_v}}} to set the weight: {{{ #!python line = PLine().m(30,30).l(-150,30).l(-30,250) line.w_v = 30.0 line.l(150,150).l(450,130).l(30,250).l(250,250) line.samples=300 #increase the resolution of the line _self.lines.clear() _self.lines.add(line) }}} This gives us: [[Image(n5.png,430)]] (I've left the original line in in light grey). By increasing the weight of the third node considerably (normally it's 1.0) we cause the line to try much, much harder to move through that point. That level of control is one of the benefits of NURBS. === NURBS in 3d — surfaces ? === Well that's good summary of where we are right now (in beta7) for NURBS curves in 2d. What about 3d? Firstly, NURBS curves work just as you'd expect in 3d. Use the convenience of {{{PLine3}}}, and make sure you set {{{.nurbs=1}}} and there you go. What about surfaces? For those familiar with the drawing functions in Field, this should come as no surprise, we use {{{PLine / PLine3}}} for those as well. Here's where we're heading: [[Image(n6.png, 430)]] A parade of little NURBS surface arbitrary doodles. And here's the arbitrary code to get there: {{{ #!python _self.lines.clear() def drawSurface(a,b,c, off): line = [] for n in range(0, 4): p3 = PLine3() p3.moveTo(30+n*10+b,230+a,100+n*-10) p3.cubicTo(150+n*10,300-n*40-c*n,30, 30+n*10,150,0,150+n*10,250,-10) p3 += off p3(color=Vector4(0,0,0,0.2)) line.append(p3) head = line[0] head.attachSurfaceLines(line) head(color=Vector4(0,0,0,0.1)) #draw ths surface itself _self.lines.add(head) #and draw the lines just for show _self.lines.addAll(line[1:]) for n in range(0, 10): drawSurface(n*4,Math.sin(n/3.0)*50,-n*Math.cos(n/1.0)*4, Vector3(-100+n*30,0,0)) }}} If you run the code above, don't forget to set the {{{_self.camera}}} to something sensible. The really important call is to '''{{{.attachSurfaceLines()}}}'''. That takes a list of {{{PLine}}} and and associates them with just one of them. That line and the list are then the {{{PLine}}} that gives you the NURBS surface. Add a {{{_self.tweaks()}}} to the code above and you can edit the NURBS control surfaces live with the mouse, just like any other {{{PLine}}}. == NURBS, where next? == Right now we are at a very intermediate point with this kind of thing. The priorities (in order): * We can't customize the "knot vector" (that's the ''Non-Uniform'' part of NURBS). * Obviously our flat, single color, renderer is about the least impressive way that you can show a surface. Using BaseGraphicsSystem we should be able to interpose much better custom renderers. * There needs to be a way of exporting these pieces of geometry to something industry standard (Collada?) * We need the equivalent of {{{Cursor}}} for NURBS {{{PLine}}} and a surface evaluation class. None of these things are particularly hard. It's work in progress. Some feedback would help set priorities. == Mouse Editing works (and that's harder than you think) == The summary is that {{{_self.tweaks()}}} does exactly what you'd expect — see BasicDrawing. Mouse editing on 3d objects gets replayed precisely — nodes that are manipulated get projected onto the 2d 'screen', manipulated, and then put back again to the same depth. This is basically what you'd expect for the simplest thing. Clearly it would be nice to have a greater range of tools, including some that are much more 3d aware, and, since Field's mouse-tool system is already very extensible, this will be pretty straightforward Getting this to work was a little lest straightforward than you might think, because the simplest case of a mouse edit that we've just described is ''view dependent'' — you get a different edit from the same mouse drag if the camera is in a different spot. So, the camera position where edits take place is recorded as well. In any case, now that this machinery is in place we can grow some interesting and code-based 3d mouse editing tools pretty rapidly.