Images tools ln

ln The 3D Line Art Engine

ln is a vector-based 3D renderer written in Go. It is used to produce 2D vector graphics (think SVGs) depicting 3D scenes.

The output of an OpenGL pipeline is a rastered image. The output of ln is a set of 2D vector paths.

Motivation

I created this so I could plot 3D drawings with my Makeblock XY Plotter.

Here's one of my drawings from the plotter...

Installation

go get github.com/fogleman/ln/ln


Features

• Primitives
• Sphere
• Cube
• Triangle
• Cylinder
• 3D Functions
• Triangle Meshes
• OBJ & STL
• Vector-based "Texturing"
• CSG (Constructive Solid Geometry) Operations
• Intersection
• Difference
• Union
• Output to PNG or SVG

How it Works

To understand how ln works, it's useful to start with the Shape interface:

type Shape interface {
Paths() Paths
Intersect(Ray) Hit
Contains(Vector, float64) bool
BoundingBox() Box
Compile()
}

Each shape must provide some Paths which are 3D polylines on the surface of the solid. Ultimately anything drawn in the final image is based on these paths. These paths can be anything. For a sphere they could be lat/lng grid lines, a triangulated-looking surface, dots on the surface, etc. This is what we call vector-based texturing. Each built-in Shape ships with a default Paths function (e.g. a Cube simply draws the outline of a cube) but you can easily provide your own.

Each shape must also provide an Intersect method that lets the engine test for ray-solid intersection. This is how the engine knows what is visible to the camera and what is hidden.

All of the Paths are chopped up to some granularity and each point is tested by shooting a ray toward the camera. If there is no intersection, that point is visible. If there is an intersection, it is hidden and will not be rendered.

The visible points are then transformed into 2D space using transformation matrices. The result can then be rendered as PNG or SVG.

The Contains method is only needed for CSG (Constructive Solid Geometry) operations.

Hello World: A Single Cube

The Code

package main

import "github.com/fogleman/ln/ln"

func main() {
// create a scene and add a single cube
scene := ln.Scene{}
scene.Add(ln.NewCube(ln.Vector{-1, -1, -1}, ln.Vector{1, 1, 1}))

// define camera parameters
eye := ln.Vector{4, 3, 2}    // camera position
center := ln.Vector{0, 0, 0} // camera looks at
up := ln.Vector{0, 0, 1}     // up direction

// define rendering parameters
width := 1024.0  // rendered width
height := 1024.0 // rendered height
fovy := 50.0     // vertical field of view, degrees
znear := 0.1     // near z plane
zfar := 10.0     // far z plane
step := 0.01     // how finely to chop the paths for visibility testing

// compute 2D paths that depict the 3D scene
paths := scene.Render(eye, center, up, width, height, fovy, znear, zfar, step)

// render the paths in an image
paths.WriteToPNG("out.png", width, height)

// save the paths as an svg
paths.WriteToSVG("out.svg", width, height)
}

Custom Texturing

Suppose we want to draw cubes with vertical stripes on their sides, as shown in the skyscrapers example above. We can just define a new type and override the Paths() function.

type StripedCube struct {
ln.Cube
Stripes int
}

func (c *StripedCube) Paths() ln.Paths {
var paths ln.Paths
x1, y1, z1 := c.Min.X, c.Min.Y, c.Min.Z
x2, y2, z2 := c.Max.X, c.Max.Y, c.Max.Z
for i := 0; i <= c.Stripes; i++ {
p := float64(i) / float64(c.Stripes)
x := x1 + (x2-x1)*p
y := y1 + (y2-y1)*p
paths = append(paths, ln.Path{{x, y1, z1}, {x, y1, z2}})
paths = append(paths, ln.Path{{x, y2, z1}, {x, y2, z2}})
paths = append(paths, ln.Path{{x1, y, z1}, {x1, y, z2}})
paths = append(paths, ln.Path{{x2, y, z1}, {x2, y, z2}})
}
return paths
}

Now StripedCube instances can be added to the scene.

Constructive Solid Geometry (CSG)

You can easily construct complex solids using Intersection, Difference, Union.

shape := ln.NewDifference(
ln.NewIntersection(
ln.NewSphere(ln.Vector{}, 1),
ln.NewCube(ln.Vector{-0.8, -0.8, -0.8}, ln.Vector{0.8, 0.8, 0.8}),
),
ln.NewCylinder(0.4, -2, 2),
ln.NewTransformedShape(ln.NewCylinder(0.4, -2, 2), ln.Rotate(ln.Vector{1, 0, 0}, ln.Radians(90))),
ln.NewTransformedShape(ln.NewCylinder(0.4, -2, 2), ln.Rotate(ln.Vector{0, 1, 0}, ln.Radians(90))),
)

This is (Sphere & Cube) - (Cylinder | Cylinder | Cylinder).

Unfortunately, it's difficult to compute the joint formed at the boundaries of these combined shapes, so sufficient texturing is needed on the original solids for a decent result.

• Go noobz not supported

D:\src\go>md bin D:\src\go>md src D:\src\go>md pkg ... D:\src\go>set GOPATH=D:\src\go D:\src\go>go get github.com/fogleman/ln package github.com/fogleman/ln: no buildable Go source files in D:\src\go\src\github.com\fogleman\ln

If applicable, would you consider adding missing steps to readme.md to support this use case: I don't want to learn Go from scratch, I just want to use your app to create svg files?

opened by polycopter 5
• ln/path: use Draw2D instead of Cairo for a pure-Go build

This partially addresses fogleman/ln#3, with the benefit of not requiring OpenGL. I'm sure the performance is suboptimal, but this was so simple I couldn't resist.

opened by fogleman 4
• Error: Failed to download resource "libpng"

curl: (22) The requested URL returned error: 404 Not Found

opened by xiaoxu193 2
• Malloc deadlock in rendering large OBJ

Running

package main

import "github.com/fogleman/ln/ln"

func main() {
tree, err := ln.LoadOBJ("tree.obj")
if err != nil {
panic(err)
}

scene := ln.Scene{}

// define camera parameters
eye := ln.Vector{4, 3, 2}    // camera position
center := ln.Vector{0, 0, 0} // camera looks at
up := ln.Vector{0, 0, 1}     // up direction

// define rendering parameters
width := 1024.0  // rendered width
height := 1024.0 // rendered height
fovy := 50.0     // vertical field of view, degrees
znear := 0.1     // near z plane
zfar := 10.0     // far z plane
step := 0.01     // how finely to chop the paths for visibility testing

// compute 2D paths that depict the 3D scene
paths := scene.Render(eye, center, up, width, height, fovy, znear, zfar, step)

paths.WriteToPNG("out.png", width, height)

// save the paths as an svg
paths.WriteToSVG("out.svg", width, height)
}


with tree.obj being a large OBJ file (15MB) gives

panic: fatal error: malloc deadlock

runtime stack:
runtime.throw(0x151fc0, 0xf)
/home/pi/golang/go/src/runtime/panic.go:527 +0x78
runtime.mallocgc(0x21, 0x0, 0x3, 0x1864a8)
/home/pi/golang/go/src/runtime/malloc.go:513 +0x13c
runtime.rawstring(0x21, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/pi/golang/go/src/runtime/string.go:264 +0x64
runtime.rawstringtmp(0x0, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/pi/golang/go/src/runtime/string.go:107 +0xb4
runtime.concatstrings(0x0, 0x45f57a38, 0x2, 0x2, 0x0, 0x0)
/home/pi/golang/go/src/runtime/string.go:48 +0x1e4
runtime.concatstring2(0x0, 0x1523c0, 0xf, 0x1556e8, 0x12, 0x0, 0x0)
/home/pi/golang/go/src/runtime/string.go:58 +0x58
runtime.(*errorString).Error(0x569a4010, 0x0, 0x0)
<autogenerated>:2 +0xd8
runtime.printany(0x12b5b0, 0x569a4010)
/home/pi/golang/go/src/runtime/error.go:75 +0x140
runtime.gopanic(0x12b5b0, 0x569a4010)
/home/pi/golang/go/src/runtime/panic.go:352 +0x5c
runtime.panicindex()
/home/pi/golang/go/src/runtime/panic.go:12 +0x48
runtime.mHeap_Grow(0x1ed0e0, 0x8, 0x0)
/home/pi/golang/go/src/runtime/mheap.go:647 +0x25c
runtime.mHeap_AllocSpanLocked(0x1ed0e0, 0x1, 0x0)
/home/pi/golang/go/src/runtime/mheap.go:532 +0x6c8
runtime.mHeap_Alloc_m(0x1ed0e0, 0x1, 0xd, 0x0, 0x5d26c)
/home/pi/golang/go/src/runtime/mheap.go:425 +0x23c
runtime.mHeap_Alloc.func1()
/home/pi/golang/go/src/runtime/mheap.go:484 +0x40
runtime.systemstack(0x45f57be4)
/home/pi/golang/go/src/runtime/asm_arm.s:256 +0xa8
runtime.mHeap_Alloc(0x1ed0e0, 0x1, 0xd, 0x100, 0x1)
/home/pi/golang/go/src/runtime/mheap.go:485 +0x58
runtime.mCentral_Grow(0x1f20d0, 0x0)
/home/pi/golang/go/src/runtime/mcentral.go:190 +0xb0
/home/pi/golang/go/src/runtime/mcentral.go:86 +0x5e0
/home/pi/golang/go/src/runtime/mcache.go:118 +0xd4
runtime.mallocgc.func2()
/home/pi/golang/go/src/runtime/malloc.go:611 +0x28
runtime.systemstack(0x569b7400)
/home/pi/golang/go/src/runtime/asm_arm.s:242 +0x80
runtime.mstart()
/home/pi/golang/go/src/runtime/proc1.go:674

goroutine 1 [running]:
runtime.systemstack_switch()
/home/pi/golang/go/src/runtime/asm_arm.s:187 +0x4 fp=0x5ec8b90c sp=0x5ec8b908
runtime.mallocgc(0xc0, 0x0, 0x3, 0x8)
/home/pi/golang/go/src/runtime/malloc.go:612 +0x840 fp=0x5ec8b978 sp=0x5ec8b90c
runtime.rawmem(0xc0, 0x30)
/home/pi/golang/go/src/runtime/malloc.go:788 +0x30 fp=0x5ec8b98c sp=0x5ec8b978
runtime.growslice(0x136148, 0x768e44e0, 0x4, 0x4, 0x5, 0x0, 0x0, 0x0)
/home/pi/golang/go/src/runtime/slice.go:92 +0x230 fp=0x5ec8b9c4 sp=0x5ec8b98c
github.com/fogleman/ln/ln.Path.Chop(0x59a9e840, 0x2, 0x2, 0x47ae147b, 0x3f847ae1, 0x0, 0x0, 0x0)
/home/pi/golang/projects/src/github.com/fogleman/ln/ln/path.go:41 +0x434 fp=0x5ec8bb28 sp=0x5ec8b9c4
github.com/fogleman/ln/ln.Paths.Chop(0x61e14000, 0xe29c8, 0xe2aaa, 0x47ae147b, 0x3f847ae1, 0x0, 0x0, 0x0)
/home/pi/golang/projects/src/github.com/fogleman/ln/ln/path.go:131 +0xc0 fp=0x5ec8bb88 sp=0x5ec8bb28
github.com/fogleman/ln/ln.(*Scene).Render(0x576ac340, 0x0, 0x40100000, 0x0, 0x40080000, 0x0, 0x40000000, 0x0, 0x0, 0x0, ...)
/home/pi/golang/projects/src/github.com/fogleman/ln/ln/scene.go:47 +0x398 fp=0x5ec8be7c sp=0x5ec8bb88
main.main()
/home/pi/golang/projects/src/github.com/tdewolff/tree/main.go:28 +0x2e0 fp=0x5ec8bf9c sp=0x5ec8be7c
runtime.main()
/home/pi/golang/go/src/runtime/proc.go:111 +0x2b4 fp=0x5ec8bfc4 sp=0x5ec8bf9c
runtime.goexit()
/home/pi/golang/go/src/runtime/asm_arm.s:1036 +0x4 fp=0x5ec8bfc4 sp=0x5ec8bfc4

goroutine 17 [syscall, 2 minutes, locked to thread]:
runtime.goexit()
/home/pi/golang/go/src/runtime/asm_arm.s:1036 +0x4


on a Raspberry Pi 2B, Debian. The OBJ file I used is here: http://pi.tacodewolff.nl:8080/tree.obj

opened by tdewolff 1
• Requires cairo

fyi https://github.com/fogleman/ln/blob/73feb4b1f52c86fb2a25e8e82a0fc551dc5cf038/ln/path.go#L8 - requires a project which requires the cairo library, meaning there are external requirements. Would you take a PR for the docs?

opened by ukd1 1
• Cannot find cairo-pdf.h (fixed)

In case someone else has this problem: after running go get github.com/ungerik/go-cairo I got an error about cairo-pdf.h being not found; this was from the include cairo-pdf.h (among other headers), where the "include" no longer worked properly because of where cairo's files were held (recent Mac OX has an Xcode path instead of the /usr/include which causes problems).

Short story solution: installed some Xcode "command line developer tools" using xcode-select --install from the terminal. No more error. Not sure if this is the best way, but see here.

edit: called "command line developer tools"

opened by JRov 0
• Gravitional waves example

Hello,

I really like the ln library and I've been using it with a drawbot I've built. Thank you!

I am trying to generate the gravitational waves example from the image in the Readme. I've gone through all of the examples and I can't seem to figure out how to generate that. I've successfully generated run all of the other examples. Can you elaborate on how that specifically is done?

opened by speckone 2
• Orthographic Errors

Hi, I'm trying to do some tests using an orthographic matrix, but I seem to be getting some errors in the ray intersection. I don't know if I've set something up wrong.

eye := ln.Vector{-3, 4, 3}   // camera position
center := ln.Vector{0, 0, 0} // camera looks at
up := ln.Vector{0, 1, 0}     // up direction
znear := 0.1 // near z plane
zfar := 10.0 // far z plane
step := 0.01 // how finely to chop the paths for visibility testing
matrix := ln.LookAt(eye, center, up).Orthographic(-1, 1, -1, 1, znear, zfar)
paths := scene.RenderWithMatrix(matrix, eye, width, height, step)


As you can see, edges are clipped before they go behind objects.

opened by scisci 5
Michael Fogleman
Software Engineer at Formlabs
generativeart is a Go package to generate many kinds of generative art.

generativeart is a Go package to generate many kinds of generative art. The goal is to collect some excellent generative art (implemented in R or Processing), and rewrite them in Go again

825 Dec 29, 2022
Generate high-quality triangulated art from images.

An iterative algorithm to generate high quality triangulated images.

53 May 26, 2021
A cross-platform tool to convert images into ascii art and print them on the console

A cross-platform tool to convert images into ascii art and print them on the console

1.1k Dec 30, 2022
Convert images to computer generated art using delaunay triangulation.

▲ Triangle is a tool for generating triangulated image using delaunay triangulation. It takes a source image and converts it to an abstract image comp

2k Dec 29, 2022
Ascii-art-web

ASCII-ART-WEB Author: Alika96 How to run Run the following commands: For building an image: docker image build -t ascii-art-web-docker . For showing i

0 Dec 13, 2021
API for generate image to ASCII Art

ASCII API Generate ASCII art from image. You can try this API here: ascii.projec

5 Jul 1, 2022
Human-friendly Go module that builds and prints directory trees using ASCII art

Human-friendly Go module that builds and prints directory trees using ASCII art.

5 Oct 11, 2022
A simple javascript website that takes user input, queries a Go based backend which then creates ascii art and sends it back to the frontend

A simple javascript website that takes user input, queries a Go based backend which then creates ascii art and sends it back to the frontend. Finally the site displays the ascii art and offers the option to download as multiple file types.

0 Jan 7, 2022
A command to output longified ascii art.

longify A command to output longified ascii art. Inspired by Tweet from @sheepla: https://twitter.com/Sheeeeepla/status/1522199846870196225 Installati

11 Sep 12, 2022
Virtual Universe 3D Engine

Vu Vu (Virtual Universe) is a 3D engine based on the modern programming language Go (Golang). Vu is composed of packages, detailed in GoDoc, and brief

199 Dec 9, 2022
A phoenix Chain client based on the go-ethereum fork,the new PoS consensus engine is based on the VRF algorithm.

Phoenix Official Golang implementation of the Phoenix protocol. !!!The current version is for testing and developing purposes only!!! Building the sou

9 Aug 18, 2022
An extensive, fast, and accurate command-line image dithering tool.

didder is an extensive, fast, and accurate command-line image dithering tool. It is designed to work well for both power users as well as pipeline scripting. It is backed by my dithering library, and is unique in its correctness and variety of dithering algorithms.

169 Dec 31, 2022
This command line converts .html file into .html with images embed.

embed-html This command line converts .html file into .html with images embed. Install > go get github.com/gonejack/embed-html Usage > embed-html *.ht

1 Oct 6, 2022
3D line art engine.

ln The 3D Line Art Engine ln is a vector-based 3D renderer written in Go. It is used to produce 2D vector graphics (think SVGs) depicting 3D scenes. T

3.1k Dec 28, 2022
:art: Contextual fmt inspired by bootstrap color classes

Cfmt Contextual fmt It provides contextual formatting functions that have nearly identical usage of the fmt package. The ideas were borrowed from boot

92 Jan 7, 2023
:pushpin: State of the art point location and neighbour finding algorithms for region quadtrees, in Go

Region quadtrees in Go Region quadtrees and efficient neighbour finding techniques in Go Go-rquad proposes various implementations of region quadtrees

122 Dec 13, 2022
generativeart is a Go package to generate many kinds of generative art.

generativeart is a Go package to generate many kinds of generative art. The goal is to collect some excellent generative art (implemented in R or Processing), and rewrite them in Go again

825 Dec 29, 2022
Generate high-quality triangulated art from images.

An iterative algorithm to generate high quality triangulated images.

53 May 26, 2021
A cross-platform tool to convert images into ascii art and print them on the console

A cross-platform tool to convert images into ascii art and print them on the console

1.1k Dec 30, 2022
Convert images to computer generated art using delaunay triangulation.

▲ Triangle is a tool for generating triangulated image using delaunay triangulation. It takes a source image and converts it to an abstract image comp

2k Dec 29, 2022