Start implementing drawer
This commit is contained in:
parent
72643a28b2
commit
f2563306e4
5 changed files with 277 additions and 406 deletions
150
draw2d.go
150
draw2d.go
|
@ -3,3 +3,153 @@
|
|||
|
||||
// Package draw2d provides a Graphic Context that can draw vector form on canvas.
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// FillRule defines the fill rule used when fill
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
// FillRuleEvenOdd determines the "insideness" of a point in the shape
|
||||
// by drawing a ray from that point to infinity in any direction
|
||||
// and counting the number of path segments from the given shape that the ray crosses.
|
||||
// If this number is odd, the point is inside; if even, the point is outside.
|
||||
FillRuleEvenOdd FillRule = iota
|
||||
// FillRuleWinding determines the "insideness" of a point in the shape
|
||||
// by drawing a ray from that point to infinity in any direction
|
||||
// and then examining the places where a segment of the shape crosses the ray.
|
||||
// Starting with a count of zero, add one each time a path segment crosses
|
||||
// the ray from left to right and subtract one each time
|
||||
// a path segment crosses the ray from right to left. After counting the crossings,
|
||||
// if the result is zero then the point is outside the path. Otherwise, it is inside.
|
||||
FillRuleWinding
|
||||
)
|
||||
|
||||
// LineCap is the style of line extremities
|
||||
type LineCap int
|
||||
|
||||
const (
|
||||
// RoundCap defines a rounded shape at the end of the line
|
||||
RoundCap LineCap = iota
|
||||
// ButtCap defines a squared shape exactly at the end of the line
|
||||
ButtCap
|
||||
// SquareCap defines a squared shape at the end of the line
|
||||
SquareCap
|
||||
)
|
||||
|
||||
// LineJoin is the style of segments joint
|
||||
type LineJoin int
|
||||
|
||||
const (
|
||||
// BevelJoin represents cut segments joint
|
||||
BevelJoin LineJoin = iota
|
||||
// RoundJoin represents rounded segments joint
|
||||
RoundJoin
|
||||
// MiterJoin represents peaker segments joint
|
||||
MiterJoin
|
||||
)
|
||||
|
||||
// StrokeStyle keeps stroke style attributes
|
||||
// that is used by the Stroke method of a Drawer
|
||||
type StrokeStyle struct {
|
||||
// Color defines the color of stroke
|
||||
Color color.Color
|
||||
// Line width
|
||||
Width float64
|
||||
// Line cap style rounded, butt or square
|
||||
LineCap LineCap
|
||||
// Line join style bevel, round or miter
|
||||
LineJoin LineJoin
|
||||
// offset of the first dash
|
||||
DashOffset float64
|
||||
// array represented dash length pair values are plain dash and impair are space between dash
|
||||
// if empty display plain line
|
||||
Dash []float64
|
||||
}
|
||||
|
||||
type FillStyle interface {
|
||||
}
|
||||
|
||||
// SolidFillStyle define style attributes for a solid fill style
|
||||
type SolidFillStyle struct {
|
||||
// Color defines the line color
|
||||
Color color.Color
|
||||
// FillRule defines the file rule to used
|
||||
FillRule FillRule
|
||||
}
|
||||
|
||||
// Vertical Alignment of the text
|
||||
type Valign int
|
||||
|
||||
const (
|
||||
ValignTop Valign = iota
|
||||
ValignTopCenter
|
||||
ValignTopBottom
|
||||
ValignTopBaseline
|
||||
)
|
||||
|
||||
// Horizontal Alignment of the text
|
||||
type Halign int
|
||||
|
||||
const (
|
||||
HalignLeft = iota
|
||||
HalignCenter
|
||||
HalignRight
|
||||
)
|
||||
|
||||
// TextStyle
|
||||
type TextStyle struct {
|
||||
// Color defines the color of text
|
||||
Color color.Color
|
||||
// Size font size
|
||||
Size float64
|
||||
// The font to use
|
||||
Font FontData
|
||||
// Horizontal Alignment of the text
|
||||
Halign Halign
|
||||
// Vertical Alignment of the text
|
||||
Valign Valign
|
||||
}
|
||||
|
||||
type ScalingPolicy int
|
||||
|
||||
const (
|
||||
// ScalingNone no scaling applied
|
||||
ScalingNone ScalingPolicy = iota
|
||||
// ScalingStretch the image is stretched so that its width and height are exactly the given width and height
|
||||
ScalingStretch
|
||||
// ScalingWidth the image is scaled so that its width is exactly the given width
|
||||
ScalingWidth
|
||||
// ScalingHeight the image is scaled so that its height is exactly the given height
|
||||
ScalingHeight
|
||||
// ScalingFit the image is scaled to the largest scale that allow the image to fit within a rectangle width x height
|
||||
ScalingFit
|
||||
// ScalingSameArea the image is scaled so that its area is exactly the area of the given rectangle width x height
|
||||
ScalingSameArea
|
||||
// ScalingFill the image is scaled to the smallest scale that allow the image to fully cover a rectangle width x height
|
||||
ScalingFill
|
||||
)
|
||||
|
||||
// ImageScaling style attributes used to display the image
|
||||
type ImageScaling struct {
|
||||
// Horizontal Alignment of the image
|
||||
Halign Halign
|
||||
// Vertical Alignment of the image
|
||||
Valign Valign
|
||||
// Width Height used by scaling policy
|
||||
Width, Height float64
|
||||
// ScalingPolicy defines the scaling policy to applied to the image
|
||||
ScalingPolicy ScalingPolicy
|
||||
}
|
||||
|
||||
// Drawer can fill and stroke a path
|
||||
type Drawer interface {
|
||||
Matrix() *Matrix
|
||||
Fill(path *Path, style FillStyle)
|
||||
Stroke(path *Path, style StrokeStyle)
|
||||
Text(text string, x, y float64, style TextStyle)
|
||||
Image(image image.Image, x, y float64, scaling ImageScaling)
|
||||
}
|
||||
|
|
79
draw2dimg/drawer.go
Normal file
79
draw2dimg/drawer.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package draw2dimg
|
||||
|
||||
import (
|
||||
"code.google.com/p/freetype-go/freetype/raster"
|
||||
"code.google.com/p/freetype-go/freetype/truetype"
|
||||
"github.com/llgcode/draw2d"
|
||||
"github.com/llgcode/draw2d/draw2dbase"
|
||||
"image"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
type Drawer struct {
|
||||
matrix draw2d.Matrix
|
||||
img draw.Image
|
||||
painter Painter
|
||||
fillRasterizer *raster.Rasterizer
|
||||
strokeRasterizer *raster.Rasterizer
|
||||
glyphBuf *truetype.GlyphBuf
|
||||
}
|
||||
|
||||
func NewDrawer(img *image.RGBA) *Drawer {
|
||||
width, height := img.Bounds().Dx(), img.Bounds().Dy()
|
||||
return &Drawer{
|
||||
draw2d.NewIdentityMatrix(),
|
||||
img,
|
||||
raster.NewRGBAPainter(img),
|
||||
raster.NewRasterizer(width, height),
|
||||
raster.NewRasterizer(width, height),
|
||||
truetype.NewGlyphBuf(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Drawer) Matrix() *draw2d.Matrix {
|
||||
return d.Matrix()
|
||||
}
|
||||
|
||||
func (d *Drawer) Fill(path *draw2d.Path, style draw2d.FillStyle) {
|
||||
switch fillStyle := style.(type) {
|
||||
case draw2d.SolidFillStyle:
|
||||
d.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(fillStyle.FillRule)
|
||||
d.painter.SetColor(fillStyle.Color)
|
||||
default:
|
||||
panic("FillStyle not supported")
|
||||
}
|
||||
|
||||
flattener := draw2dbase.Transformer{d.matrix, draw2dbase.FtLineBuilder{d.fillRasterizer}}
|
||||
|
||||
draw2dbase.Flatten(path, flattener, d.matrix.GetScale())
|
||||
|
||||
d.fillRasterizer.Rasterize(d.painter)
|
||||
d.fillRasterizer.Clear()
|
||||
}
|
||||
|
||||
func (d *Drawer) Stroke(path *draw2d.Path, style draw2d.StrokeStyle) {
|
||||
d.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
stroker := draw2dbase.NewLineStroker(style.LineCap, style.LineJoin, draw2dbase.Transformer{d.matrix, draw2dbase.FtLineBuilder{d.strokeRasterizer}})
|
||||
stroker.HalfLineWidth = style.Width / 2
|
||||
|
||||
var liner draw2dbase.Flattener
|
||||
if style.Dash != nil && len(style.Dash) > 0 {
|
||||
liner = draw2dbase.NewDashConverter(style.Dash, style.DashOffset, stroker)
|
||||
} else {
|
||||
liner = stroker
|
||||
}
|
||||
|
||||
draw2dbase.Flatten(path, liner, d.matrix.GetScale())
|
||||
|
||||
d.painter.SetColor(style.Color)
|
||||
d.strokeRasterizer.Rasterize(d.painter)
|
||||
d.strokeRasterizer.Clear()
|
||||
}
|
||||
|
||||
func (d *Drawer) Text(text string, x, y float64, style draw2d.TextStyle) {
|
||||
|
||||
}
|
||||
|
||||
func (d *Drawer) Image(image image.Image, x, y float64, scaling draw2d.ImageScaling) {
|
||||
}
|
|
@ -6,46 +6,68 @@ import (
|
|||
)
|
||||
|
||||
// Droid draws a droid at specified position
|
||||
func Droid(gc draw2d.GraphicContext, x, y float64) {
|
||||
gc.SetLineCap(draw2d.RoundCap)
|
||||
gc.SetLineWidth(5)
|
||||
func Droid(drawer draw2d.Drawer, x, y float64, fillStyle draw2d.FillStyle, strokeStyle draw2d.StrokeStyle) {
|
||||
strokeStyle.LineCap = draw2d.RoundCap
|
||||
strokeStyle.Width = 5
|
||||
|
||||
path := &draw2d.Path{}
|
||||
|
||||
// head
|
||||
gc.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 360*(math.Pi/180))
|
||||
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()
|
||||
path.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 360*(math.Pi/180))
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
path.Clear()
|
||||
path.MoveTo(x+60, y+25)
|
||||
path.LineTo(x+50, y+10)
|
||||
path.MoveTo(x+100, y+25)
|
||||
path.LineTo(x+110, y+10)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// left eye
|
||||
Circle(gc, x+60, y+45, 5)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
Circle(path, x+60, y+45, 5)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// right eye
|
||||
Circle(gc, x+100, y+45, 5)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
Circle(path, x+100, y+45, 5)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// body
|
||||
RoundedRectangle(gc, x+30, y+75, x+30+100, y+75+90, 10, 10)
|
||||
gc.FillStroke()
|
||||
Rectangle(gc, x+30, y+75, x+30+100, y+75+80)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
RoundedRectangle(path, x+30, y+75, x+30+100, y+75+90, 10, 10)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
path.Clear()
|
||||
Rectangle(path, x+30, y+75, x+30+100, y+75+80)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// left arm
|
||||
RoundedRectangle(gc, x+5, y+80, x+5+20, y+80+70, 10, 10)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
RoundedRectangle(path, x+5, y+80, x+5+20, y+80+70, 10, 10)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// right arm
|
||||
RoundedRectangle(gc, x+135, y+80, x+135+20, y+80+70, 10, 10)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
RoundedRectangle(path, x+135, y+80, x+135+20, y+80+70, 10, 10)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// left leg
|
||||
RoundedRectangle(gc, x+50, y+150, x+50+20, y+150+50, 10, 10)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
RoundedRectangle(path, x+50, y+150, x+50+20, y+150+50, 10, 10)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
|
||||
// right leg
|
||||
RoundedRectangle(gc, x+90, y+150, x+90+20, y+150+50, 10, 10)
|
||||
gc.FillStroke()
|
||||
path.Clear()
|
||||
RoundedRectangle(path, x+90, y+150, x+90+20, y+150+50, 10, 10)
|
||||
drawer.Fill(path, fillStyle)
|
||||
drawer.Stroke(path, strokeStyle)
|
||||
}
|
||||
|
|
162
graphics.go
162
graphics.go
|
@ -1,162 +0,0 @@
|
|||
package draw2d
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// FillRule defines the fill rule used when fill
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
// FillRuleEvenOdd determines the "insideness" of a point in the shape
|
||||
// by drawing a ray from that point to infinity in any direction
|
||||
// and counting the number of path segments from the given shape that the ray crosses.
|
||||
// If this number is odd, the point is inside; if even, the point is outside.
|
||||
FillRuleEvenOdd FillRule = iota
|
||||
// FillRuleWinding determines the "insideness" of a point in the shape
|
||||
// by drawing a ray from that point to infinity in any direction
|
||||
// and then examining the places where a segment of the shape crosses the ray.
|
||||
// Starting with a count of zero, add one each time a path segment crosses
|
||||
// the ray from left to right and subtract one each time
|
||||
// a path segment crosses the ray from right to left. After counting the crossings,
|
||||
// if the result is zero then the point is outside the path. Otherwise, it is inside.
|
||||
FillRuleWinding
|
||||
)
|
||||
|
||||
// LineCap is the style of line extremities
|
||||
type LineCap int
|
||||
|
||||
const (
|
||||
// RoundCap defines a rounded shape at the end of the line
|
||||
RoundCap LineCap = iota
|
||||
// ButtCap defines a squared shape exactly at the end of the line
|
||||
ButtCap
|
||||
// SquareCap defines a squared shape at the end of the line
|
||||
SquareCap
|
||||
)
|
||||
|
||||
// LineJoin is the style of segments joint
|
||||
type LineJoin int
|
||||
|
||||
const (
|
||||
// BevelJoin represents cut segments joint
|
||||
BevelJoin LineJoin = iota
|
||||
// RoundJoin represents rounded segments joint
|
||||
RoundJoin
|
||||
// MiterJoin represents peaker segments joint
|
||||
MiterJoin
|
||||
)
|
||||
|
||||
// StrokeStyle keeps stroke style attributes
|
||||
// that is used by the Stroke method of a Drawer
|
||||
type StrokeStyle struct {
|
||||
// Color defines the color of stroke
|
||||
Color color.Color
|
||||
// Line width
|
||||
Width float64
|
||||
// Line cap style rounded, butt or square
|
||||
LineCap LineCap
|
||||
// Line join style bevel, round or miter
|
||||
LineJoin LineJoin
|
||||
// offset of the first dash
|
||||
dashOffset float64
|
||||
// array represented dash length pair values are plain dash and impair are space between dash
|
||||
// if empty display plain line
|
||||
dash []float64
|
||||
}
|
||||
|
||||
// FillStyle
|
||||
type FillStyle struct {
|
||||
}
|
||||
|
||||
// SolidFillStyle define style attributes for a solid fill style
|
||||
type SolidFillStyle struct {
|
||||
FillStyle
|
||||
// Color defines the line color
|
||||
Color color.Color
|
||||
// FillRule defines the file rule to used
|
||||
FillRule FillRule
|
||||
}
|
||||
|
||||
// Vertical Alignment of the text
|
||||
type Valign int
|
||||
|
||||
const (
|
||||
ValignTop Valign = iota
|
||||
ValignTopCenter
|
||||
ValignTopBottom
|
||||
ValignTopBaseline
|
||||
)
|
||||
|
||||
// Horizontal Alignment of the text
|
||||
type Halign int
|
||||
|
||||
const (
|
||||
HalignLeft = iota
|
||||
HalignCenter
|
||||
HalignRight
|
||||
)
|
||||
|
||||
type ScalingPolicy int
|
||||
|
||||
const (
|
||||
// ScalingNone no scaling applied
|
||||
ScalingNone ScalingPolicy = iota
|
||||
// ScalingStretch the image is stretched so that its width and height are exactly the given width and height
|
||||
ScalingStretch
|
||||
// ScalingWidth the image is scaled so that its width is exactly the given width
|
||||
ScalingWidth
|
||||
// ScalingHeight the image is scaled so that its height is exactly the given height
|
||||
ScalingHeight
|
||||
// ScalingFit the image is scaled to the largest scale that allow the image to fit within a rectangle width x height
|
||||
ScalingFit
|
||||
// ScalingSameArea the image is scaled so that its area is exactly the area of the given rectangle width x height
|
||||
ScalingSameArea
|
||||
// ScalingFill the image is scaled to the smallest scale that allow the image to fully cover a rectangle width x height
|
||||
ScalingFill
|
||||
)
|
||||
|
||||
// TextStyle
|
||||
type TextStyle struct {
|
||||
// Color defines the color of text
|
||||
Color color.Color
|
||||
// Size font size
|
||||
Size float64
|
||||
// The font to use
|
||||
Font FontData
|
||||
// Horizontal Alignment of the text
|
||||
Halign Halign
|
||||
// Vertical Alignment of the text
|
||||
Valign Valign
|
||||
}
|
||||
|
||||
// ImageStyle style attributes used to display the image
|
||||
type ImageStyle struct {
|
||||
// Horizontal Alignment of the image
|
||||
Halign Halign
|
||||
// Vertical Alignment of the image
|
||||
Valign Valign
|
||||
// Width Height used by scaling policy
|
||||
Width, Height float64
|
||||
// ScalingPolicy defines the scaling policy to applied to the image
|
||||
ScalingPolicy ScalingPolicy
|
||||
}
|
||||
|
||||
// Style defines properties that
|
||||
type Style struct {
|
||||
Matrix Matrix
|
||||
StrokeStyle StrokeStyle
|
||||
FillStyle FillStyle
|
||||
TextStyle TextStyle
|
||||
ImageStyle TextStyle
|
||||
}
|
||||
|
||||
// Drawer can fill and stroke a path
|
||||
type Drawer interface {
|
||||
Style() *Style
|
||||
Fill(Path)
|
||||
Stroke(Path)
|
||||
Text(text string, x, y float64)
|
||||
Image(image image.Image, x, y float64)
|
||||
}
|
218
transform.go
218
transform.go
|
@ -1,218 +0,0 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 21/11/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
type MatrixTransform [6]float64
|
||||
|
||||
const (
|
||||
epsilon = 1e-6
|
||||
)
|
||||
|
||||
// Determinant compute the determinant of the matrix
|
||||
func (tr MatrixTransform) Determinant() float64 {
|
||||
return tr[0]*tr[3] - tr[1]*tr[2]
|
||||
}
|
||||
|
||||
// Transform applies the transformation matrix to points. It modify the points passed in parameter.
|
||||
func (tr MatrixTransform) Transform(points []float64) {
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
// TransformPoint applies the transformation matrix to point. It returns the point the transformed point.
|
||||
func (tr MatrixTransform) TransformPoint(x, y float64) (xres, yres float64) {
|
||||
xres = x*tr[0] + y*tr[2] + tr[4]
|
||||
yres = x*tr[1] + y*tr[3] + tr[5]
|
||||
return xres, yres
|
||||
}
|
||||
|
||||
func minMax(x, y float64) (min, max float64) {
|
||||
if x > y {
|
||||
return y, x
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
// Transform applies the transformation matrix to the rectangle represented by the min and the max point of the rectangle
|
||||
func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 float64) (nx0, ny0, nx2, ny2 float64) {
|
||||
points := []float64{x0, y0, x2, y0, x2, y2, x0, y2}
|
||||
tr.Transform(points)
|
||||
points[0], points[2] = minMax(points[0], points[2])
|
||||
points[4], points[6] = minMax(points[4], points[6])
|
||||
points[1], points[3] = minMax(points[1], points[3])
|
||||
points[5], points[7] = minMax(points[5], points[7])
|
||||
|
||||
nx0 = math.Min(points[0], points[4])
|
||||
ny0 = math.Min(points[1], points[5])
|
||||
nx2 = math.Max(points[2], points[6])
|
||||
ny2 = math.Max(points[3], points[7])
|
||||
return nx0, ny0, nx2, ny2
|
||||
}
|
||||
|
||||
// InverseTransform applies the transformation inverse matrix to the rectangle represented by the min and the max point of the rectangle
|
||||
func (tr MatrixTransform) InverseTransform(points []float64) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// InverseTransformPoint applies the transformation inverse matrix to point. It returns the point the transformed point.
|
||||
func (tr MatrixTransform) InverseTransformPoint(x, y float64) (xres, yres float64) {
|
||||
d := tr.Determinant() // matrix determinant
|
||||
xres = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d
|
||||
yres = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d
|
||||
return xres, yres
|
||||
}
|
||||
|
||||
// VectorTransform applies the transformation matrix to points without using the translation parameter of the affine matrix.
|
||||
// It modify the points passed in parameter.
|
||||
func (tr MatrixTransform) VectorTransform(points []float64) {
|
||||
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] = x*tr[1] + y*tr[3]
|
||||
}
|
||||
}
|
||||
|
||||
// NewIdentityMatrix creates an identity transformation matrix.
|
||||
func NewIdentityMatrix() MatrixTransform {
|
||||
return [6]float64{1, 0, 0, 1, 0, 0}
|
||||
}
|
||||
|
||||
// NewTranslationMatrix creates a transformation matrix with a translation tx and ty translation parameter
|
||||
func NewTranslationMatrix(tx, ty float64) MatrixTransform {
|
||||
return [6]float64{1, 0, 0, 1, tx, ty}
|
||||
}
|
||||
|
||||
// NewScaleMatrix creates a transformation matrix with a sx, sy scale factor
|
||||
func NewScaleMatrix(sx, sy float64) MatrixTransform {
|
||||
return [6]float64{sx, 0, 0, sy, 0, 0}
|
||||
}
|
||||
|
||||
// NewRotationMatrix creates a rotation transformation matrix. angle is in radian
|
||||
func NewRotationMatrix(angle float64) MatrixTransform {
|
||||
c := math.Cos(angle)
|
||||
s := math.Sin(angle)
|
||||
return [6]float64{c, s, -s, c, 0, 0}
|
||||
}
|
||||
|
||||
// NewMatrixTransform creates a transformation matrix, combining a scale and a translation, that transform rectangle1 into rectangle2.
|
||||
func NewMatrixFromRects(rectangle1, rectangle2 [4]float64) 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]float64{xScale, 0, 0, yScale, xOffset, yOffset}
|
||||
}
|
||||
|
||||
// Inverse returns a matrix that is the inverse of the given matrix.
|
||||
func (tr MatrixTransform) Inverse() MatrixTransform {
|
||||
d := tr.Determinant() // matrix determinant
|
||||
return [6]float64{
|
||||
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}
|
||||
}
|
||||
|
||||
// Multiply composes Matrix tr1 with tr2 returns the resulting matrix
|
||||
func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform {
|
||||
return [6]float64{
|
||||
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]}
|
||||
}
|
||||
|
||||
// Scale adds a scale to the matrix
|
||||
func (tr *MatrixTransform) Scale(sx, sy float64) *MatrixTransform {
|
||||
tr[0] = sx * tr[0]
|
||||
tr[1] = sx * tr[1]
|
||||
tr[2] = sy * tr[2]
|
||||
tr[3] = sy * tr[3]
|
||||
return tr
|
||||
}
|
||||
|
||||
// Translate adds a translation to the matrix
|
||||
func (tr *MatrixTransform) Translate(tx, ty float64) *MatrixTransform {
|
||||
tr[4] = tx*tr[0] + ty*tr[2] + tr[4]
|
||||
tr[5] = ty*tr[3] + tx*tr[1] + tr[5]
|
||||
return tr
|
||||
}
|
||||
|
||||
// Rotate adds a rotation to the matrix. angle is in radian
|
||||
func (tr *MatrixTransform) Rotate(angle float64) *MatrixTransform {
|
||||
c := math.Cos(angle)
|
||||
s := math.Sin(angle)
|
||||
t0 := c*tr[0] + s*tr[2]
|
||||
t1 := s*tr[3] + c*tr[1]
|
||||
t2 := c*tr[2] - s*tr[0]
|
||||
t3 := c*tr[3] - s*tr[1]
|
||||
tr[0] = t0
|
||||
tr[1] = t1
|
||||
tr[2] = t2
|
||||
tr[3] = t3
|
||||
return tr
|
||||
}
|
||||
|
||||
// GetTranslation
|
||||
func (tr MatrixTransform) GetTranslation() (x, y float64) {
|
||||
return tr[4], tr[5]
|
||||
}
|
||||
|
||||
// GetScaling
|
||||
func (tr MatrixTransform) GetScaling() (x, y float64) {
|
||||
return tr[0], tr[3]
|
||||
}
|
||||
|
||||
// GetScale computes the scale of the matrix
|
||||
func (tr MatrixTransform) GetScale() float64 {
|
||||
x := 0.707106781*tr[0] + 0.707106781*tr[1]
|
||||
y := 0.707106781*tr[2] + 0.707106781*tr[3]
|
||||
return math.Sqrt(x*x + y*y)
|
||||
}
|
||||
|
||||
// ******************** Testing ********************
|
||||
|
||||
// Equals 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
|
||||
}
|
||||
|
||||
// IsIdentity 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()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// fequals compares two floats. return true if the distance between the two floats is less than epsilon, false otherwise
|
||||
func fequals(float1, float2 float64) bool {
|
||||
return math.Abs(float1-float2) <= epsilon
|
||||
}
|
Loading…
Reference in a new issue