Git Product home page Git Product logo

rasterx's Introduction

rasterx

Rasterx is a golang rasterizer that implements path stroking functions capable of SVG 2.0 compliant 'arc' joins and explicit loop closing.

  • Paths can be explicity closed or left open, resulting in a line join or end caps.
  • Arc joins are supported, which causes the extending edge from a Bezier curve to follow the radius of curvature at the end point rather than a straight line miter, resulting in a more fluid looking join.
  • Not specified in the SVG2.0 spec., but supported in rasterx is the arc-clip join, which is the arc join analog of a miter-clip join, both of which end the miter at a specified distance, rather than all or nothing.
  • Several cap and gap functions in addition to those specified by SVG2.0 are implemented, specifically quad and cubic caps and gaps.
  • Line start and end capping functions can be different.

rasterx example

The above image shows the effect of using different join modes for a stroked curving path. The top stroked path uses miter (green) or arc (red, yellow, orange) join functions with high miter limit. The middle and lower path shows the effect of using the miter-clip and arc-clip joins, repectively, with different miter-limit values. The black chevrons at the top show different cap and gap functions.

Scanner interface

Rasterx takes the path description of lines, bezier curves, and drawing parameters, and converts them into a set of straight line segments before rasterizing the lines to an image using some method of antialiasing. Rasterx abstracts this last step through the Scanner interface. There are two different structs that satisfy the Scanner interface; ScannerGV and ScannerFT. ScannerGV wraps the rasterizer found in the golang.org/x/image/vector package. ScannerFT contains a modified version of the antialiaser found in the golang freetype translation. These use different functions to connect an image to the antialiaser. ScannerFT uses a Painter to translate the raster onto the image, and ScannerGV uses the vector's Draw method with a source image and uses the path as an alpha mask. Please see the test files for examples. At this time, the ScannerFT is a bit faster as compared to ScannerGV for larger and less complicated images, while ScannerGV can be faster for smaller and more complex images. Also ScannerGV does not allow for using the even-odd winding rule, which is something the SVG specification uses. Since ScannerFT is subject to freetype style licensing rules, it lives here in a separate repository and must be imported into your project seperately. ScannerGV is included in the rasterx package, and has more go-friendly licensing.

Below are the results of some benchmarks performed on a sample shape (the letter Q ). The first test is the time it takes to scan the image after all the curves have been flattened. The second test is the time it takes to flatten, and scan a simple filled image. The last test is the time it takes to flatten a stroked and dashed outline of the shape and scan it. Results for three different image sizes are shown.

128x128 Image
Test                        Rep       Time
BenchmarkScanGV-16          5000      287180 ns/op
BenchmarkFillGV-16          5000      339831 ns/op
BenchmarkDashGV-16          2000      968265 ns/op

BenchmarkScanFT-16    	   20000       88118 ns/op
BenchmarkFillFT-16    	    5000      214370 ns/op
BenchmarkDashFT-16    	    1000     2063797 ns/op

256x256 Image
Test                        Rep       Time
BenchmarkScanGV-16          2000     1188452 ns/op
BenchmarkFillGV-16          1000     1277268 ns/op
BenchmarkDashGV-16          500      2238169 ns/op

BenchmarkScanFT-16    	    5000      290685 ns/op
BenchmarkFillFT-16    	    3000      446329 ns/op
BenchmarkDashFT-16    	     500     2923512 ns/op

512x512 Image
Test                        Rep       Time
BenchmarkScanGV-16           500     3341038 ns/op
BenchmarkFillGV-16           500     4032213 ns/op
BenchmarkDashGV-16           200     6003355 ns/op

BenchmarkScanFT-16    	    5000      292884 ns/op
BenchmarkFillFT-16    	    3000      449582 ns/op
BenchmarkDashFT-16    	     500     2800493 ns/op

The package uses an interface called Rasterx, which is satisfied by three structs, Filler, Stroker and Dasher. The Filler flattens Bezier curves into lines and uses an anonymously composed Scanner for the antialiasing step. The Stroker embeds a Filler and adds path stroking, and the Dasher embedds a Stroker and adds the ability to create dashed stroked curves.

rasterx Scheme

Each of the Filler, Dasher, and Stroker can function on their own and each implement the Rasterx interface, so if you need just the curve filling but no stroking capability, you only need a Filler. On the other hand if you have created a Dasher and want to use it to Fill, you can just do this:

filler := &dasher.Filler

Now filler is a filling rasterizer. Please see rasterx_test.go for examples.

Non-standard library dependencies

rasterx requires the following imports which are not included in the go standard library:

  • golang.org/x/image/math/fixed
  • golang.org/x/image/vector

These can be included in your gopath by the following 'get' commands:

  • "go get golang.org/x/image/vector"
  • "go get golang.org/x/image/math/fixed"

If you want to use the freetype style antialiaser, 'go get' or clone into your workspace the scanFT package:

  • github.com/srwiley/scanFT

rasterx's People

Contributors

rcoreilly avatar srwiley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

rasterx's Issues

Scaled transform breaks dashing

When rasterizing an SVG with a scaled transformation matrix dashing does not behave correctly.
My goal is to create a high resolution png render of the svg.

At scale 1.0 (correct):
Not Scaled SVG

At scale 2.0:
image

Code used:

package main

import (
	"image"
	"image/png"
	"os"

	"github.com/srwiley/oksvg"
	"github.com/srwiley/rasterx"
)

func main() {
	scale := 2.0

	in, err := os.Open("in.svg")
	check(err)
	defer in.Close()

	icon, err := oksvg.ReadIconStream(in)
	check(err)

	w, h := int(icon.ViewBox.W*scale), int(icon.ViewBox.H*scale)

	icon.SetTarget(0, 0, float64(w), float64(h))
	rgba := image.NewRGBA(image.Rect(0, 0, w, h))
	dasher := rasterx.NewDasher(w, h, rasterx.NewScannerGV(w, h, rgba, rgba.Bounds()))
	icon.Draw(dasher, 1)

	out, err := os.Create("out.png")
	check(err)
	defer out.Close()

	err = png.Encode(out, rgba)
	check(err)
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

SVG used:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 110 110">
    <path d="
      M 50, 50
	  l 0 -50
    " stroke="teal" fill="none" />
    <path d="
      M 0, 50
      a 50,50 0 1,0 100,0
      a 50,50 0 1,0 -100,0
    " stroke="teal" fill="none" stroke-dasharray="314" stroke-dashoffset="157" />
</svg>

fix for radial gradients with UserSpaceOnUse, c == f

I finally found the source of a radial gradient issue I'd noticed for a while: the fx, fy coordinates were not being transformed, and so the simple distance-based case was not triggering. Diff to fix is attached -- too lazy to do a PR but let me know if you prefer that.

Parens around the d's probably doesn't matter but anyway it might be slightly more precision-preserving?

diff --git a/gradient.go b/gradient.go
index 6be8fe7..8f00985 100644
--- a/gradient.go
+++ b/gradient.go
@@ -189,8 +189,10 @@ func (g *Gradient) GetColorFunctionUS(opacity float64, objMatrix Matrix2D) inter
 			ry *= g.Bounds.H
 		} else {
 			cx, cy = g.Matrix.Transform(cx, cy)
+			fx, fy = g.Matrix.Transform(fx, fy)
 			rx, ry = g.Matrix.TransformVector(rx, ry)
 			cx, cy = objMatrix.Transform(cx, cy)
+			fx, fy = objMatrix.Transform(fx, fy)
 			rx, ry = objMatrix.TransformVector(rx, ry)
 		}
 
@@ -203,7 +205,7 @@ func (g *Gradient) GetColorFunctionUS(opacity float64, objMatrix Matrix2D) inter
 					x, y := gradT.Transform(float64(xi)+0.5, float64(yi)+0.5)
 					dx := float64(x) - cx
 					dy := float64(y) - cy
-					return g.tColor(math.Sqrt(dx*dx/(rx*rx)+dy*dy/(ry*ry)), opacity)
+					return g.tColor(math.Sqrt((dx*dx)/(rx*rx)+(dy*dy)/(ry*ry)), opacity)
 				})
 			}
 			return ColorFunc(func(xi, yi int) color.Color {
@@ -211,7 +213,7 @@ func (g *Gradient) GetColorFunctionUS(opacity float64, objMatrix Matrix2D) inter
 				y := float64(yi) + 0.5
 				dx := x - cx
 				dy := y - cy
-				return g.tColor(math.Sqrt(dx*dx/(rx*rx)+dy*dy/(ry*ry)), opacity)
+				return g.tColor(math.Sqrt((dx*dx)/(rx*rx)+(dy*dy)/(ry*ry)), opacity)
 			})
 		}
 		fx /= rx

How do you use AddArc given the context from SVG2.0 data?

First, thanks for the library. This might be considered related to #13 Given documentation or a test as an example for AddArc may clarify both issues.

I am trying to figure out how to use AddArc given an SVG 2.0 instruction.

func AddArc(points []float64, cx, cy, px, py float64, p Adder) (lx, ly float64) {

Let's assume I have M 0 0 a4.9916 4.9916 0 0 1 -0.59375 0.1875 notice this is the relative version which I think simply means that the last two values must be adjusted by the current point and in this case it is 0, 0.

I have summarized so far that the Points is basically all the parameters of a, no issue there. What I can't figure out is what is cx, cy, px, py parameters?

I can't seem to get the correct arc. Is cx,cy current point that would make since, is that correct?

It is the px, py I don't really have any idea at all what it could be or how to derive it from the information I have.

Given this is a relative instruction would I need to update points based on the current location or is that somehow related to the c or p parameters?

Thanks for your time!

Text lose

I tried to convert a svg which contains words in , but lose all text after convertion?
origin svg:

in

after:

out

Dashes = [0,0] ends up in infinite loop

if you scale down lines with dashes to a sufficiently small size, the fixed.Int26_6 version can end up being 0, at which point the Dasher rendering code in dash.go ends up stuck in an infinite loop!

Best soln would be to fix SetStroke to detect the problem and reset Dashes if all are 0.

I'm now catching this upstream, but would be good to fix here too.

Display bug in fyne

Hello.
After discussing with andy (main maintainer of fyne library), about my bug, we came to the conclusion that it should be poster here since fyne uses rasterx for SVG display.

Here are two SVGs that are not correctly displayed in fyne:
image
image
Code:

<?xml version="1.0" encoding="utf-8"?>

<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Calendar / Calendar_Days">
<path id="Vector" d="M8 4H7.2002C6.08009 4 5.51962 4 5.0918 4.21799C4.71547 4.40973 4.40973 4.71547 4.21799 5.0918C4 5.51962 4 6.08009 4 7.2002V8M8 4H16M8 4V2M16 4H16.8002C17.9203 4 18.4796 4 18.9074 4.21799C19.2837 4.40973 19.5905 4.71547 19.7822 5.0918C20 5.5192 20 6.07899 20 7.19691V8M16 4V2M4 8V16.8002C4 17.9203 4 18.4801 4.21799 18.9079C4.40973 19.2842 4.71547 19.5905 5.0918 19.7822C5.5192 20 6.07899 20 7.19691 20H16.8031C17.921 20 18.48 20 18.9074 19.7822C19.2837 19.5905 19.5905 19.2842 19.7822 18.9079C20 18.4805 20 17.9215 20 16.8036V8M4 8H20M16 16H16.002L16.002 16.002L16 16.002V16ZM12 16H12.002L12.002 16.002L12 16.002V16ZM8 16H8.002L8.00195 16.002L8 16.002V16ZM16.002 12V12.002L16 12.002V12H16.002ZM12 12H12.002L12.002 12.002L12 12.002V12ZM8 12H8.002L8.00195 12.002L8 12.002V12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

image
image
Code

<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.5 13.75C9.5 14.72 10.25 15.5 11.17 15.5H13.05C13.85 15.5 14.5 14.82 14.5 13.97C14.5 13.06 14.1 12.73 13.51 12.52L10.5 11.47C9.91 11.26 9.51001 10.94 9.51001 10.02C9.51001 9.17999 10.16 8.48999 10.96 8.48999H12.84C13.76 8.48999 14.51 9.26999 14.51 10.24" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 7.5V16.5" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 12C22 17.52 17.52 22 12 22C6.48 22 2 17.52 2 12C2 6.48 6.48 2 12 2" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17 3V7H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 2L17 7" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Please explain the AddArc parameters, e.g. to make a "pie part"

Hi,
I spent hours making tests and trying to understand the parameters, and the "points" content and I didn't find my way to make a "pie part". Whatever I do, the path becomes a circle.

Using this

	cx, cy := float64(w/2.0), float64(h/2.0)
	r := float64(w / 3.0)
	angle := 45.0
	rot := angle * math.Pi / 180.0

	// find the point on circle of radius r at angle rot
	px := cx + r*math.Cos(rot)
	py := cy + r*math.Sin(rot)

	points := []float64{r, r, angle, 1, 0, px, py}

	stroker.Start(rasterx.ToFixedP(cx, cy))
	//stroker.Line(rasterx.ToFixedP(cx, cy+r))
	stroker.Start(rasterx.ToFixedP(px, py))
	rasterx.AddArc(points, cx, cy, px, py, stroker)
	//stroker.Stop(false)
	//stroker.Line(rasterx.ToFixedP(cx, cy))
	stroker.Stop(false)
	stroker.Draw()

out

With this:

	stroker.Start(rasterx.ToFixedP(cx, cy))
	stroker.Line(rasterx.ToFixedP(cx, cy+r))
	stroker.Start(rasterx.ToFixedP(px, py))
	rasterx.AddArc(points, cx, cy, px, py, stroker)
	stroker.Line(rasterx.ToFixedP(cx, cy))
	stroker.Stop(false)
	stroker.Draw()

out

I made several other tests to start the path somewhere else, with no idea of how to make a "pie chart element".

This is the wanted shape:
image

The goal is to make a pie chart (of course :) ) but (sorry to say this) the code is not well documented... Can you please give a simple example of a pie chart element?

incorrect rasterization of open paths

Hi,
while testing rasterx, i write this example:

package main

import (
	"image"
	"image/color"
	"image/draw"
	"log"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/exp/shiny/widget"

	"golang.org/x/image/math/fixed"

	"github.com/srwiley/rasterx"
)

const (
	S = 512
)

func main() {
	log.SetFlags(0)
	driver.Main(func(s screen.Screen) {
		src := TestMultiFunctionGV() // image.NewRGBA(image.Rect(0, 0, sw, sh))

		w := widget.NewSheet(widget.NewImage(src, src.Bounds()))
		if err := widget.RunWindow(s, w, &widget.RunWindowOptions{
			NewWindowOptions: screen.NewWindowOptions{
				Title:  "Rasterx Example",
				Width:  S,
				Height: S,
			},
		}); err != nil {
			log.Fatal(err)
		}
	})
}

//////////////////////////////////////////////////////

func toFixedP(x, y float64) (p fixed.Point26_6) {
	p.X = fixed.Int26_6(x * 64)
	p.Y = fixed.Int26_6(y * 64)
	return
}

func GetTestPath() (testPath rasterx.Path) {
	//Path for Q
	//M210.08,222.97
	testPath.Start(toFixedP(210.08, 222.97))
	//L192.55,244.95
	testPath.Line(toFixedP(192.55, 244.95))
	//Q146.53,229.95,115.55,209.55
	testPath.QuadBezier(toFixedP(146.53, 229.95), toFixedP(115.55, 209.55))
	//Q102.50,211.00,95.38,211.00
	testPath.QuadBezier(toFixedP(102.50, 211.00), toFixedP(95.38, 211.00))
	//Q56.09,211.00,31.17,182.33
	testPath.QuadBezier(toFixedP(56.09, 211.00), toFixedP(31.17, 182.33))
	//Q6.27,153.66,6.27,108.44
	testPath.QuadBezier(toFixedP(6.27, 153.66), toFixedP(6.27, 108.44))
	//Q6.27,61.89,31.44,33.94
	testPath.QuadBezier(toFixedP(6.27, 61.89), toFixedP(31.44, 33.94))
	//Q56.62,6.00,98.55,6.00
	testPath.QuadBezier(toFixedP(56.62, 6.00), toFixedP(98.55, 6.00))
	//Q141.27,6.00,166.64,33.88
	testPath.QuadBezier(toFixedP(141.27, 6.00), toFixedP(166.64, 33.88))
	//Q192.02,61.77,192.02,108.70
	testPath.QuadBezier(toFixedP(192.02, 61.77), toFixedP(192.02, 108.70))
	//Q192.02,175.67,140.86,202.05
	testPath.QuadBezier(toFixedP(192.02, 175.67), toFixedP(140.86, 202.05))
	//Q173.42,216.66,210.08,222.97
	testPath.QuadBezier(toFixedP(173.42, 216.66), toFixedP(210.08, 222.97))
	//z
	testPath.Stop(false)
	//M162.22,109.69 M162.22,109.69
	testPath.Start(toFixedP(162.22, 109.69))
	//Q162.22,70.11,145.61,48.55
	testPath.QuadBezier(toFixedP(162.22, 70.11), toFixedP(145.61, 48.55))
	//Q129.00,27.00,98.42,27.00
	testPath.QuadBezier(toFixedP(129.00, 27.00), toFixedP(98.42, 27.00))
	//Q69.14,27.00,52.53,48.62
	testPath.QuadBezier(toFixedP(69.14, 27.00), toFixedP(52.53, 48.62))
	//Q35.92,70.25,35.92,108.50
	testPath.QuadBezier(toFixedP(35.92, 70.25), toFixedP(35.92, 108.50))
	//Q35.92,146.75,52.53,168.38
	testPath.QuadBezier(toFixedP(35.92, 146.75), toFixedP(52.53, 168.38))
	//Q69.14,190.00,98.42,190.00
	testPath.QuadBezier(toFixedP(69.14, 190.00), toFixedP(98.42, 190.00))
	//Q128.34,190.00,145.28,168.70
	testPath.QuadBezier(toFixedP(128.34, 190.00), toFixedP(145.28, 168.70))
	//Q162.22,147.41,162.22,109.69
	testPath.QuadBezier(toFixedP(162.22, 147.41), toFixedP(162.22, 109.69))
	//z
	testPath.Stop(false)

	return testPath
}

// TestMultiFunction tests a Dasher's ability to function
// as a filler, stroker, and dasher by invoking the corresponding anonymous structs
func TestMultiFunctionGV() image.Image {
	img := image.NewRGBA(image.Rect(0, 0, S, S))
	src := image.NewUniform(color.NRGBA{255, 0, 0, 255})
	scannerGV := rasterx.NewScannerGV(S, S, img, img.Bounds(), src, image.ZP)

	draw.Draw(img, img.Bounds(), image.White, image.ZP, draw.Src)

	d := rasterx.NewDasher(S, S, scannerGV)
	d.SetStroke(10*64, 4*64, rasterx.RoundCap, nil, rasterx.RoundGap, rasterx.ArcClip, []float64{33, 12}, 0)
	p := GetTestPath()

	p.AddTo(d)
	d.Draw()
	d.Clear()
	return img
}

Note testPath.Stop(false) in GetTestPath.
Result is in attached file:
screenshot

i able to resolve issue by adding new PathCommand:

// Human readable path constants
const (
	PathMoveTo PathCommand = iota
	PathLineTo
	PathQuadTo
	PathCubicTo
	PathClose
	PathCloseF
)

...

// Close joins the ends of the path
func (p *Path) Stop(closeLoop bool) {
	if closeLoop {
		*p = append(*p, fixed.Int26_6(PathClose))
	} else {
		*p = append(*p, fixed.Int26_6(PathCloseF))
	}
}

// AddPath adds the Path p to q. This bridges the path and adder interface.
func (p Path) AddTo(q Adder) {
	for i := 0; i < len(p); {
		switch PathCommand(p[i]) {
		case PathMoveTo:
			q.Start(fixed.Point26_6{p[i+1], p[i+2]})
			i += 3
		case PathLineTo:
			q.Line(fixed.Point26_6{p[i+1], p[i+2]})
			i += 3
		case PathQuadTo:
			q.QuadBezier(fixed.Point26_6{p[i+1], p[i+2]}, fixed.Point26_6{p[i+3], p[i+4]})
			i += 5
		case PathCubicTo:
			q.CubeBezier(fixed.Point26_6{p[i+1], p[i+2]},
				fixed.Point26_6{p[i+3], p[i+4]}, fixed.Point26_6{p[i+5], p[i+6]})
			i += 7
		case PathClose:
			q.Stop(true)
			i += 1
		case PathCloseF:
		 	q.Stop(false)
		  	i += 1
		default:
			panic("adder geom: bad path")
		}
	}
	q.Stop(false)
}

but i am not sure if this is correct fix.

Regards.

Implementing the rasterx interface

Hello! I want to be able to render svgs directly to, say, an opengl framebuffer rather than an image.Image. I've actually already got the engine and framework and shaders needed, and It seems to me that a lot of the types in this package could be interfaces, which could then allow me to implement them to allow me to do just that. Is that a direction you'd be willing to take? I'll put together the PR and write it myself, I'm just wondering if you had any tips or ideas on going about it. Thanks!

I tried adding rasterx to awesome-go page... needs some work before they will accept it

I was adding GoGi etc to it and figured I'd add rasterx while I was at it.. Anyway, looks like you'll have to do some things and submit it separately if you want to:

avelino/awesome-go#2222

These should be improved, they have a lot of issues, especially with golint:
https://goreportcard.com/report/github.com/srwiley/rasterx

These miss a lot of documentation, which should also be improved:
https://godoc.org/github.com/srwiley/rasterx

these don't have enough coverage:
https://github.com/srwiley/rasterx

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.