From aec12d2759a80b561e047cef0340dd08001a495e Mon Sep 17 00:00:00 2001 From: "legoff.laurent" Date: Thu, 25 Nov 2010 15:21:38 +0000 Subject: [PATCH] Begin to work on matrix transformation (does not yet really work) --- draw2d/src/pkg/draw2d/draw2d.go | 52 +++++++- draw2d/src/pkg/draw2d/transform.go | 204 +++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 draw2d/src/pkg/draw2d/transform.go diff --git a/draw2d/src/pkg/draw2d/draw2d.go b/draw2d/src/pkg/draw2d/draw2d.go index 03d9965..192c4c6 100644 --- a/draw2d/src/pkg/draw2d/draw2d.go +++ b/draw2d/src/pkg/draw2d/draw2d.go @@ -39,6 +39,7 @@ type GraphicContext struct { } type contextStack struct { + tr MatrixTransform path *Path lineWidth float dash []float @@ -48,7 +49,6 @@ type contextStack struct { fillRule FillRule cap Cap join Join - previous *contextStack } @@ -62,16 +62,50 @@ func NewGraphicContext(pi *image.RGBA) *GraphicContext { gc.rasterizer = 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 - gc.current.path = new(Path) return gc } +func (gc *GraphicContext) SetMatrixTransform(tr MatrixTransform) { + gc.current.tr = tr +} + +func (gc *GraphicContext) ComposeMatrixTransform(tr MatrixTransform) { + gc.current.tr.Compose(tr) +} + +func (gc *GraphicContext) Rotate(angle float) { + ox, oy := gc.current.path.LastPoint() + tr := NewTranslationMatrix(ox, oy) + tr2 := NewRotationMatrix(angle) + tr1 := tr.GetInverseTransformation() + gc.current.tr.Compose(tr).Compose(tr2).Compose(tr1) +} + +func (gc *GraphicContext) Translate(tx, ty float) { + ox, oy := gc.current.path.LastPoint() + tr := NewTranslationMatrix(ox, oy) + tr2 := NewTranslationMatrix(tx, ty) + tr1 := tr.GetInverseTransformation() + gc.current.tr.Compose(tr).Compose(tr2).Compose(tr1) +} + +func (gc *GraphicContext) Scale(sx, sy float) { + ox, oy := gc.current.path.LastPoint() + tr := NewTranslationMatrix(ox, oy) + tr2 := NewScaleMatrix(sx, sy) + tr1 := tr.GetInverseTransformation() + gc.current.tr.Compose(tr).Compose(tr2).Compose(tr1) +} + func (gc *GraphicContext) Clear() { width, height := gc.PaintedImage.Bounds().Dx(), gc.PaintedImage.Bounds().Dy() gc.ClearRect(0, 0, width, height) @@ -139,50 +173,64 @@ func (gc *GraphicContext) BeginPath() { } func (gc *GraphicContext) MoveTo(x, y float) { + gc.current.tr.Transform(&x, &y) gc.current.path.MoveTo(x, y) } func (gc *GraphicContext) RMoveTo(dx, dy float) { + gc.current.tr.VectorTransform(&dx, &dy) gc.current.path.RMoveTo(dx, dy) } func (gc *GraphicContext) LineTo(x, y float) { + gc.current.tr.Transform(&x, &y) gc.current.path.LineTo(x, y) } func (gc *GraphicContext) RLineTo(dx, dy float) { + gc.current.tr.VectorTransform(&dx, &dy) gc.current.path.RLineTo(dx, dy) } func (gc *GraphicContext) Rect(x1, y1, x2, y2 float) { + gc.current.tr.Transform(&x1, &y1, &x2, &y2) gc.current.path.Rect(x1, y1, x2, y2) } func (gc *GraphicContext) RRect(dx1, dy1, dx2, dy2 float) { + gc.current.tr.VectorTransform(&dx1, &dy1, &dx2, &dy2) gc.current.path.RRect(dx1, dy1, dx2, dy2) } func (gc *GraphicContext) QuadCurveTo(cx, cy, x, y float) { + gc.current.tr.Transform(&cx, &cy, &x, &y) gc.current.path.QuadCurveTo(cx, cy, x, y) } func (gc *GraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float) { + gc.current.tr.VectorTransform(&dcx, &dcy, &dx, &dy) gc.current.path.RQuadCurveTo(dcx, dcy, dx, dy) } func (gc *GraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) { + gc.current.tr.Transform(&cx1, &cy1, &cx2, &cy2, &x, &y) gc.current.path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) } func (gc *GraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) { + gc.current.tr.VectorTransform(&dcx1, &dcy1, &dcx2, &dcy2, &dx, &dy) gc.current.path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy) } func (gc *GraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float) { + gc.current.tr.Transform(&cx, &cy) + gc.current.tr.VectorTransform(&rx, &ry) gc.current.path.ArcTo(cx, cy, rx, ry, startAngle, angle) } func (gc *GraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) { + gc.current.tr.VectorTransform(&dcx, &dcy) + gc.current.tr.VectorTransform(&rx, &ry) gc.current.path.RArcTo(dcx, dcy, rx, ry, startAngle, angle) } diff --git a/draw2d/src/pkg/draw2d/transform.go b/draw2d/src/pkg/draw2d/transform.go new file mode 100644 index 0000000..4bc46b4 --- /dev/null +++ b/draw2d/src/pkg/draw2d/transform.go @@ -0,0 +1,204 @@ +// 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) TransformX(x, y float) float { + return x*tr[0] + y*tr[2] + tr[4] +} + +func (tr MatrixTransform) TransformY(x, y float) float { + return x*tr[1] + y*tr[3] + tr[5] +} + +func (tr MatrixTransform) Determinant() float { + return tr[0]*tr[3] - tr[1]*tr[2] +} + +func (tr MatrixTransform) InverseTransformX(x, y float) float { + return ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / tr.Determinant() +} + +func (tr MatrixTransform) InverseTransformY(x, y float) float { + return ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / tr.Determinant() +} + +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] = tr.TransformX(x, y) + *points[j] = tr.TransformY(x, y) + } +} + + +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) VectorTransformX(x, y float) float { + return x*tr[0] + y*tr[2] +} + +func (tr MatrixTransform) VectorTransformY(x, y float) float { + return x*tr[1] + y*tr[3] +} + +func (tr MatrixTransform) VectorInverseTransformX(x, y float) float { + d := tr.Determinant() // matrix determinant + return (x*tr[3] - y*tr[2]) / d +} + +func (tr MatrixTransform) VectorInverseTransformY(x, y float) float { + d := tr.Determinant() // matrix determinant + return (y*tr[0] - x*tr[1]) / d +} + +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] = tr.VectorTransformX(x, y) + *points[j] = tr.VectorTransformY(x, y) + } +} + +// ******************** 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 [4]float, 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} +} + +/** + * Returns a transformation that is the composition (tr2 o tr1) of the given + * transformations tr2 and tr1. + + * For given point (x, y), the composed transformation is defined by the + * equation: + * (tr2 o tr1)(x, y) = tr2(tr1(x, y)) + */ +func (tr1 MatrixTransform) GetComposedTransformation(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 (tr1 *MatrixTransform) Compose(tr2 MatrixTransform) (*MatrixTransform){ + tr1[0] = tr2[0]*tr1[0] + tr2[1]*tr1[2] + tr1[1] = tr2[1]*tr1[3] + tr2[0]*tr1[1] + tr1[2] = tr2[2]*tr1[0] + tr2[3]*tr1[2] + tr1[3] = tr2[3]*tr1[3] + tr2[2]*tr1[1] + tr1[4] = tr2[4]*tr1[0] + tr2[5]*tr1[2] + tr1[4] + tr1[5] = tr2[5]*tr1[3] + tr2[4]*tr1[1] + tr1[5] + return tr1 +} + +// ******************** 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 +}