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 provides a Graphic Context that can draw vector form on canvas.
|
||||||
package draw2d
|
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
|
// Droid draws a droid at specified position
|
||||||
func Droid(gc draw2d.GraphicContext, x, y float64) {
|
func Droid(drawer draw2d.Drawer, x, y float64, fillStyle draw2d.FillStyle, strokeStyle draw2d.StrokeStyle) {
|
||||||
gc.SetLineCap(draw2d.RoundCap)
|
strokeStyle.LineCap = draw2d.RoundCap
|
||||||
gc.SetLineWidth(5)
|
strokeStyle.Width = 5
|
||||||
|
|
||||||
|
path := &draw2d.Path{}
|
||||||
|
|
||||||
// head
|
// head
|
||||||
gc.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 360*(math.Pi/180))
|
path.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 360*(math.Pi/180))
|
||||||
gc.FillStroke()
|
drawer.Fill(path, fillStyle)
|
||||||
gc.MoveTo(x+60, y+25)
|
drawer.Stroke(path, strokeStyle)
|
||||||
gc.LineTo(x+50, y+10)
|
|
||||||
gc.MoveTo(x+100, y+25)
|
path.Clear()
|
||||||
gc.LineTo(x+110, y+10)
|
path.MoveTo(x+60, y+25)
|
||||||
gc.Stroke()
|
path.LineTo(x+50, y+10)
|
||||||
|
path.MoveTo(x+100, y+25)
|
||||||
|
path.LineTo(x+110, y+10)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// left eye
|
// left eye
|
||||||
Circle(gc, x+60, y+45, 5)
|
path.Clear()
|
||||||
gc.FillStroke()
|
Circle(path, x+60, y+45, 5)
|
||||||
|
drawer.Fill(path, fillStyle)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// right eye
|
// right eye
|
||||||
Circle(gc, x+100, y+45, 5)
|
path.Clear()
|
||||||
gc.FillStroke()
|
Circle(path, x+100, y+45, 5)
|
||||||
|
drawer.Fill(path, fillStyle)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// body
|
// body
|
||||||
RoundedRectangle(gc, x+30, y+75, x+30+100, y+75+90, 10, 10)
|
path.Clear()
|
||||||
gc.FillStroke()
|
RoundedRectangle(path, x+30, y+75, x+30+100, y+75+90, 10, 10)
|
||||||
Rectangle(gc, x+30, y+75, x+30+100, y+75+80)
|
drawer.Fill(path, fillStyle)
|
||||||
gc.FillStroke()
|
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
|
// left arm
|
||||||
RoundedRectangle(gc, x+5, y+80, x+5+20, y+80+70, 10, 10)
|
path.Clear()
|
||||||
gc.FillStroke()
|
RoundedRectangle(path, x+5, y+80, x+5+20, y+80+70, 10, 10)
|
||||||
|
drawer.Fill(path, fillStyle)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// right arm
|
// right arm
|
||||||
RoundedRectangle(gc, x+135, y+80, x+135+20, y+80+70, 10, 10)
|
path.Clear()
|
||||||
gc.FillStroke()
|
RoundedRectangle(path, x+135, y+80, x+135+20, y+80+70, 10, 10)
|
||||||
|
drawer.Fill(path, fillStyle)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// left leg
|
// left leg
|
||||||
RoundedRectangle(gc, x+50, y+150, x+50+20, y+150+50, 10, 10)
|
path.Clear()
|
||||||
gc.FillStroke()
|
RoundedRectangle(path, x+50, y+150, x+50+20, y+150+50, 10, 10)
|
||||||
|
drawer.Fill(path, fillStyle)
|
||||||
|
drawer.Stroke(path, strokeStyle)
|
||||||
|
|
||||||
// right leg
|
// right leg
|
||||||
RoundedRectangle(gc, x+90, y+150, x+90+20, y+150+50, 10, 10)
|
path.Clear()
|
||||||
gc.FillStroke()
|
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