This commit is contained in:
parent
7e5c4132b6
commit
baa71dfa7e
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>draw2d</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.googlecode.goclipse.goBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>goclipse.goNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1 @@
|
|||
Laurent Le Goff
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
|
||||
This package (written in *[http://golang.org go]*) provide an API to draw 2d geometrical form on [http://golang.org/pkg/image/ images].
|
||||
This library is largely inspired by [http://www.tailrecursive.org/postscript/ postscript], [http://cairographics.org/ cairo], [http://dev.w3.org/html5/canvas-api/canvas-2d-api.html#the-2d-drawing-context HTML5 canvas].
|
||||
|
||||
The package depends on [http://code.google.com/p/freetype-go/ freetype-go] package thanks to its rasterization algorithm.
|
||||
|
||||
Some algorithm have been translated from http://www.antigrain.com project ([http://www.antigrain.com/research/adaptive_bezier/index.html adaptive bezier], and arc drawing)
|
||||
|
||||
=== Installation ====
|
||||
Once you have Go installed, to install draw2d:
|
||||
|
||||
* First see the installation procedure of [http://code.google.com/p/freetype-go/ freetype-go]
|
||||
* goinstall draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d
|
||||
|
||||
a good starting point is the [http://code.google.com/p/draw2d/wiki/GettingStarted getting started]
|
||||
|
||||
|
||||
=== [http://code.google.com/p/draw2d/wiki/Samples Samples] ===
|
||||
Sample images generated by draw2d (inspired by [http://cairographics.org/samples/ cairo samples]):
|
||||
there's already some bugs please refer to [http://code.google.com/p/draw2d/issues/list issue tracking]
|
||||
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestPath.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestDrawArc.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestDrawArcNegative.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestCurveRectangle.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestDrawCubicCurve.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestDash.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestFillStroke.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestFillStyle.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestMultiSegmentCaps.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestRoundRectangle.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestLineCap.png]
|
||||
[http://draw2d.googlecode.com/svn/wiki/test_results/TestLineJoin.png]
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=gettingStarted testdraw2d testPath
|
||||
OFILES=$(TARG:%=%.$O)
|
||||
|
||||
all: $(TARG)
|
||||
|
||||
$(TARG): %: %.$O
|
||||
$(LD) -o $@ $<
|
||||
|
||||
$(OFILES): %.$O: %.go Makefile
|
||||
$(GC) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.[$(OS)] $(TARG) $(CLEANFILES)
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bufio"
|
||||
|
||||
"image"
|
||||
"image/png"
|
||||
"draw2d"
|
||||
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
|
||||
)
|
||||
|
||||
|
||||
func saveToPngFile(filePath string, m image.Image) {
|
||||
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
b := bufio.NewWriter(f)
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Wrote %s OK.\n", filePath)
|
||||
}
|
||||
|
||||
func main() {
|
||||
i := image.NewRGBA(200, 200)
|
||||
gc := draw2d.NewGraphicContext(i)
|
||||
gc.MoveTo(10.0, 10.0)
|
||||
gc.LineTo(100.0, 10.0)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestPath.png", i)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
canvas { border: 1px solid black; }
|
||||
</style>
|
||||
<title>Canvas tutorial</title>
|
||||
<script type="text/javascript" src="test.js">
|
||||
</script>
|
||||
</head>
|
||||
<body onload="executeTests()">
|
||||
<p>TestStar</p>
|
||||
<canvas id="TestStar" width="400" height="400"></canvas>
|
||||
<p>TestTransform</p>
|
||||
<canvas id="TestTransform" width="400" height="400"></canvas>
|
||||
<canvas id="TestPathTransform" width="400" height="400"></canvas>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,79 @@
|
|||
%!
|
||||
% Example of rotation... draws 36 lines in a circular pattern
|
||||
|
||||
0 10 360 { % Go from 0 to 360 degrees in 10 degree steps
|
||||
newpath % Start a new path
|
||||
|
||||
gsave
|
||||
5 setlinewidth % Keep rotations temporary
|
||||
144 144 moveto
|
||||
rotate % Rotate by degrees on stack from 'for'
|
||||
72 0 rlineto
|
||||
stroke
|
||||
grestore % Get back the unrotated state
|
||||
|
||||
} for % Iterate over angles
|
||||
|
||||
showpage
|
||||
|
||||
/box {
|
||||
newpath
|
||||
moveto
|
||||
72 0 rlineto
|
||||
0 72 rlineto
|
||||
-72 0 rlineto
|
||||
closepath
|
||||
} def
|
||||
|
||||
% Specify font for text labels
|
||||
/Helvetica findfont 40 scalefont setfont
|
||||
|
||||
gsave
|
||||
40 40 translate % Set origin to (40, 40)
|
||||
0 0 box stroke % Draw box at new origin...
|
||||
77 0 moveto
|
||||
(Translated) show % and label
|
||||
grestore
|
||||
|
||||
gsave
|
||||
100 150 translate % Translate origin to (100, 150)
|
||||
30 rotate % Rotate counter-clockwise by 30 degrees
|
||||
0 0 box stroke % Draw box...
|
||||
75 0 moveto
|
||||
(Translated & Rotated) show % and label
|
||||
grestore
|
||||
|
||||
gsave
|
||||
40 300 translate % Translate to (40, 300)
|
||||
0.5 1 scale % Reduce x coord by 1/2, y coord left alone
|
||||
0 0 box stroke % Draw box...
|
||||
75 0 moveto
|
||||
(Translated & Squished) show % and label
|
||||
grestore
|
||||
|
||||
gsave
|
||||
100 450 translate % Set origin to (300, 300)
|
||||
45 rotate % Rotate coordinates by 45 degrees
|
||||
0.5 1 scale % Scale coordinates
|
||||
0 0 box stroke % Draw box
|
||||
75 0 moveto
|
||||
(Everything) show
|
||||
grestore
|
||||
|
||||
showpage
|
||||
|
||||
gsave
|
||||
20 setlinewidth
|
||||
0 100 moveto
|
||||
100 100 lineto
|
||||
1 4 scale
|
||||
200 100 lineto
|
||||
stroke
|
||||
|
||||
200 50 50 0 361 arc
|
||||
stroke
|
||||
grestore
|
||||
|
||||
showpage
|
||||
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
function toDegree(radians) {
|
||||
return radians * 180 / Math.PI;
|
||||
}
|
||||
function toRadians(degree) {
|
||||
return degree * Math.PI / 180;
|
||||
}
|
||||
|
||||
function draw(test){
|
||||
var canvas = document.getElementById(test.name);
|
||||
if (canvas.getContext){
|
||||
var gc = canvas.getContext('2d');
|
||||
test(gc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function executeTests() {
|
||||
draw(TestStar)
|
||||
draw(TestTransform)
|
||||
draw(TestPathTransform)
|
||||
}
|
||||
|
||||
function TestStar(gc) {
|
||||
for(i = 0.0 ; i < 360; i = i + 10) {// Go from 0 to 360 degrees in 10 degree steps
|
||||
gc.beginPath() // Start a new path
|
||||
gc.save() // Keep rotations temporary
|
||||
gc.lineWidth = 5
|
||||
gc.translate(144, 144)
|
||||
gc.rotate(i * (Math.PI / 180.0)) // Rotate by degrees on stack from 'for'
|
||||
gc.moveTo(0, 0)
|
||||
gc.lineTo(72, 0)
|
||||
gc.stroke()
|
||||
gc.restore() // Get back the unrotated state
|
||||
}
|
||||
}
|
||||
|
||||
function TestTransform(gc) {
|
||||
|
||||
gc.save()
|
||||
gc.translate(40, 40) // Set origin to (40, 40)
|
||||
gc.beginPath()
|
||||
gc.moveTo(0,0)
|
||||
gc.lineTo(72,0)
|
||||
gc.lineTo(72, 72)
|
||||
gc.lineTo(0, 72)
|
||||
gc.closePath()
|
||||
gc.stroke()
|
||||
gc.restore()
|
||||
|
||||
gc.save()
|
||||
gc.translate(100, 150) // Translate origin to (100, 150)
|
||||
gc.rotate(30* (Math.PI / 180.0)) // Rotate counter-clockwise by 30 degrees
|
||||
gc.beginPath()
|
||||
gc.moveTo(0,0)
|
||||
gc.lineTo(72,0)
|
||||
gc.lineTo(72, 72)
|
||||
gc.lineTo(0, 72)
|
||||
gc.closePath() // Draw box...
|
||||
gc.stroke()
|
||||
gc.restore()
|
||||
|
||||
gc.save()
|
||||
gc.translate(40, 300) // Translate to (40, 300)
|
||||
gc.scale(0.5, 1) // Reduce x coord by 1/2, y coord left alone
|
||||
gc.beginPath()
|
||||
gc.moveTo(0,0)
|
||||
gc.lineTo(72,0)
|
||||
gc.lineTo(72, 72)
|
||||
gc.lineTo(0, 72)
|
||||
gc.closePath() // Draw box...
|
||||
gc.stroke()
|
||||
gc.restore()
|
||||
|
||||
gc.save()
|
||||
gc.translate(300, 300) // Set origin to (300, 300)
|
||||
gc.rotate(45* (Math.PI / 180.0)) // Rotate coordinates by 45 degrees
|
||||
gc.scale(0.5, 1) // Scale coordinates
|
||||
gc.beginPath()
|
||||
gc.moveTo(0,0)
|
||||
gc.lineTo(72,0)
|
||||
gc.lineTo(72, 72)
|
||||
gc.lineTo(0, 72)
|
||||
gc.closePath() // Draw box
|
||||
gc.stroke()
|
||||
gc.restore()
|
||||
}
|
||||
|
||||
|
||||
function TestPathTransform(gc) {
|
||||
gc.lineWidth = 20
|
||||
gc.moveTo(0,100)
|
||||
gc.lineTo(100,100)
|
||||
gc.scale(1,4)
|
||||
gc.lineTo(200,100)
|
||||
gc.stroke()
|
||||
gc.beginPath()
|
||||
gc.arc(200, 50, 50, 0, 6.28, false)
|
||||
gc.stroke()
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package main
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bufio"
|
||||
"time"
|
||||
|
||||
"math"
|
||||
"image"
|
||||
"image/png"
|
||||
"draw2d"
|
||||
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
|
||||
)
|
||||
|
||||
const (
|
||||
width, height = 500, 500
|
||||
)
|
||||
|
||||
var (
|
||||
lastTime int64
|
||||
folder = "../../../../wiki/test_results/"
|
||||
)
|
||||
|
||||
func initGc(w, h int) (image.Image, *draw2d.GraphicContext) {
|
||||
i := image.NewRGBA(w, h)
|
||||
gc := draw2d.NewGraphicContext(i)
|
||||
lastTime = time.Nanoseconds()
|
||||
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.SetFillColor(image.White)
|
||||
// fill the background
|
||||
//gc.Clear()
|
||||
|
||||
return i, gc
|
||||
}
|
||||
|
||||
func saveToPngFile(TestName string, m image.Image) {
|
||||
dt := time.Nanoseconds() - lastTime
|
||||
fmt.Printf("%s during: %f ms\n", TestName, float(dt)*10e-6)
|
||||
filePath := folder + TestName + ".png"
|
||||
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
b := bufio.NewWriter(f)
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Wrote %s OK.\n", filePath)
|
||||
}
|
||||
|
||||
func android(gc *draw2d.GraphicContext, x, y float) {
|
||||
gc.SetLineCap(draw2d.RoundCap)
|
||||
gc.SetLineWidth(5)
|
||||
gc.ArcTo(x+80, y+70, 50, 50, 180 * (math.Pi/180), 360 * (math.Pi/180)) // head
|
||||
gc.FillStroke()
|
||||
gc.MoveTo(x+60, y+25)
|
||||
gc.LineTo(x+50, y+10)
|
||||
gc.MoveTo(x+100, y+25)
|
||||
gc.LineTo( x+110, y+10)
|
||||
gc.Stroke()
|
||||
gc.Circle(x+60, y+45, 5) // left eye
|
||||
gc.FillStroke()
|
||||
gc.Circle(x+100, y+45, 5) // right eye
|
||||
gc.FillStroke()
|
||||
gc.RoundRect(x+30, y+75, x+30+100, y+75+90, 10, 10) // body
|
||||
gc.FillStroke()
|
||||
gc.Rect(x+30, y+75, x+30+100, y+75+80)
|
||||
gc.FillStroke()
|
||||
gc.RoundRect(x+5, y+80, x+5+20, y+80+70, 10, 10) // left arm
|
||||
gc.FillStroke()
|
||||
gc.RoundRect(x+135, y+80, x+135+20, y+80+70, 10, 10) // right arm
|
||||
gc.FillStroke()
|
||||
gc.RoundRect(x+50, y+150, x+50+20, y+150+50, 10, 10) // left leg
|
||||
gc.FillStroke()
|
||||
gc.RoundRect(x+90, y+150, x+90+20, y+150+50, 10, 10) // right leg
|
||||
gc.FillStroke()
|
||||
}
|
||||
|
||||
|
||||
|
||||
func main() {
|
||||
i, gc := initGc(width, height)
|
||||
android(gc, 100, 100)
|
||||
saveToPngFile("TestAndroid", i)
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bufio"
|
||||
"time"
|
||||
|
||||
"math"
|
||||
"image"
|
||||
"image/png"
|
||||
"draw2d"
|
||||
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
|
||||
)
|
||||
|
||||
const (
|
||||
w, h = 256, 256
|
||||
)
|
||||
|
||||
var (
|
||||
lastTime int64
|
||||
folder = "../../../../wiki/test_results/"
|
||||
)
|
||||
|
||||
func initGc(w, h int) (image.Image, *draw2d.GraphicContext) {
|
||||
i := image.NewRGBA(w, h)
|
||||
gc := draw2d.NewGraphicContext(i)
|
||||
lastTime = time.Nanoseconds()
|
||||
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.SetFillColor(image.White)
|
||||
// fill the background
|
||||
//gc.Clear()
|
||||
|
||||
return i, gc
|
||||
}
|
||||
|
||||
func saveToPngFile(TestName string, m image.Image) {
|
||||
dt := time.Nanoseconds() - lastTime
|
||||
fmt.Printf("%s during: %f ms\n", TestName, float(dt)*10e-6)
|
||||
filePath := folder + TestName + ".png"
|
||||
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
b := bufio.NewWriter(f)
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Wrote %s OK.\n", filePath)
|
||||
}
|
||||
|
||||
/*
|
||||
<img src="../test_results/TestPath.png"/>
|
||||
*/
|
||||
func TestPath() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.SetLineWidth(10)
|
||||
gc.Translate(10,10)
|
||||
gc.MoveTo(0.0, 0.0)
|
||||
gc.LineTo(100.0, 00.0)
|
||||
gc.LineTo(100.0, 100.0)
|
||||
gc.LineTo(0.0, 100.0)
|
||||
gc.LineTo(0.0, 0.0)
|
||||
gc.FillStroke()
|
||||
saveToPngFile("TestPath", i)
|
||||
}
|
||||
|
||||
|
||||
func cos(f float) float {
|
||||
return float(math.Cos(float64(f)))
|
||||
}
|
||||
func sin(f float) float {
|
||||
return float(math.Sin(float64(f)))
|
||||
}
|
||||
/*
|
||||
<img src="../test_results/TestDrawArc.png"/>
|
||||
*/
|
||||
func TestDrawArc() {
|
||||
i, gc := initGc(w, h)
|
||||
// draw an arc
|
||||
xc, yc := 128.0, 128.0
|
||||
radiusX, radiusY := 100.0, 100.0
|
||||
startAngle := 45.0 * (math.Pi / 180.0) /* angles are specified */
|
||||
angle := 135 * (math.Pi / 180.0) /* in radians */
|
||||
gc.SetLineWidth(10)
|
||||
gc.SetLineCap(draw2d.ButtCap)
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.ArcTo(xc, yc, radiusX, radiusY, startAngle, angle)
|
||||
gc.Stroke()
|
||||
// fill a circle
|
||||
gc.SetStrokeColor(image.RGBAColor{255, 0x33, 0x33, 0x80})
|
||||
gc.SetFillColor(image.RGBAColor{255, 0x33, 0x33, 0x80})
|
||||
gc.SetLineWidth(6)
|
||||
|
||||
gc.MoveTo(xc, yc)
|
||||
gc.LineTo(xc+cos(startAngle)*radiusX, yc+sin(startAngle)*radiusY)
|
||||
gc.MoveTo(xc, yc)
|
||||
gc.LineTo(xc-radiusX, yc)
|
||||
gc.Stroke()
|
||||
|
||||
gc.ArcTo(xc, yc, 10.0, 10.0, 0, 2*math.Pi)
|
||||
gc.Fill()
|
||||
saveToPngFile("TestDrawArc", i)
|
||||
}
|
||||
/*
|
||||
<img src="../test_results/TestDrawArc.png"/>
|
||||
*/
|
||||
func TestDrawArcNegative() {
|
||||
i, gc := initGc(w, h)
|
||||
// draw an arc
|
||||
xc, yc := 128.0, 128.0
|
||||
radiusX, radiusY := 100.0, 100.0
|
||||
startAngle := 45.0 * (math.Pi / 180.0) /* angles are specified */
|
||||
angle := -225 * (math.Pi / 180.0) /* in radians */
|
||||
gc.SetLineWidth(10)
|
||||
gc.SetLineCap(draw2d.ButtCap)
|
||||
gc.SetStrokeColor(image.Black)
|
||||
|
||||
gc.ArcTo(xc, yc, radiusX, radiusY, startAngle, angle)
|
||||
gc.Stroke()
|
||||
// fill a circle
|
||||
gc.SetStrokeColor(image.RGBAColor{255, 0x33, 0x33, 0x80})
|
||||
gc.SetFillColor(image.RGBAColor{255, 0x33, 0x33, 0x80})
|
||||
gc.SetLineWidth(6)
|
||||
|
||||
|
||||
gc.MoveTo(xc, yc)
|
||||
gc.LineTo(xc+cos(startAngle)*radiusX, yc+sin(startAngle)*radiusY)
|
||||
gc.MoveTo(xc, yc)
|
||||
gc.LineTo(xc-radiusX, yc)
|
||||
gc.Stroke()
|
||||
|
||||
gc.ArcTo(xc, yc, 10.0, 10.0, 0, 2*math.Pi)
|
||||
gc.Fill()
|
||||
saveToPngFile("TestDrawArcNegative", i)
|
||||
}
|
||||
|
||||
func TestCurveRectangle() {
|
||||
i, gc := initGc(w, h)
|
||||
|
||||
/* a custom shape that could be wrapped in a function */
|
||||
x0, y0 := 25.6, 25.6 /* parameters like cairo_rectangle */
|
||||
rect_width, rect_height := 204.8, 204.8
|
||||
radius := 102.4 /* and an approximate curvature radius */
|
||||
|
||||
x1 := x0 + rect_width
|
||||
y1 := y0 + rect_height
|
||||
if rect_width/2 < radius {
|
||||
if rect_height/2 < radius {
|
||||
gc.MoveTo(x0, (y0+y1)/2)
|
||||
gc.CubicCurveTo(x0, y0, x0, y0, (x0+x1)/2, y0)
|
||||
gc.CubicCurveTo(x1, y0, x1, y0, x1, (y0+y1)/2)
|
||||
gc.CubicCurveTo(x1, y1, x1, y1, (x1+x0)/2, y1)
|
||||
gc.CubicCurveTo(x0, y1, x0, y1, x0, (y0+y1)/2)
|
||||
} else {
|
||||
gc.MoveTo(x0, y0+radius)
|
||||
gc.CubicCurveTo(x0, y0, x0, y0, (x0+x1)/2, y0)
|
||||
gc.CubicCurveTo(x1, y0, x1, y0, x1, y0+radius)
|
||||
gc.LineTo(x1, y1-radius)
|
||||
gc.CubicCurveTo(x1, y1, x1, y1, (x1+x0)/2, y1)
|
||||
gc.CubicCurveTo(x0, y1, x0, y1, x0, y1-radius)
|
||||
}
|
||||
} else {
|
||||
if rect_height/2 < radius {
|
||||
gc.MoveTo(x0, (y0+y1)/2)
|
||||
gc.CubicCurveTo(x0, y0, x0, y0, x0+radius, y0)
|
||||
gc.LineTo(x1-radius, y0)
|
||||
gc.CubicCurveTo(x1, y0, x1, y0, x1, (y0+y1)/2)
|
||||
gc.CubicCurveTo(x1, y1, x1, y1, x1-radius, y1)
|
||||
gc.LineTo(x0+radius, y1)
|
||||
gc.CubicCurveTo(x0, y1, x0, y1, x0, (y0+y1)/2)
|
||||
} else {
|
||||
gc.MoveTo(x0, y0+radius)
|
||||
gc.CubicCurveTo(x0, y0, x0, y0, x0+radius, y0)
|
||||
gc.LineTo(x1-radius, y0)
|
||||
gc.CubicCurveTo(x1, y0, x1, y0, x1, y0+radius)
|
||||
gc.LineTo(x1, y1-radius)
|
||||
gc.CubicCurveTo(x1, y1, x1, y1, x1-radius, y1)
|
||||
gc.LineTo(x0+radius, y1)
|
||||
gc.CubicCurveTo(x0, y1, x0, y1, x0, y1-radius)
|
||||
}
|
||||
}
|
||||
gc.ClosePath()
|
||||
|
||||
gc.SetFillColor(image.RGBAColor{0x80, 0x80, 0xFF, 0xFF})
|
||||
gc.SetStrokeColor(image.RGBAColor{0x80, 0, 0, 0x80})
|
||||
gc.SetLineWidth(10.0)
|
||||
gc.FillStroke()
|
||||
|
||||
saveToPngFile("TestCurveRectangle", i)
|
||||
}
|
||||
/*
|
||||
<img src="../test_results/TestDrawCubicCurve.png"/>
|
||||
*/
|
||||
func TestDrawCubicCurve() {
|
||||
i, gc := initGc(800, 600)
|
||||
// draw a cubic curve
|
||||
x, y := 25.6, 128.0
|
||||
x1, y1 := 102.4, 230.4
|
||||
x2, y2 := 153.6, 25.6
|
||||
x3, y3 := 230.4, 128.0
|
||||
|
||||
gc.SetFillColor(image.RGBAColor{0xAA, 0xAA, 0xAA, 0xFF})
|
||||
gc.SetLineWidth(5)
|
||||
gc.MoveTo(x, y)
|
||||
gc.CubicCurveTo(x1, y1, x2, y2, x3, y3)
|
||||
gc.Stroke()
|
||||
|
||||
gc.SetStrokeColor(image.RGBAColor{0xFF, 0x33, 0x33, 0x88})
|
||||
|
||||
gc.SetLineWidth(6)
|
||||
// draw segment of curve
|
||||
gc.MoveTo(x, y)
|
||||
gc.LineTo(x1, y1)
|
||||
gc.LineTo(x2, y2)
|
||||
gc.LineTo(x3, y3)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestDrawCubicCurve", i)
|
||||
}
|
||||
|
||||
/*
|
||||
<img src="../test_results/TestDash.png"/>
|
||||
*/
|
||||
func TestDash() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.SetLineDash([]float{50, 10, 10, 10}, -50.0)
|
||||
gc.SetLineCap(draw2d.ButtCap)
|
||||
gc.SetLineJoin(draw2d.BevelJoin)
|
||||
gc.SetLineWidth(10)
|
||||
|
||||
gc.MoveTo(128.0, 25.6)
|
||||
gc.LineTo(128.0, 25.6)
|
||||
gc.LineTo(230.4, 230.4)
|
||||
gc.RLineTo(-102.4, 0.0)
|
||||
gc.CubicCurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0)
|
||||
gc.Stroke()
|
||||
gc.SetLineDash(nil, 0.0)
|
||||
saveToPngFile("TestDash", i)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<img src="../test_results/TestFillStroke.png"/>
|
||||
*/
|
||||
func TestFillStroke() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.MoveTo(128.0, 25.6)
|
||||
gc.LineTo(230.4, 230.4)
|
||||
gc.RLineTo(-102.4, 0.0)
|
||||
gc.CubicCurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0)
|
||||
gc.ClosePath()
|
||||
|
||||
gc.MoveTo(64.0, 25.6)
|
||||
gc.RLineTo(51.2, 51.2)
|
||||
gc.RLineTo(-51.2, 51.2)
|
||||
gc.RLineTo(-51.2, -51.2)
|
||||
gc.ClosePath()
|
||||
|
||||
gc.SetLineWidth(10.0)
|
||||
gc.SetFillColor(image.RGBAColor{0, 0, 0xFF, 0xFF})
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.FillStroke()
|
||||
saveToPngFile("TestFillStroke", i)
|
||||
}
|
||||
|
||||
/*
|
||||
<img src="../test_results/TestFillStyle.png"/>
|
||||
*/
|
||||
func TestFillStyle() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.SetLineWidth(6)
|
||||
|
||||
gc.Rect(12, 12, 244, 70)
|
||||
|
||||
wheel1 := new(draw2d.Path)
|
||||
wheel1.ArcTo(64, 64, 40, 40, 0, 2*math.Pi)
|
||||
wheel2 := new(draw2d.Path)
|
||||
wheel2.ArcTo(192, 64, 40, 40, 0, 2*math.Pi)
|
||||
|
||||
gc.SetFillRule(draw2d.FillRuleEvenOdd)
|
||||
gc.SetFillColor(image.RGBAColor{0, 0xB2, 0, 0xFF})
|
||||
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.FillStroke(wheel1, wheel2)
|
||||
|
||||
gc.Rect(12, 140, 244, 198)
|
||||
wheel1 = new(draw2d.Path)
|
||||
wheel1.ArcTo(64, 192, 40, 40, 0, 2*math.Pi)
|
||||
wheel2 = new(draw2d.Path)
|
||||
wheel2.ArcTo(192, 192, 40, 40, 0, -2*math.Pi)
|
||||
|
||||
gc.SetFillRule(draw2d.FillRuleWinding)
|
||||
gc.SetFillColor(image.RGBAColor{0, 0, 0xE5, 0xFF})
|
||||
gc.FillStroke(wheel1, wheel2)
|
||||
saveToPngFile("TestFillStyle", i)
|
||||
}
|
||||
|
||||
func TestMultiSegmentCaps() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.MoveTo(50.0, 75.0)
|
||||
gc.LineTo(200.0, 75.0)
|
||||
|
||||
gc.MoveTo(50.0, 125.0)
|
||||
gc.LineTo(200.0, 125.0)
|
||||
|
||||
gc.MoveTo(50.0, 175.0)
|
||||
gc.LineTo(200.0, 175.0)
|
||||
|
||||
gc.SetLineWidth(30.0)
|
||||
gc.SetLineCap(draw2d.RoundCap)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestMultiSegmentCaps", i)
|
||||
}
|
||||
|
||||
|
||||
func TestRoundRectangle() {
|
||||
i, gc := initGc(w, h)
|
||||
/* a custom shape that could be wrapped in a function */
|
||||
x, y := 25.6, 25.6
|
||||
width, height := 204.8, 204.8
|
||||
aspect := 1.0 /* aspect ratio */
|
||||
corner_radius := height / 10.0 /* and corner curvature radius */
|
||||
|
||||
radius := corner_radius / aspect
|
||||
degrees := math.Pi / 180.0
|
||||
|
||||
gc.ArcTo(x+width-radius, y+radius, radius, radius, -90*degrees, 90*degrees)
|
||||
gc.ArcTo(x+width-radius, y+height-radius, radius, radius, 0*degrees, 90*degrees)
|
||||
gc.ArcTo(x+radius, y+height-radius, radius, radius, 90*degrees, 90*degrees)
|
||||
gc.ArcTo(x+radius, y+radius, radius, radius, 180*degrees, 90*degrees)
|
||||
gc.ClosePath()
|
||||
|
||||
gc.SetFillColor(image.RGBAColor{0x80, 0x80, 0xFF, 0xFF})
|
||||
gc.SetStrokeColor(image.RGBAColor{0x80, 0, 0, 0x80})
|
||||
gc.SetLineWidth(10.0)
|
||||
gc.FillStroke()
|
||||
|
||||
saveToPngFile("TestRoundRectangle", i)
|
||||
}
|
||||
|
||||
func TestLineCap() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.SetLineWidth(30.0)
|
||||
gc.SetLineCap(draw2d.ButtCap)
|
||||
gc.MoveTo(64.0, 50.0)
|
||||
gc.LineTo(64.0, 200.0)
|
||||
gc.Stroke()
|
||||
gc.SetLineCap(draw2d.RoundCap)
|
||||
gc.MoveTo(128.0, 50.0)
|
||||
gc.LineTo(128.0, 200.0)
|
||||
gc.Stroke()
|
||||
gc.SetLineCap(draw2d.SquareCap)
|
||||
gc.MoveTo(192.0, 50.0)
|
||||
gc.LineTo(192.0, 200.0)
|
||||
gc.Stroke()
|
||||
|
||||
/* draw helping lines */
|
||||
gc.SetStrokeColor(image.RGBAColor{0xFF, 0x33, 0x33, 0xFF})
|
||||
gc.SetLineWidth(2.56)
|
||||
gc.MoveTo(64.0, 50.0)
|
||||
gc.LineTo(64.0, 200.0)
|
||||
gc.MoveTo(128.0, 50.0)
|
||||
gc.LineTo(128.0, 200.0)
|
||||
gc.MoveTo(192.0, 50.0)
|
||||
gc.LineTo(192.0, 200.0)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestLineCap", i)
|
||||
}
|
||||
func TestLineJoin() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.SetLineWidth(40)
|
||||
gc.MoveTo(76.8, 84.48)
|
||||
gc.RLineTo(51.2, -51.2)
|
||||
gc.RLineTo(51.2, 51.2)
|
||||
gc.SetLineJoin(draw2d.MiterJoin) /* default */
|
||||
gc.Stroke()
|
||||
|
||||
gc.MoveTo(76.8, 161.28)
|
||||
gc.RLineTo(51.2, -51.2)
|
||||
gc.RLineTo(51.2, 51.2)
|
||||
gc.SetLineJoin(draw2d.BevelJoin)
|
||||
gc.Stroke()
|
||||
|
||||
gc.MoveTo(76.8, 238.08)
|
||||
gc.RLineTo(51.2, -51.2)
|
||||
gc.RLineTo(51.2, 51.2)
|
||||
gc.SetLineJoin(draw2d.RoundJoin)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestLineJoin", i)
|
||||
}
|
||||
|
||||
func TestBubble() {
|
||||
i, gc := initGc(w, h)
|
||||
gc.BeginPath()
|
||||
gc.MoveTo(75,25)
|
||||
gc.QuadCurveTo(25,25,25,62.5)
|
||||
gc.QuadCurveTo(25,100,50,100)
|
||||
gc.QuadCurveTo(50,120,30,125)
|
||||
gc.QuadCurveTo(60,120,65,100)
|
||||
gc.QuadCurveTo(125,100,125,62.5)
|
||||
gc.QuadCurveTo(125,25,75,25)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestBubble", i)
|
||||
}
|
||||
|
||||
func TestStar() {
|
||||
i, gc := initGc(w, h)
|
||||
for i := 0.0 ; i < 360; i = i + 10 {// Go from 0 to 360 degrees in 10 degree steps
|
||||
gc.Save()
|
||||
gc.SetLineWidth(5) // Keep rotations temporary
|
||||
gc.Translate(144, 144)
|
||||
gc.Rotate(i * (math.Pi / 180.0)) // Rotate by degrees on stack from 'for'
|
||||
gc.MoveTo(0, 0)
|
||||
gc.LineTo(72, 0)
|
||||
gc.Stroke()
|
||||
gc.Restore() // Get back the unrotated state
|
||||
}
|
||||
saveToPngFile("TestStar", i)
|
||||
}
|
||||
|
||||
func TestTransform() {
|
||||
i, gc := initGc(800, 600)
|
||||
|
||||
gc.Save()
|
||||
gc.Translate(40, 40) // Set origin to (40, 40)
|
||||
gc.BeginPath()
|
||||
gc.MoveTo(0,0)
|
||||
gc.RLineTo(72,0)
|
||||
gc.RLineTo(0, 72)
|
||||
gc.RLineTo(-72,0)
|
||||
gc.ClosePath()
|
||||
gc.Stroke()
|
||||
gc.Restore()
|
||||
|
||||
gc.Save()
|
||||
gc.Translate(100, 150) // Translate origin to (100, 150)
|
||||
gc.Rotate(30* (math.Pi / 180.0)) // Rotate counter-clockwise by 30 degrees
|
||||
gc.BeginPath()
|
||||
gc.MoveTo(0,0)
|
||||
gc.RLineTo(72,0)
|
||||
gc.RLineTo(0, 72)
|
||||
gc.RLineTo(-72,0)
|
||||
gc.ClosePath() // Draw box...
|
||||
gc.Stroke()
|
||||
gc.Restore()
|
||||
|
||||
gc.Save()
|
||||
gc.Translate(40, 300) // Translate to (40, 300)
|
||||
gc.Scale(0.5, 1) // Reduce x coord by 1/2, y coord left alone
|
||||
gc.BeginPath()
|
||||
gc.MoveTo(0,0)
|
||||
gc.RLineTo(72,0)
|
||||
gc.RLineTo(0, 72)
|
||||
gc.RLineTo(-72,0)
|
||||
gc.ClosePath() // Draw box...
|
||||
gc.Stroke()
|
||||
gc.Restore()
|
||||
|
||||
gc.Save()
|
||||
gc.Translate(300, 300) // Set origin to (300, 300)
|
||||
gc.Rotate(45* (math.Pi / 180.0)) // Rotate coordinates by 45 degrees
|
||||
gc.Scale(0.5, 1) // Scale coordinates
|
||||
gc.BeginPath()
|
||||
gc.MoveTo(0,0)
|
||||
gc.RLineTo(72,0)
|
||||
gc.RLineTo(0, 72)
|
||||
gc.RLineTo(-72,0)
|
||||
gc.ClosePath() // Draw box
|
||||
gc.Stroke()
|
||||
gc.Restore()
|
||||
|
||||
saveToPngFile("TestTransform", i)
|
||||
}
|
||||
|
||||
func TestPathTransform() {
|
||||
i, gc := initGc(800, 600)
|
||||
gc.SetLineWidth(20)
|
||||
gc.Scale(1,4)
|
||||
gc.ArcTo(200, 50, 50, 50, 0, math.Pi * 2)
|
||||
gc.Stroke()
|
||||
saveToPngFile("TestPathTransform", i)
|
||||
}
|
||||
|
||||
func main() {
|
||||
TestPath()
|
||||
TestDrawArc()
|
||||
TestDrawArcNegative()
|
||||
TestCurveRectangle()
|
||||
TestDrawCubicCurve()
|
||||
TestDash()
|
||||
TestFillStroke()
|
||||
TestFillStyle()
|
||||
TestMultiSegmentCaps()
|
||||
TestRoundRectangle()
|
||||
TestLineCap()
|
||||
TestLineJoin()
|
||||
TestBubble()
|
||||
TestStar()
|
||||
TestTransform()
|
||||
TestPathTransform()
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package main
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bufio"
|
||||
"time"
|
||||
"math"
|
||||
|
||||
"image"
|
||||
"image/png"
|
||||
"draw2d"
|
||||
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
|
||||
)
|
||||
|
||||
const (
|
||||
width, height = 500, 300
|
||||
)
|
||||
|
||||
var (
|
||||
lastTime int64
|
||||
folder = "../../../../wiki/test_results/"
|
||||
)
|
||||
|
||||
func initGc(w, h int) (image.Image, *draw2d.GraphicContext) {
|
||||
i := image.NewRGBA(w, h)
|
||||
gc := draw2d.NewGraphicContext(i)
|
||||
lastTime = time.Nanoseconds()
|
||||
|
||||
gc.SetStrokeColor(image.Black)
|
||||
gc.SetFillColor(image.White)
|
||||
// fill the background
|
||||
//gc.Clear()
|
||||
|
||||
return i, gc
|
||||
}
|
||||
|
||||
func saveToPngFile(TestName string, m image.Image) {
|
||||
dt := time.Nanoseconds() - lastTime
|
||||
fmt.Printf("%s during: %f ms\n", TestName, float(dt)*10e-6)
|
||||
filePath := folder + TestName + ".png"
|
||||
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
b := bufio.NewWriter(f)
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Wrote %s OK.\n", filePath)
|
||||
}
|
||||
|
||||
func gordon(gc *draw2d.GraphicContext, x, y, w, h float) {
|
||||
h23 := (h * 2) / 3
|
||||
|
||||
blf := image.RGBAColor{0, 0, 0, 0xff}
|
||||
wf := image.RGBAColor{0xff, 0xff, 0xff, 0xff}
|
||||
nf := image.RGBAColor{0x8B, 0x45, 0x13, 0xff}
|
||||
brf := image.RGBAColor{0x8B, 0x45, 0x13, 0x99}
|
||||
brb := image.RGBAColor{0x8B, 0x45, 0x13, 0xBB}
|
||||
|
||||
gc.MoveTo(x, y+h)
|
||||
gc.CubicCurveTo(x, y+h, x+w/2, y-h, x+w, y+h)
|
||||
gc.ClosePath()
|
||||
gc.SetFillColor(brb)
|
||||
gc.Fill()
|
||||
gc.RoundRect(x, y+h, x+ w, y+h+h, 10, 10)
|
||||
gc.Fill()
|
||||
gc.Circle(x, y+h, w/12) // left ear
|
||||
gc.SetFillColor(brf)
|
||||
gc.Fill()
|
||||
gc.Circle(x, y+h, w/12-10)
|
||||
gc.SetFillColor(nf)
|
||||
gc.Fill()
|
||||
|
||||
gc.Circle(x+w, y+h, w/12) // right ear
|
||||
gc.SetFillColor(brf)
|
||||
gc.Fill()
|
||||
gc.Circle(x+w, y+h, w/12-10)
|
||||
gc.SetFillColor(nf)
|
||||
gc.Fill()
|
||||
|
||||
gc.Circle(x+w/3, y+h23, w/9) // left eye
|
||||
gc.SetFillColor(wf)
|
||||
gc.Fill()
|
||||
gc.Circle(x+w/3+10, y+h23, w / 10 - 10)
|
||||
gc.SetFillColor(blf)
|
||||
gc.Fill()
|
||||
gc.Circle(x+w/3+15, y+h23, 5)
|
||||
gc.SetFillColor(wf)
|
||||
gc.Fill()
|
||||
|
||||
gc.Circle(x+w-w/3, y+h23, w/9) // right eye
|
||||
gc.Fill()
|
||||
gc.Circle(x+w-w/3+10, y+h23, w / 10 - 10)
|
||||
gc.SetFillColor(blf)
|
||||
gc.Fill()
|
||||
gc.Circle(x+w-(w/3)+15, y+h23, 5)
|
||||
gc.SetFillColor(wf)
|
||||
gc.Fill()
|
||||
|
||||
gc.SetFillColor(wf)
|
||||
gc.RoundRect(x+w/2-w/8, y+h+30, x+w/2-w/8 + w/8, y+h+30 + w/6, 5, 5) // left tooth
|
||||
gc.Fill()
|
||||
gc.RoundRect(x+w/2, y+h+30, x+w/2+w/8, y+h+30+w/6, 5, 5) // right tooth
|
||||
gc.Fill()
|
||||
|
||||
|
||||
gc.Ellipse(x+(w/2), y+h+30, w/6, w/12) // snout
|
||||
gc.SetFillColor(nf)
|
||||
gc.Fill()
|
||||
gc.Ellipse(x+(w/2), y+h+10, w/10, w/12) // nose
|
||||
gc.SetFillColor(blf)
|
||||
gc.Fill()
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
i, gc := initGc(width, height)
|
||||
gc.Clear()
|
||||
gc.Translate(100, 100)
|
||||
gc.Rotate(-30 * (math.Pi / 180.0))
|
||||
gordon(gc, 48, 48, 240, 72)
|
||||
saveToPngFile("TestGopher", i)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"draw2d"
|
||||
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
path := new(draw2d.Path)
|
||||
path.MoveTo(2.0, 3.0)
|
||||
path.LineTo(2.0, 3.0)
|
||||
path.QuadCurveTo(2.0, 3.0, 10, 20)
|
||||
path.CubicCurveTo(2.0, 3.0, 10, 20, 13, 23)
|
||||
path.ArcTo(2.0, 3.0, 100, 200, 200, 300)
|
||||
fmt.Printf("%v\n", path)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d
|
||||
GOFILES=\
|
||||
arc.go\
|
||||
curves.go\
|
||||
draw2d.go\
|
||||
math.go\
|
||||
path.go\
|
||||
transform.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
|
||||
func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float) (lastX, lastY float){
|
||||
end := start + angle
|
||||
clockWise := true
|
||||
if angle < 0 {
|
||||
clockWise = false
|
||||
}
|
||||
ra := (fabs(rx) + fabs(ry)) / 2
|
||||
da := acos(ra/(ra+0.125/scale)) * 2
|
||||
//normalize
|
||||
if !clockWise {
|
||||
da = -da
|
||||
}
|
||||
angle = start + da
|
||||
var curX, curY float
|
||||
for {
|
||||
if (angle < end-da/4) != clockWise {
|
||||
curX = x + cos(end)*rx
|
||||
curY = y + sin(end)*ry
|
||||
return curX, curY
|
||||
}
|
||||
curX = x + cos(angle)*rx
|
||||
curY = y + sin(angle)*ry
|
||||
|
||||
angle += da
|
||||
t.Vertex(curX, curY)
|
||||
}
|
||||
return curX, curY
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var (
|
||||
CurveRecursionLimit = 32
|
||||
CurveCollinearityEpsilon = 1e-30
|
||||
CurveAngleToleranceEpsilon = 0.01
|
||||
)
|
||||
|
||||
/*
|
||||
The function has the following parameters:
|
||||
approximationScale :
|
||||
Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one.
|
||||
It always has some scaling coefficient.
|
||||
The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels.
|
||||
Usually it looks as follows:
|
||||
curved.approximationScale(transform.scale());
|
||||
where transform is the affine matrix that includes all the transformations, including viewport and zoom.
|
||||
angleTolerance :
|
||||
You set it in radians.
|
||||
The less this value is the more accurate will be the approximation at sharp turns.
|
||||
But 0 means that we don't consider angle conditions at all.
|
||||
cuspLimit :
|
||||
An angle in radians.
|
||||
If 0, only the real cusps will have bevel cuts.
|
||||
If more than 0, it will restrict the sharpness.
|
||||
The more this value is the less sharp turns will be cut.
|
||||
Typically it should not exceed 10-15 degrees.
|
||||
*/
|
||||
func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float) {
|
||||
cuspLimit = computeCuspLimit(cuspLimit)
|
||||
distanceToleranceSquare := 0.5 / approximationScale
|
||||
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
||||
recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* see cubicBezier comments for approximationScale and angleTolerance definition
|
||||
*/
|
||||
func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float) {
|
||||
distanceToleranceSquare := 0.5 / approximationScale
|
||||
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
||||
|
||||
recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance)
|
||||
}
|
||||
|
||||
|
||||
func computeCuspLimit(v float) (r float) {
|
||||
if v == 0.0 {
|
||||
r = 0.0
|
||||
} else {
|
||||
r = math.Pi - v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
||||
*/
|
||||
func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float, level int, distanceToleranceSquare, angleTolerance float) {
|
||||
if level > CurveRecursionLimit {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate all the mid-points of the line segments
|
||||
//----------------------
|
||||
x12 := (x1 + x2) / 2
|
||||
y12 := (y1 + y2) / 2
|
||||
x23 := (x2 + x3) / 2
|
||||
y23 := (y2 + y3) / 2
|
||||
x123 := (x12 + x23) / 2
|
||||
y123 := (y12 + y23) / 2
|
||||
|
||||
dx := x3 - x1
|
||||
dy := y3 - y1
|
||||
d := fabs(((x2-x3)*dy - (y2-y3)*dx))
|
||||
|
||||
if d > CurveCollinearityEpsilon {
|
||||
// Regular case
|
||||
//-----------------
|
||||
if d*d <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||
// If the curvature doesn't exceed the distanceTolerance value
|
||||
// we tend to finish subdivisions.
|
||||
//----------------------
|
||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||
v.Vertex(x123, y123)
|
||||
return
|
||||
}
|
||||
|
||||
// Angle & Cusp Condition
|
||||
//----------------------
|
||||
da := fabs(atan2(y3-y2, x3-x2) - atan2(y2-y1, x2-x1))
|
||||
if da >= math.Pi {
|
||||
da = 2*math.Pi - da
|
||||
}
|
||||
|
||||
if da < angleTolerance {
|
||||
// Finally we can stop the recursion
|
||||
//----------------------
|
||||
v.Vertex(x123, y123)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Collinear case
|
||||
//------------------
|
||||
da := dx*dx + dy*dy
|
||||
if da == 0 {
|
||||
d = squareDistance(x1, y1, x2, y2)
|
||||
} else {
|
||||
d = ((x2-x1)*dx + (y2-y1)*dy) / da
|
||||
if d > 0 && d < 1 {
|
||||
// Simple collinear case, 1---2---3
|
||||
// We can leave just two endpoints
|
||||
return
|
||||
}
|
||||
if d <= 0 {
|
||||
d = squareDistance(x2, y2, x1, y1)
|
||||
} else if d >= 1 {
|
||||
d = squareDistance(x2, y2, x3, y3)
|
||||
} else {
|
||||
d = squareDistance(x2, y2, x1+d*dx, y1+d*dy)
|
||||
}
|
||||
}
|
||||
if d < distanceToleranceSquare {
|
||||
v.Vertex(x2, y2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Continue subdivision
|
||||
//----------------------
|
||||
recursiveQuadraticBezierBezier(v, x1, y1, x12, y12, x123, y123, level+1, distanceToleranceSquare, angleTolerance)
|
||||
recursiveQuadraticBezierBezier(v, x123, y123, x23, y23, x3, y3, level+1, distanceToleranceSquare, angleTolerance)
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
||||
*/
|
||||
func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float, level int, distanceToleranceSquare, angleTolerance, cuspLimit float) {
|
||||
if level > CurveRecursionLimit {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate all the mid-points of the line segments
|
||||
//----------------------
|
||||
x12 := (x1 + x2) / 2
|
||||
y12 := (y1 + y2) / 2
|
||||
x23 := (x2 + x3) / 2
|
||||
y23 := (y2 + y3) / 2
|
||||
x34 := (x3 + x4) / 2
|
||||
y34 := (y3 + y4) / 2
|
||||
x123 := (x12 + x23) / 2
|
||||
y123 := (y12 + y23) / 2
|
||||
x234 := (x23 + x34) / 2
|
||||
y234 := (y23 + y34) / 2
|
||||
x1234 := (x123 + x234) / 2
|
||||
y1234 := (y123 + y234) / 2
|
||||
|
||||
// Try to approximate the full cubic curve by a single straight line
|
||||
//------------------
|
||||
dx := x4 - x1
|
||||
dy := y4 - y1
|
||||
|
||||
d2 := fabs(((x2-x4)*dy - (y2-y4)*dx))
|
||||
d3 := fabs(((x3-x4)*dy - (y3-y4)*dx))
|
||||
|
||||
switch {
|
||||
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
||||
// All collinear OR p1==p4
|
||||
//----------------------
|
||||
k := dx*dx + dy*dy
|
||||
if k == 0 {
|
||||
d2 = squareDistance(x1, y1, x2, y2)
|
||||
d3 = squareDistance(x4, y4, x3, y3)
|
||||
} else {
|
||||
k = 1 / k
|
||||
da1 := x2 - x1
|
||||
da2 := y2 - y1
|
||||
d2 = k * (da1*dx + da2*dy)
|
||||
da1 = x3 - x1
|
||||
da2 = y3 - y1
|
||||
d3 = k * (da1*dx + da2*dy)
|
||||
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
|
||||
// Simple collinear case, 1---2---3---4
|
||||
// We can leave just two endpoints
|
||||
return
|
||||
}
|
||||
if d2 <= 0 {
|
||||
d2 = squareDistance(x2, y2, x1, y1)
|
||||
} else if d2 >= 1 {
|
||||
d2 = squareDistance(x2, y2, x4, y4)
|
||||
} else {
|
||||
d2 = squareDistance(x2, y2, x1+d2*dx, y1+d2*dy)
|
||||
}
|
||||
|
||||
if d3 <= 0 {
|
||||
d3 = squareDistance(x3, y3, x1, y1)
|
||||
} else if d3 >= 1 {
|
||||
d3 = squareDistance(x3, y3, x4, y4)
|
||||
} else {
|
||||
d3 = squareDistance(x3, y3, x1+d3*dx, y1+d3*dy)
|
||||
}
|
||||
}
|
||||
if d2 > d3 {
|
||||
if d2 < distanceToleranceSquare {
|
||||
v.Vertex(x2, y2)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if d3 < distanceToleranceSquare {
|
||||
v.Vertex(x3, y3)
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
|
||||
// p1,p2,p4 are collinear, p3 is significant
|
||||
//----------------------
|
||||
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||
v.Vertex(x23, y23)
|
||||
return
|
||||
}
|
||||
|
||||
// Angle Condition
|
||||
//----------------------
|
||||
da1 := fabs(atan2(y4-y3, x4-x3) - atan2(y3-y2, x3-x2))
|
||||
if da1 >= math.Pi {
|
||||
da1 = 2*math.Pi - da1
|
||||
}
|
||||
|
||||
if da1 < angleTolerance {
|
||||
v.Vertex(x2, y2)
|
||||
v.Vertex(x3, y3)
|
||||
return
|
||||
}
|
||||
|
||||
if cuspLimit != 0.0 {
|
||||
if da1 > cuspLimit {
|
||||
v.Vertex(x3, y3)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
||||
// p1,p3,p4 are collinear, p2 is significant
|
||||
//----------------------
|
||||
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||
v.Vertex(x23, y23)
|
||||
return
|
||||
}
|
||||
|
||||
// Angle Condition
|
||||
//----------------------
|
||||
da1 := fabs(atan2(y3-y2, x3-x2) - atan2(y2-y1, x2-x1))
|
||||
if da1 >= math.Pi {
|
||||
da1 = 2*math.Pi - da1
|
||||
}
|
||||
|
||||
if da1 < angleTolerance {
|
||||
v.Vertex(x2, y2)
|
||||
v.Vertex(x3, y3)
|
||||
return
|
||||
}
|
||||
|
||||
if cuspLimit != 0.0 {
|
||||
if da1 > cuspLimit {
|
||||
v.Vertex(x2, y2)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
|
||||
// Regular case
|
||||
//-----------------
|
||||
if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||
// If the curvature doesn't exceed the distanceTolerance value
|
||||
// we tend to finish subdivisions.
|
||||
//----------------------
|
||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||
v.Vertex(x23, y23)
|
||||
return
|
||||
}
|
||||
|
||||
// Angle & Cusp Condition
|
||||
//----------------------
|
||||
k := atan2(y3-y2, x3-x2)
|
||||
da1 := fabs(k - atan2(y2-y1, x2-x1))
|
||||
da2 := fabs(atan2(y4-y3, x4-x3) - k)
|
||||
if da1 >= math.Pi {
|
||||
da1 = 2*math.Pi - da1
|
||||
}
|
||||
if da2 >= math.Pi {
|
||||
da2 = 2*math.Pi - da2
|
||||
}
|
||||
|
||||
if da1+da2 < angleTolerance {
|
||||
// Finally we can stop the recursion
|
||||
//----------------------
|
||||
v.Vertex(x23, y23)
|
||||
return
|
||||
}
|
||||
|
||||
if cuspLimit != 0.0 {
|
||||
if da1 > cuspLimit {
|
||||
v.Vertex(x2, y2)
|
||||
return
|
||||
}
|
||||
|
||||
if da2 > cuspLimit {
|
||||
v.Vertex(x3, y3)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Continue subdivision
|
||||
//----------------------
|
||||
recursiveCubicBezier(v, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
|
||||
recursiveCubicBezier(v, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package draw2d
|
||||
|
||||
|
||||
type LineDasher struct {
|
||||
|
||||
}
|
||||
|
||||
func (d *LineDasher) Start() {
|
||||
|
||||
}
|
||||
|
||||
func (d *LineDasher) Stop() {
|
||||
|
||||
}
|
||||
|
||||
func (d *LineDasher) Vertex(x, y float) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
type PathAdapter struct {
|
||||
path *raster.Path
|
||||
x, y, distance float
|
||||
dash []float
|
||||
currentDash int
|
||||
dashOffset float
|
||||
}
|
||||
|
||||
func tracePath(dash []float, dashOffset float, paths ...*Path) *raster.Path {
|
||||
var adapter PathAdapter
|
||||
if dash != nil && len(dash) > 0 {
|
||||
adapter.dash = dash
|
||||
} else {
|
||||
adapter.dash = nil
|
||||
}
|
||||
adapter.currentDash = 0
|
||||
adapter.dashOffset = dashOffset
|
||||
adapter.path = new(raster.Path)
|
||||
for _, path := range paths {
|
||||
path.TraceLine(&adapter)
|
||||
}
|
||||
return adapter.path
|
||||
}
|
||||
|
||||
func floatToPoint(x, y float) raster.Point {
|
||||
return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}
|
||||
}
|
||||
|
||||
func (p *PathAdapter) MoveTo(x, y float) {
|
||||
p.path.Start(floatToPoint(x, y))
|
||||
p.x, p.y = x, y
|
||||
p.distance = p.dashOffset
|
||||
p.currentDash = 0
|
||||
}
|
||||
|
||||
func (p *PathAdapter) LineTo(x, y float) {
|
||||
if p.dash != nil {
|
||||
rest := p.dash[p.currentDash] - p.distance
|
||||
for rest < 0 {
|
||||
p.distance = p.distance - p.dash[p.currentDash]
|
||||
p.currentDash = (p.currentDash + 1) % len(p.dash)
|
||||
rest = p.dash[p.currentDash] - p.distance
|
||||
}
|
||||
d := distance(p.x, p.y, x, y)
|
||||
for d >= rest {
|
||||
k := rest / d
|
||||
lx := p.x + k*(x-p.x)
|
||||
ly := p.y + k*(y-p.y)
|
||||
if p.currentDash%2 == 0 {
|
||||
// line
|
||||
p.path.Add1(floatToPoint(lx, ly))
|
||||
} else {
|
||||
// gap
|
||||
p.path.Start(floatToPoint(lx, ly))
|
||||
}
|
||||
d = d - rest
|
||||
p.x, p.y = lx, ly
|
||||
p.currentDash = (p.currentDash + 1) % len(p.dash)
|
||||
rest = p.dash[p.currentDash]
|
||||
}
|
||||
p.distance = d
|
||||
if p.currentDash%2 == 0 {
|
||||
p.path.Add1(floatToPoint(x, y))
|
||||
} else {
|
||||
p.path.Start(floatToPoint(x, y))
|
||||
}
|
||||
if p.distance >= p.dash[p.currentDash] {
|
||||
p.distance = p.distance - p.dash[p.currentDash]
|
||||
p.currentDash = (p.currentDash + 1) % len(p.dash)
|
||||
}
|
||||
} else {
|
||||
p.path.Add1(floatToPoint(x, y))
|
||||
}
|
||||
p.x, p.y = x, y
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,20 @@
|
|||
package draw2d
|
||||
|
||||
type DemuxConverter struct {
|
||||
converters []VertexConverter
|
||||
}
|
||||
|
||||
func NewDemuxConverter(converters ...VertexConverter) (*DemuxConverter) {
|
||||
return &DemuxConverter{converters}
|
||||
}
|
||||
|
||||
func (dc*DemuxConverter) NextCommand(cmd VertexCommand) {
|
||||
for _,converter := range dc.converters {
|
||||
converter.NextCommand(cmd)
|
||||
}
|
||||
}
|
||||
func (dc *DemuxConverter) Vertex(x, y float) {
|
||||
for _,converter := range dc.converters {
|
||||
converter.Vertex(x, y)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
// this package contains a GraphicalContext that wraps an image and permit draw vectorial 2D forms.
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"exp/draw"
|
||||
"image"
|
||||
"freetype-go.googlecode.com/hg/freetype/raster"
|
||||
"math"
|
||||
)
|
||||
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
FillRuleEvenOdd FillRule = iota
|
||||
FillRuleWinding
|
||||
)
|
||||
|
||||
type GraphicContext struct {
|
||||
PaintedImage *image.RGBA
|
||||
fillRasterizer *raster.Rasterizer
|
||||
strokeRasterizer *raster.Rasterizer
|
||||
current *contextStack
|
||||
}
|
||||
|
||||
type contextStack struct {
|
||||
tr MatrixTransform
|
||||
path *Path
|
||||
lineWidth float
|
||||
dash []float
|
||||
dashOffset float
|
||||
strokeColor image.Color
|
||||
fillColor image.Color
|
||||
fillRule FillRule
|
||||
cap Cap
|
||||
join Join
|
||||
previous *contextStack
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Graphic context from an image
|
||||
*/
|
||||
func NewGraphicContext(pi *image.RGBA) *GraphicContext {
|
||||
gc := new(GraphicContext)
|
||||
gc.PaintedImage = pi
|
||||
width, height := gc.PaintedImage.Bounds().Dx(), gc.PaintedImage.Bounds().Dy()
|
||||
gc.fillRasterizer = raster.NewRasterizer(width, height)
|
||||
gc.strokeRasterizer = raster.NewRasterizer(width, height)
|
||||
|
||||
gc.current = new(contextStack)
|
||||
|
||||
gc.current.tr = NewIdentityMatrix()
|
||||
gc.current.path = new(Path)
|
||||
gc.current.lineWidth = 1.0
|
||||
gc.current.strokeColor = image.Black
|
||||
gc.current.fillColor = image.White
|
||||
gc.current.cap = RoundCap
|
||||
gc.current.fillRule = FillRuleEvenOdd
|
||||
gc.current.join = RoundJoin
|
||||
return gc
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetMatrixTransform(tr MatrixTransform) {
|
||||
gc.current.tr = tr
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) ComposeMatrixTransform(tr MatrixTransform) {
|
||||
gc.current.tr = tr.Multiply(gc.current.tr)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Rotate(angle float) {
|
||||
gc.current.tr = NewRotationMatrix(angle).Multiply(gc.current.tr)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Translate(tx, ty float) {
|
||||
gc.current.tr = NewTranslationMatrix(tx, ty).Multiply(gc.current.tr)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Scale(sx, sy float) {
|
||||
gc.current.tr = NewScaleMatrix(sx, sy).Multiply(gc.current.tr)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Clear() {
|
||||
width, height := gc.PaintedImage.Bounds().Dx(), gc.PaintedImage.Bounds().Dy()
|
||||
gc.ClearRect(0, 0, width, height)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
|
||||
imageColor := image.NewColorImage(gc.current.fillColor)
|
||||
draw.Draw(gc.PaintedImage, image.Rect(x1, y1, x2, y2), imageColor, image.ZP)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetStrokeColor(c image.Color) {
|
||||
gc.current.strokeColor = c
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetFillColor(c image.Color) {
|
||||
gc.current.fillColor = c
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetFillRule(f FillRule) {
|
||||
gc.current.fillRule = f
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetLineWidth(lineWidth float) {
|
||||
gc.current.lineWidth = lineWidth
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetLineCap(cap Cap) {
|
||||
gc.current.cap = cap
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetLineJoin(join Join) {
|
||||
gc.current.join = join
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) SetLineDash(dash []float, dashOffset float) {
|
||||
gc.current.dash = dash
|
||||
gc.current.dashOffset = dashOffset
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Save() {
|
||||
context := new(contextStack)
|
||||
context.lineWidth = gc.current.lineWidth
|
||||
context.strokeColor = gc.current.strokeColor
|
||||
context.fillColor = gc.current.fillColor
|
||||
context.fillRule = gc.current.fillRule
|
||||
context.dash = gc.current.dash
|
||||
context.dashOffset = gc.current.dashOffset
|
||||
context.cap = gc.current.cap
|
||||
context.join = gc.current.join
|
||||
context.path = gc.current.path.Copy()
|
||||
copy(context.tr[:], gc.current.tr[:])
|
||||
context.previous = gc.current
|
||||
gc.current = context
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Restore() {
|
||||
if gc.current.previous != nil {
|
||||
oldContext := gc.current
|
||||
gc.current = gc.current.previous
|
||||
oldContext.previous = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) BeginPath() {
|
||||
gc.current.path = new(Path)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) MoveTo(x, y float) {
|
||||
gc.current.path.MoveTo(x, y)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RMoveTo(dx, dy float) {
|
||||
gc.current.path.RMoveTo(dx, dy)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) LineTo(x, y float) {
|
||||
gc.current.path.LineTo(x, y)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RLineTo(dx, dy float) {
|
||||
gc.current.path.RLineTo(dx, dy)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) QuadCurveTo(cx, cy, x, y float) {
|
||||
gc.current.path.QuadCurveTo(cx, cy, x, y)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float) {
|
||||
gc.current.path.RQuadCurveTo(dcx, dcy, dx, dy)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) {
|
||||
gc.current.path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) {
|
||||
gc.current.path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float) {
|
||||
gc.current.path.ArcTo(cx, cy, rx, ry, startAngle, angle)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) {
|
||||
gc.current.path.RArcTo(dcx, dcy, rx, ry, startAngle, angle)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) ClosePath() {
|
||||
gc.current.path.Close()
|
||||
}
|
||||
//high level path creation
|
||||
func (gc *GraphicContext) Rect(x1, y1, x2, y2 float) {
|
||||
if gc.current.path.isEmpty() {
|
||||
gc.current.path.MoveTo(x1, y1)
|
||||
} else {
|
||||
gc.current.path.LineTo(x1, y1)
|
||||
}
|
||||
gc.current.path.LineTo(x2, y1)
|
||||
gc.current.path.LineTo(x2, y2)
|
||||
gc.current.path.LineTo(x1, y2)
|
||||
gc.current.path.Close()
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) RoundRect(x1, y1, x2, y2, arcWidth, arcHeight float) {
|
||||
arcWidth = arcWidth/2;
|
||||
arcHeight = arcHeight/2;
|
||||
gc.MoveTo(x1, y1+ arcHeight);
|
||||
gc.QuadCurveTo(x1, y1, x1 + arcWidth, y1);
|
||||
gc.LineTo(x2-arcWidth, y1);
|
||||
gc.QuadCurveTo(x2, y1, x2, y1 + arcHeight);
|
||||
gc.LineTo(x2, y2-arcHeight);
|
||||
gc.QuadCurveTo(x2, y2, x2 - arcWidth, y2);
|
||||
gc.LineTo(x1 + arcWidth, y2);
|
||||
gc.QuadCurveTo(x1, y2, x1, y2 - arcHeight);
|
||||
gc.ClosePath()
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Ellipse(cx, cy, rx, ry float) {
|
||||
gc.current.path.ArcTo(cx, cy, rx, ry, 0, -math.Pi * 2)
|
||||
gc.current.path.Close()
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Circle(cx, cy, radius float) {
|
||||
gc.current.path.ArcTo(cx, cy, radius, radius, 0, -math.Pi * 2)
|
||||
gc.current.path.Close()
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer,color image.Color) {
|
||||
painter := raster.NewRGBAPainter(gc.PaintedImage)
|
||||
painter.SetColor(color)
|
||||
rasterizer.Rasterize(painter)
|
||||
rasterizer.Clear()
|
||||
gc.current.path = new(Path)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Stroke(paths ...*Path) {
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
stroker := NewLineStroker(NewVertexMatrixTransform(gc.current.tr, NewVertexRasterizer(gc.strokeRasterizer)))
|
||||
stroker.HalfLineWidth = gc.current.lineWidth / 2
|
||||
|
||||
paths = append(paths, gc.current.path)
|
||||
pathConverter := NewPathConverter(stroker)
|
||||
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||
for _, path := range paths {
|
||||
pathConverter.Convert(path)
|
||||
}
|
||||
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) Fill(paths ...*Path) {
|
||||
filler := NewVertexMatrixTransform(gc.current.tr, NewVertexRasterizer(gc.fillRasterizer))
|
||||
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||
|
||||
paths = append(paths, gc.current.path)
|
||||
pathConverter := NewPathConverter(filler)
|
||||
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||
for _, path := range paths {
|
||||
pathConverter.Convert(path)
|
||||
}
|
||||
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) FillStroke(paths ...*Path) {
|
||||
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
filler := NewVertexMatrixTransform(gc.current.tr, NewVertexRasterizer(gc.fillRasterizer))
|
||||
|
||||
stroker := NewLineStroker(NewVertexMatrixTransform(gc.current.tr, NewVertexRasterizer(gc.strokeRasterizer)))
|
||||
stroker.HalfLineWidth = gc.current.lineWidth / 2
|
||||
|
||||
demux := NewDemuxConverter(filler, stroker)
|
||||
paths = append(paths, gc.current.path)
|
||||
pathConverter := NewPathConverter(demux)
|
||||
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||
for _, path := range paths {
|
||||
pathConverter.Convert(path)
|
||||
}
|
||||
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||
|
||||
}
|
||||
|
||||
func (f FillRule) fillRule() bool {
|
||||
switch f {
|
||||
case FillRuleEvenOdd:
|
||||
return false
|
||||
case FillRuleWinding:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c Cap) capper() raster.Capper {
|
||||
switch c {
|
||||
case RoundCap:
|
||||
return raster.RoundCapper
|
||||
case ButtCap:
|
||||
return raster.ButtCapper
|
||||
case SquareCap:
|
||||
return raster.SquareCapper
|
||||
}
|
||||
return raster.RoundCapper
|
||||
}
|
||||
|
||||
func (j Join) joiner() raster.Joiner {
|
||||
switch j {
|
||||
case RoundJoin:
|
||||
return raster.RoundJoiner
|
||||
case BevelJoin:
|
||||
return raster.BevelJoiner
|
||||
}
|
||||
return raster.RoundJoiner
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package draw2d
|
||||
|
||||
|
||||
type Join int
|
||||
|
||||
const (
|
||||
BevelJoin Join = iota
|
||||
RoundJoin
|
||||
MiterJoin
|
||||
)
|
||||
|
||||
type JoinerFunc func(x1, y1, nx1, ny1, x2, y2, nx2, ny2 float)
|
||||
|
||||
func emptyJoiner(x1, y1, nx1, ny1, x2, y2, nx2, ny2 float) {
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func fabs(x float) float {
|
||||
switch {
|
||||
case x < 0:
|
||||
return -x
|
||||
case x == 0:
|
||||
return 0 // return correctly fabs(-0)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func cos(f float) float {
|
||||
return float(math.Cos(float64(f)))
|
||||
}
|
||||
func sin(f float) float {
|
||||
return float(math.Sin(float64(f)))
|
||||
}
|
||||
func acos(f float) float {
|
||||
return float(math.Acos(float64(f)))
|
||||
}
|
||||
|
||||
func atan2(x, y float) float {
|
||||
return float(math.Atan2(float64(x), float64(y)))
|
||||
}
|
||||
|
||||
func distance(x1, y1, x2, y2 float) float {
|
||||
return vectorDistance(x2 - x1, y2 - y1)
|
||||
}
|
||||
|
||||
func vectorDistance(dx, dy float) float {
|
||||
return float(math.Sqrt(float64(dx*dx + dy*dy)))
|
||||
}
|
||||
|
||||
func squareDistance(x1, y1, x2, y2 float) float {
|
||||
dx := x2 - x1
|
||||
dy := y2 - y1
|
||||
return dx*dx + dy*dy
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type PathCmd byte
|
||||
|
||||
const (
|
||||
NoPathCmd PathCmd = iota
|
||||
MoveTo
|
||||
LineTo
|
||||
QuadCurveTo
|
||||
CubicCurveTo
|
||||
ArcTo
|
||||
Close
|
||||
)
|
||||
|
||||
type Path struct {
|
||||
commands []PathCmd
|
||||
vertices []float
|
||||
x, y float
|
||||
}
|
||||
|
||||
|
||||
func (p *Path) appendToPath(cmd PathCmd, vertices ...float) {
|
||||
p.commands = append(p.commands, cmd)
|
||||
p.vertices = append(p.vertices, vertices...)
|
||||
}
|
||||
|
||||
func (src *Path) Copy() (dest *Path) {
|
||||
dest = new(Path)
|
||||
dest.commands = make([]PathCmd, len(src.commands))
|
||||
copy(dest.commands, src.commands)
|
||||
dest.vertices = make([]float, len(src.vertices))
|
||||
copy(dest.vertices, src.vertices)
|
||||
return dest
|
||||
}
|
||||
|
||||
func (p *Path) LastPoint() (x, y float) {
|
||||
return p.x, p.y
|
||||
}
|
||||
|
||||
func (p *Path) isEmpty() bool {
|
||||
return len(p.commands) == 0
|
||||
}
|
||||
|
||||
func (p *Path) Close() *Path {
|
||||
p.appendToPath(Close)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) MoveTo(x, y float) *Path {
|
||||
p.appendToPath(MoveTo, x, y)
|
||||
p.x = x
|
||||
p.y = y
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) RMoveTo(dx, dy float) *Path {
|
||||
x, y := p.LastPoint()
|
||||
p.MoveTo(x+dx, y+dy)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) LineTo(x, y float) *Path {
|
||||
p.appendToPath(LineTo, x, y)
|
||||
p.x = x
|
||||
p.y = y
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) RLineTo(dx, dy float) *Path {
|
||||
x, y := p.LastPoint()
|
||||
p.LineTo(x+dx, y+dy)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) QuadCurveTo(cx, cy, x, y float) *Path {
|
||||
p.appendToPath(QuadCurveTo, cx, cy, x, y)
|
||||
p.x = x
|
||||
p.y = y
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) RQuadCurveTo(dcx, dcy, dx, dy float) *Path {
|
||||
x, y := p.LastPoint()
|
||||
p.RQuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) *Path {
|
||||
p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y)
|
||||
p.x = x
|
||||
p.y = y
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) *Path {
|
||||
x, y := p.LastPoint()
|
||||
p.RCubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, angle float) *Path {
|
||||
endAngle := startAngle + angle
|
||||
clockWise := true
|
||||
if angle < 0 {
|
||||
clockWise = false
|
||||
}
|
||||
// normalize
|
||||
if clockWise {
|
||||
for endAngle < startAngle {
|
||||
endAngle += math.Pi * 2.0
|
||||
}
|
||||
} else {
|
||||
for startAngle < endAngle {
|
||||
startAngle += math.Pi * 2.0
|
||||
}
|
||||
}
|
||||
startX := cx + cos(startAngle)*rx
|
||||
startY := cy + sin(startAngle)*ry
|
||||
if len(p.commands) > 0 {
|
||||
p.LineTo(startX, startY)
|
||||
} else {
|
||||
p.MoveTo(startX, startY)
|
||||
}
|
||||
p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle)
|
||||
p.x = cx + cos(endAngle)*rx
|
||||
p.y = cy + sin(endAngle)*ry
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *Path {
|
||||
x, y := p.LastPoint()
|
||||
p.RArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Path) String() string {
|
||||
s := ""
|
||||
j := 0
|
||||
for _, cmd := range p.commands {
|
||||
switch cmd {
|
||||
case MoveTo:
|
||||
s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
|
||||
j = j + 2
|
||||
case LineTo:
|
||||
s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
|
||||
j = j + 2
|
||||
case QuadCurveTo:
|
||||
s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3])
|
||||
j = j + 4
|
||||
case CubicCurveTo:
|
||||
s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
|
||||
j = j + 6
|
||||
case ArcTo:
|
||||
s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
|
||||
j = j + 6
|
||||
case Close:
|
||||
s += "Close\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package draw2d
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
type PathConverter struct {
|
||||
converter VertexConverter
|
||||
ApproximationScale, AngleTolerance, CuspLimit float
|
||||
startX, startY, x, y float
|
||||
}
|
||||
|
||||
func NewPathConverter(converter VertexConverter) (*PathConverter) {
|
||||
return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
|
||||
func (c *PathConverter) Convert(path *Path) {
|
||||
j := 0
|
||||
for _, cmd := range path.commands {
|
||||
j = j + c.ConvertCommand(cmd, path.vertices[j:]...)
|
||||
}
|
||||
c.converter.NextCommand(VertexStopCommand)
|
||||
}
|
||||
|
||||
|
||||
func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices... float) int {
|
||||
switch cmd {
|
||||
case MoveTo:
|
||||
c.MoveTo(vertices[0], vertices[1])
|
||||
return 2
|
||||
case LineTo:
|
||||
c.LineTo(vertices[0], vertices[1])
|
||||
return 2
|
||||
case QuadCurveTo:
|
||||
c.QuadCurveTo(vertices[0], vertices[1], vertices[2], vertices[3])
|
||||
return 4
|
||||
case CubicCurveTo:
|
||||
c.CubicCurveTo(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5])
|
||||
return 6
|
||||
case ArcTo:
|
||||
c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale)
|
||||
if(c.startX == c.x && c.startY== c.y) {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
}
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
return 6
|
||||
case Close:
|
||||
c.Close()
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *PathConverter) MoveTo(x, y float) *PathConverter {
|
||||
c.x, c.y = x, y
|
||||
c.startX, c.startY = c.x, c.y
|
||||
c.converter.NextCommand(VertexStopCommand)
|
||||
c.converter.NextCommand(VertexStartCommand)
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) RMoveTo( dx, dy float) *PathConverter {
|
||||
c.MoveTo(c.x+dx, c.y+dy)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) LineTo( x, y float) *PathConverter {
|
||||
c.x, c.y = x, y
|
||||
if(c.startX == c.x && c.startY== c.y) {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
}
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
c.converter.NextCommand(VertexJoinCommand)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) RLineTo( dx, dy float) *PathConverter {
|
||||
c.LineTo(c.x+dx, c.y+dy)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) QuadCurveTo( cx, cy, x, y float) *PathConverter {
|
||||
quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance)
|
||||
c.x, c.y = x, y
|
||||
if(c.startX == c.x && c.startY== c.y) {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
}
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) RQuadCurveTo( dcx, dcy, dx, dy float) *PathConverter {
|
||||
c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) CubicCurveTo( cx1, cy1, cx2, cy2, x, y float) *PathConverter {
|
||||
cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
|
||||
c.x, c.y = x, y
|
||||
if(c.startX == c.x && c.startY== c.y) {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
}
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) RCubicCurveTo( dcx1, dcy1, dcx2, dcy2, dx, dy float) *PathConverter {
|
||||
c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) ArcTo( cx, cy, rx, ry, startAngle, angle float) *PathConverter {
|
||||
endAngle := startAngle + angle
|
||||
clockWise := true
|
||||
if angle < 0 {
|
||||
clockWise = false
|
||||
}
|
||||
// normalize
|
||||
if clockWise {
|
||||
for endAngle < startAngle {
|
||||
endAngle += math.Pi * 2.0
|
||||
}
|
||||
} else {
|
||||
for startAngle < endAngle {
|
||||
startAngle += math.Pi * 2.0
|
||||
}
|
||||
}
|
||||
startX := cx + cos(startAngle)*rx
|
||||
startY := cy + sin(startAngle)*ry
|
||||
c.MoveTo(startX, startY)
|
||||
c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale)
|
||||
if(c.startX == c.x && c.startY== c.y) {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
}
|
||||
c.converter.Vertex(c.x, c.y)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) RArcTo( dcx, dcy, rx, ry, startAngle, angle float) *PathConverter {
|
||||
c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *PathConverter) Close() *PathConverter {
|
||||
c.converter.NextCommand(VertexCloseCommand)
|
||||
c.converter.Vertex(c.startX, c.startY)
|
||||
return c
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package draw2d
|
||||
|
||||
import (
|
||||
"freetype-go.googlecode.com/hg/freetype/raster"
|
||||
)
|
||||
|
||||
type VertexRasterizer struct {
|
||||
rasterizer *raster.Rasterizer
|
||||
command VertexCommand
|
||||
}
|
||||
|
||||
|
||||
func NewVertexRasterizer(rasterizer *raster.Rasterizer) (*VertexRasterizer) {
|
||||
vr := new(VertexRasterizer)
|
||||
vr.rasterizer = rasterizer
|
||||
return vr
|
||||
}
|
||||
|
||||
|
||||
func (vr *VertexRasterizer) NextCommand(command VertexCommand) {
|
||||
vr.command = command
|
||||
}
|
||||
|
||||
func (vr *VertexRasterizer) Vertex(x, y float) {
|
||||
switch vr.command {
|
||||
case VertexStartCommand:
|
||||
vr.rasterizer.Start(floatToPoint(x,y))
|
||||
default:
|
||||
vr.rasterizer.Add1(floatToPoint(x,y))
|
||||
}
|
||||
vr.command = VertexNoCommand
|
||||
}
|
||||
|
||||
func floatToPoint(x, y float) raster.Point {
|
||||
return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package draw2d
|
||||
|
||||
type Cap int
|
||||
|
||||
const (
|
||||
RoundCap Cap = iota
|
||||
ButtCap
|
||||
SquareCap
|
||||
)
|
||||
|
||||
type LineStroker struct {
|
||||
Next VertexConverter
|
||||
HalfLineWidth float
|
||||
Cap Cap
|
||||
Join Join
|
||||
vertices []float
|
||||
rewind []float
|
||||
x, y, nx, ny float
|
||||
command VertexCommand
|
||||
}
|
||||
|
||||
func NewLineStroker(converter VertexConverter) (*LineStroker){
|
||||
l := new(LineStroker)
|
||||
l.Next = converter
|
||||
l.HalfLineWidth = 0.5
|
||||
l.vertices = make([]float, 0)
|
||||
l.rewind = make([]float, 0)
|
||||
l.Cap = ButtCap
|
||||
l.Join = MiterJoin
|
||||
l.command = VertexNoCommand
|
||||
return l
|
||||
}
|
||||
|
||||
|
||||
func (l *LineStroker) NextCommand(command VertexCommand) {
|
||||
l.command = command
|
||||
if(command == VertexStopCommand) {
|
||||
l.Next.NextCommand(VertexStartCommand)
|
||||
for i,j:=0,1; j < len(l.vertices); i,j=i+2,j+2 {
|
||||
l.Next.Vertex(l.vertices[i], l.vertices[j])
|
||||
l.Next.NextCommand(VertexNoCommand)
|
||||
}
|
||||
for i,j:=len(l.rewind) - 2 ,len(l.rewind) - 1; j > 0; i,j=i-2,j-2 {
|
||||
l.Next.NextCommand(VertexNoCommand)
|
||||
l.Next.Vertex(l.rewind[i], l.rewind[j])
|
||||
}
|
||||
if len(l.vertices) > 1 {
|
||||
l.Next.NextCommand(VertexNoCommand)
|
||||
l.Next.Vertex(l.vertices[0] , l.vertices[1])
|
||||
}
|
||||
l.Next.NextCommand(VertexStopCommand)
|
||||
// reinit vertices
|
||||
l.vertices = make([]float, 0)
|
||||
l.rewind = make([]float, 0)
|
||||
l.x, l.y, l.nx, l.ny = 0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LineStroker) Vertex(x, y float) {
|
||||
switch l.command {
|
||||
case VertexNoCommand:
|
||||
l.line(l.x, l.y, x, y)
|
||||
case VertexStartCommand:
|
||||
l.x, l.y = x, y
|
||||
case VertexJoinCommand:
|
||||
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
|
||||
case VertexCloseCommand:
|
||||
l.line(l.x, l.y, x, y)
|
||||
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
|
||||
l.closePolygon()
|
||||
}
|
||||
l.command = VertexNoCommand
|
||||
}
|
||||
|
||||
func (l *LineStroker) closePolygon() {
|
||||
if len(l.vertices) > 1 {
|
||||
l.vertices = append(l.vertices, l.vertices[0] , l.vertices[1])
|
||||
l.rewind = append(l.rewind, l.rewind[0] , l.rewind[1])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (l *LineStroker) line(x1, y1, x2, y2 float) {
|
||||
dx := (x2 - x1)
|
||||
dy := (y2 - y1)
|
||||
d := vectorDistance(dx, dy)
|
||||
if d != 0 {
|
||||
nx := dy * l.HalfLineWidth / d
|
||||
ny := -(dx * l.HalfLineWidth / d)
|
||||
l.vertices = append(l.vertices, x1 + nx, y1 + ny, x2 + nx , y2 + ny)
|
||||
l.rewind = append(l.rewind, x1 - nx, y1 - ny, x2 - nx, y2 - ny)
|
||||
l.x, l.y, l.nx, l.ny = x2 , y2 , nx, ny
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LineStroker) joinLine(x1, y1, nx1, ny1, x2, y2 float) {
|
||||
dx := (x2 - x1)
|
||||
dy := (y2 - y1)
|
||||
d := vectorDistance(dx, dy)
|
||||
|
||||
if(d != 0) {
|
||||
nx := dy * l.HalfLineWidth / d
|
||||
ny := -(dx * l.HalfLineWidth / d)
|
||||
/* l.join(x1, y1, x1 + nx, y1 - ny, nx, ny, x1 + ny2, y1 + nx2, nx2, ny2)
|
||||
l.join(x1, y1, x1 - ny1, y1 - nx1, nx1, ny1, x1 - ny2, y1 - nx2, nx2, ny2)*/
|
||||
|
||||
l.vertices = append(l.vertices, x1 + nx, y1 + ny, x2 + nx , y2 + ny)
|
||||
l.rewind = append(l.rewind, x1 - nx, y1 - ny, x2 - nx, y2 - ny)
|
||||
l.x, l.y, l.nx, l.ny = x2 , y2 ,nx, ny
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void math_stroke<VC>::calc_arc(VC& vc,
|
||||
double x, double y,
|
||||
double dx1, double dy1,
|
||||
double dx2, double dy2)
|
||||
{
|
||||
double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign);
|
||||
double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign);
|
||||
double da = a1 - a2;
|
||||
int i, n;
|
||||
|
||||
da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
|
||||
|
||||
add_vertex(vc, x + dx1, y + dy1);
|
||||
if(m_width_sign > 0)
|
||||
{
|
||||
if(a1 > a2) a2 += 2 * pi;
|
||||
n = int((a2 - a1) / da);
|
||||
da = (a2 - a1) / (n + 1);
|
||||
a1 += da;
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
|
||||
a1 += da;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(a1 < a2) a2 -= 2 * pi;
|
||||
n = int((a1 - a2) / da);
|
||||
da = (a1 - a2) / (n + 1);
|
||||
a1 -= da;
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
|
||||
a1 -= da;
|
||||
}
|
||||
}
|
||||
add_vertex(vc, x + dx2, y + dy2);
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
type MatrixTransform [6]float
|
||||
|
||||
const (
|
||||
epsilon = 1e-6
|
||||
)
|
||||
|
||||
func (tr MatrixTransform) Determinant() float {
|
||||
return tr[0]*tr[3] - tr[1]*tr[2]
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) Transform(points ...*float) {
|
||||
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
|
||||
x := *points[i]
|
||||
y := *points[j]
|
||||
*points[i] = x*tr[0] + y*tr[2] + tr[4]
|
||||
*points[j] = x*tr[1] + y*tr[3] + tr[5]
|
||||
}
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) InverseTransform(points ...*float) {
|
||||
d := tr.Determinant() // matrix determinant
|
||||
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
|
||||
x := *points[i]
|
||||
y := *points[j]
|
||||
*points[i] = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d
|
||||
*points[j] = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d
|
||||
}
|
||||
}
|
||||
|
||||
// ******************** Vector transformations ********************
|
||||
|
||||
func (tr MatrixTransform) VectorTransform(points ...*float) {
|
||||
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
|
||||
x := *points[i]
|
||||
y := *points[j]
|
||||
*points[i] = x*tr[0] + y*tr[2]
|
||||
*points[j] = y*tr[3] + x*tr[1]
|
||||
}
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) VectorTransform2(points ...*float) {
|
||||
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
|
||||
x := *points[i]
|
||||
y := *points[j]
|
||||
*points[i] = x*tr[0] + y*tr[2]
|
||||
*points[j] = y*tr[3] + x*tr[1]
|
||||
}
|
||||
}
|
||||
|
||||
// ******************** Transformations creation ********************
|
||||
|
||||
/** Creates an identity transformation. */
|
||||
func NewIdentityMatrix() MatrixTransform {
|
||||
return [6]float{1, 0, 0, 1, 0, 0}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transformation with a translation, that,
|
||||
* transform point1 into point2.
|
||||
*/
|
||||
func NewTranslationMatrix(tx, ty float) MatrixTransform {
|
||||
return [6]float{1, 0, 0, 1, tx, ty}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transformation with a sx, sy scale factor
|
||||
*/
|
||||
func NewScaleMatrix(sx, sy float) MatrixTransform {
|
||||
return [6]float{sx, 0, 0, sy, 0, 0}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rotation transformation.
|
||||
*/
|
||||
func NewRotationMatrix(angle float) MatrixTransform {
|
||||
c := cos(angle)
|
||||
s := sin(angle)
|
||||
return [6]float{c, s, -s, c, 0, 0}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transformation, combining a scale and a translation, that transform rectangle1 into rectangle2.
|
||||
*/
|
||||
func NewMatrixTransform(rectangle1, rectangle2 [4]float) MatrixTransform {
|
||||
xScale := (rectangle2[2] - rectangle2[0]) / (rectangle1[2] - rectangle1[0])
|
||||
yScale := (rectangle2[3] - rectangle2[1]) / (rectangle1[3] - rectangle1[1])
|
||||
xOffset := rectangle2[0] - (rectangle1[0] * xScale)
|
||||
yOffset := rectangle2[1] - (rectangle1[1] * yScale)
|
||||
return [6]float{xScale, 0, 0, yScale, xOffset, yOffset}
|
||||
}
|
||||
|
||||
// ******************** Transformations operations ********************
|
||||
|
||||
/**
|
||||
* Returns a transformation that is the inverse of the given transformation.
|
||||
*/
|
||||
func (tr MatrixTransform) GetInverseTransformation() MatrixTransform {
|
||||
d := tr.Determinant() // matrix determinant
|
||||
return [6]float{
|
||||
tr[3] / d,
|
||||
-tr[1] / d,
|
||||
-tr[2] / d,
|
||||
tr[0] / d,
|
||||
(tr[2]*tr[5] - tr[3]*tr[4]) / d,
|
||||
(tr[1]*tr[4] - tr[0]*tr[5]) / d}
|
||||
}
|
||||
|
||||
|
||||
func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform {
|
||||
return [6]float{
|
||||
tr1[0]*tr2[0] + tr1[1]*tr2[2],
|
||||
tr1[1]*tr2[3] + tr1[0]*tr2[1],
|
||||
tr1[2]*tr2[0] + tr1[3]*tr2[2],
|
||||
tr1[3]*tr2[3] + tr1[2]*tr2[1],
|
||||
tr1[4]*tr2[0] + tr1[5]*tr2[2] + tr2[4],
|
||||
tr1[5]*tr2[3] + tr1[4]*tr2[1] + tr2[5]}
|
||||
}
|
||||
|
||||
|
||||
func (tr *MatrixTransform) Scale(sx, sy float) (*MatrixTransform){
|
||||
tr[0] = tr[0]*sx;
|
||||
tr[1] = tr[1]*sx;
|
||||
tr[4] = tr[4]*sx;
|
||||
tr[2] = tr[2]*sy;
|
||||
tr[3] = tr[3]*sy;
|
||||
tr[5] = tr[5]*sy;
|
||||
return tr;
|
||||
}
|
||||
|
||||
func (tr *MatrixTransform) Translate(tx, ty float) (*MatrixTransform){
|
||||
tr[4] = tr[4] + tx
|
||||
tr[5] = tr[5] + ty
|
||||
return tr;
|
||||
}
|
||||
|
||||
func (tr *MatrixTransform) Rotate(angle float) (*MatrixTransform){
|
||||
ca := cos(angle);
|
||||
sa := sin(angle);
|
||||
t0 := tr[0] * ca - tr[1] * sa;
|
||||
t2 := tr[1] * ca - tr[3] * sa;
|
||||
t4 := tr[4] * ca - tr[5] * sa;
|
||||
tr[1] = tr[0] * sa + tr[1] * ca;
|
||||
tr[3] = tr[2] * sa + tr[3] * ca;
|
||||
tr[5] = tr[4] * sa + tr[5] * ca;
|
||||
tr[0] = t0;
|
||||
tr[2] = t2;
|
||||
tr[4] = t4;
|
||||
return tr;
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) GetTranslation() (x, y float) {
|
||||
return tr[4], tr[5]
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) GetScaling() (x, y float) {
|
||||
return tr[0], tr[3]
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) GetMaxAbsScaling() (s float) {
|
||||
sx := fabs(tr[0])
|
||||
sy := fabs(tr[3])
|
||||
if(sx > sy) {
|
||||
return sx
|
||||
}
|
||||
return sy
|
||||
}
|
||||
|
||||
func (tr MatrixTransform) GetMinAbsScaling() (s float) {
|
||||
sx := fabs(tr[0])
|
||||
sy := fabs(tr[3])
|
||||
if(sx > sy) {
|
||||
return sy
|
||||
}
|
||||
return sx
|
||||
}
|
||||
|
||||
// ******************** Testing ********************
|
||||
|
||||
/**
|
||||
* Tests if a two transformation are equal. A tolerance is applied when
|
||||
* comparing matrix elements.
|
||||
*/
|
||||
func (tr1 MatrixTransform) Equals(tr2 MatrixTransform) bool {
|
||||
for i := 0; i < 6; i = i + 1 {
|
||||
if !fequals(tr1[i], tr2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a transformation is the identity transformation. A tolerance
|
||||
* is applied when comparing matrix elements.
|
||||
*/
|
||||
func (tr MatrixTransform) IsIdentity() bool {
|
||||
return fequals(tr[4], 0) && fequals(tr[5], 0) && tr.IsTranslation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a transformation is is a pure translation. A tolerance
|
||||
* is applied when comparing matrix elements.
|
||||
*/
|
||||
func (tr MatrixTransform) IsTranslation() bool {
|
||||
return fequals(tr[0], 1) && fequals(tr[1], 0) && fequals(tr[2], 0) && fequals(tr[3], 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two floats.
|
||||
* return true if the distance between the two floats is less than epsilon, false otherwise
|
||||
*/
|
||||
func fequals(float1, float2 float) bool {
|
||||
return fabs(float1-float2) <= epsilon
|
||||
}
|
||||
|
||||
type VertexMatrixTransform struct {
|
||||
tr MatrixTransform
|
||||
Next VertexConverter
|
||||
}
|
||||
|
||||
func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) (*VertexMatrixTransform){
|
||||
return &VertexMatrixTransform{tr, converter}
|
||||
}
|
||||
|
||||
// Vertex Matrix Transform
|
||||
func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) {
|
||||
vmt.Next.NextCommand(command)
|
||||
}
|
||||
|
||||
func (vmt *VertexMatrixTransform) Vertex(x, y float) {
|
||||
vmt.tr.Transform(&x, &y)
|
||||
vmt.Next.Vertex(x, y)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package draw2d
|
||||
|
||||
type VertexCommand byte
|
||||
|
||||
const (
|
||||
VertexNoCommand VertexCommand = iota
|
||||
VertexStartCommand
|
||||
VertexJoinCommand
|
||||
VertexCloseCommand
|
||||
VertexStopCommand
|
||||
)
|
||||
|
||||
type VertexConverter interface {
|
||||
NextCommand(cmd VertexCommand)
|
||||
Vertex(x, y float)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue