mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-01 22:17:29 +08:00
Moved point.go and matrix.go back to their original locations.
This commit is contained in:
parent
8c3aa80d9c
commit
f566fe5f68
@ -87,11 +87,6 @@ func (m *Matrix) Translation() (float64, float64) {
|
|||||||
return m[6], m[7]
|
return m[6], m[7]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translation returns the translation part of `m`.
|
|
||||||
func (m *Matrix) ScalingX() float64 {
|
|
||||||
return math.Hypot(m[0], m[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform returns coordinates `x`,`y` transformed by `m`.
|
// Transform returns coordinates `x`,`y` transformed by `m`.
|
||||||
func (m *Matrix) Transform(x, y float64) (float64, float64) {
|
func (m *Matrix) Transform(x, y float64) (float64, float64) {
|
||||||
xp := x*m[0] + y*m[1] + m[6]
|
xp := x*m[0] + y*m[1] + m[6]
|
||||||
@ -99,42 +94,24 @@ func (m *Matrix) Transform(x, y float64) (float64, float64) {
|
|||||||
return xp, yp
|
return xp, yp
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScalingFactorX returns X scaling of the affine transform.
|
// ScalingFactorX returns the X scaling of the affine transform.
|
||||||
func (m *Matrix) ScalingFactorX() float64 {
|
func (m *Matrix) ScalingFactorX() float64 {
|
||||||
return math.Sqrt(m[0]*m[0] + m[1]*m[1])
|
return math.Hypot(m[0], m[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScalingFactorY returns X scaling of the affine transform.
|
// ScalingFactorY returns the Y scaling of the affine transform.
|
||||||
func (m *Matrix) ScalingFactorY() float64 {
|
func (m *Matrix) ScalingFactorY() float64 {
|
||||||
return math.Sqrt(m[3]*m[3] + m[4]*m[4])
|
return math.Hypot(m[3], m[4])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Angle returns the angle of the affine transform.
|
// Angle returns the angle of the affine transform in `m` in degrees.
|
||||||
// For simplicity, we assume the transform is a multiple of 90 degrees.
|
func (m *Matrix) Angle() float64 {
|
||||||
func (m *Matrix) Angle() int {
|
theta := math.Atan2(-m[1], m[0])
|
||||||
a, b, c, d := m[0], m[1], m[3], m[4]
|
if theta < 0.0 {
|
||||||
// We are returning θ for
|
theta += 2 * math.Pi
|
||||||
// a b cos θ -sin θ
|
|
||||||
// c d = sin θ cos θ
|
|
||||||
if a > 0 && d > 0 {
|
|
||||||
// 1 0
|
|
||||||
// 0 1
|
|
||||||
return 0
|
|
||||||
} else if b < 0 && c > 0 {
|
|
||||||
// 0 1
|
|
||||||
// -1 0
|
|
||||||
return 90
|
|
||||||
} else if a < 0 && d < 0 {
|
|
||||||
// -1 0
|
|
||||||
// 0 -1
|
|
||||||
return 180
|
|
||||||
} else if b > 0 && c < 0 {
|
|
||||||
// 0 -1
|
|
||||||
// 1 0
|
|
||||||
return 270
|
|
||||||
}
|
}
|
||||||
common.Log.Debug("ERROR: Angle not a multiple of 90°. m=%s", m)
|
return theta / math.Pi * 180.0
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixup forces `m` to have reasonable values. It is a guard against crazy values in corrupt PDF
|
// fixup forces `m` to have reasonable values. It is a guard against crazy values in corrupt PDF
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* file 'LICENSE.md', which is part of this source code package.
|
* file 'LICENSE.md', which is part of this source code package.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package contentstream
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
@ -20,7 +20,7 @@ type GraphicsState struct {
|
|||||||
ColorspaceNonStroking model.PdfColorspace
|
ColorspaceNonStroking model.PdfColorspace
|
||||||
ColorStroking model.PdfColor
|
ColorStroking model.PdfColor
|
||||||
ColorNonStroking model.PdfColor
|
ColorNonStroking model.PdfColor
|
||||||
CTM model.Matrix
|
CTM Matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
type GraphicStateStack []GraphicsState
|
type GraphicStateStack []GraphicsState
|
||||||
@ -211,7 +211,7 @@ func (proc *ContentStreamProcessor) Process(resources *model.PdfPageResources) e
|
|||||||
proc.graphicsState.ColorspaceNonStroking = model.NewPdfColorspaceDeviceGray()
|
proc.graphicsState.ColorspaceNonStroking = model.NewPdfColorspaceDeviceGray()
|
||||||
proc.graphicsState.ColorStroking = model.NewPdfColorDeviceGray(0)
|
proc.graphicsState.ColorStroking = model.NewPdfColorDeviceGray(0)
|
||||||
proc.graphicsState.ColorNonStroking = model.NewPdfColorDeviceGray(0)
|
proc.graphicsState.ColorNonStroking = model.NewPdfColorDeviceGray(0)
|
||||||
proc.graphicsState.CTM = model.IdentityMatrix()
|
proc.graphicsState.CTM = IdentityMatrix()
|
||||||
|
|
||||||
for _, op := range proc.operations {
|
for _, op := range proc.operations {
|
||||||
var err error
|
var err error
|
||||||
@ -563,15 +563,15 @@ func (proc *ContentStreamProcessor) handleCommand_k(op *ContentStreamOperation,
|
|||||||
func (proc *ContentStreamProcessor) handleCommand_cm(op *ContentStreamOperation,
|
func (proc *ContentStreamProcessor) handleCommand_cm(op *ContentStreamOperation,
|
||||||
resources *model.PdfPageResources) error {
|
resources *model.PdfPageResources) error {
|
||||||
if len(op.Params) != 6 {
|
if len(op.Params) != 6 {
|
||||||
common.Log.Debug("Invalid number of parameters for cm: %d", len(op.Params))
|
common.Log.Debug("ERROR: Invalid number of parameters for cm: %d", len(op.Params))
|
||||||
return errors.New("Invalid number of parameters")
|
return errors.New("invalid number of parameters")
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := core.GetNumbersAsFloat(op.Params)
|
f, err := core.GetNumbersAsFloat(op.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m := model.NewMatrix(f[0], f[1], f[2], f[3], f[4], f[5])
|
m := NewMatrix(f[0], f[1], f[2], f[3], f[4], f[5])
|
||||||
proc.graphicsState.CTM.Concat(m)
|
proc.graphicsState.CTM.Concat(m)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -7,11 +7,13 @@
|
|||||||
|
|
||||||
// FIXME(peterwilliams97) Change to functional style. i.e. Return new value, don't mutate.
|
// FIXME(peterwilliams97) Change to functional style. i.e. Return new value, don't mutate.
|
||||||
|
|
||||||
package model
|
package extractor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"github.com/unidoc/unidoc/pdf/contentstream"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Point defines a point (X,Y) in Cartesian coordinates.
|
// Point defines a point (X,Y) in Cartesian coordinates.
|
||||||
@ -32,7 +34,7 @@ func (p *Point) Set(x, y float64) {
|
|||||||
|
|
||||||
// Transform transforms `p` by the affine transformation a, b, c, d, tx, ty.
|
// Transform transforms `p` by the affine transformation a, b, c, d, tx, ty.
|
||||||
func (p *Point) Transform(a, b, c, d, tx, ty float64) {
|
func (p *Point) Transform(a, b, c, d, tx, ty float64) {
|
||||||
m := NewMatrix(a, b, c, d, tx, ty)
|
m := contentstream.NewMatrix(a, b, c, d, tx, ty)
|
||||||
p.transformByMatrix(m)
|
p.transformByMatrix(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ func (p Point) Rotate(theta float64) Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transformByMatrix transforms `p` by the affine transformation `m`.
|
// transformByMatrix transforms `p` by the affine transformation `m`.
|
||||||
func (p *Point) transformByMatrix(m Matrix) {
|
func (p *Point) transformByMatrix(m contentstream.Matrix) {
|
||||||
p.X, p.Y = m.Transform(p.X, p.Y)
|
p.X, p.Y = m.Transform(p.X, p.Y)
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +318,7 @@ func (to *textObject) setTextMatrix(f []float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
a, b, c, d, tx, ty := f[0], f[1], f[2], f[3], f[4], f[5]
|
a, b, c, d, tx, ty := f[0], f[1], f[2], f[3], f[4], f[5]
|
||||||
to.Tm = model.NewMatrix(a, b, c, d, tx, ty)
|
to.Tm = contentstream.NewMatrix(a, b, c, d, tx, ty)
|
||||||
to.Tlm = to.Tm
|
to.Tlm = to.Tm
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +342,7 @@ func (to *textObject) showTextAdjusted(args *core.PdfObjectArray) error {
|
|||||||
if vertical {
|
if vertical {
|
||||||
dy, dx = dx, dy
|
dy, dx = dx, dy
|
||||||
}
|
}
|
||||||
td := translationMatrix(model.Point{X: dx, Y: dy})
|
td := translationMatrix(Point{X: dx, Y: dy})
|
||||||
to.Tm = td.Mult(to.Tm)
|
to.Tm = td.Mult(to.Tm)
|
||||||
common.Log.Trace("showTextAdjusted: dx,dy=%3f,%.3f Tm=%s", dx, dy, to.Tm)
|
common.Log.Trace("showTextAdjusted: dx,dy=%3f,%.3f Tm=%s", dx, dy, to.Tm)
|
||||||
case *core.PdfObjectString:
|
case *core.PdfObjectString:
|
||||||
@ -574,9 +574,9 @@ type textObject struct {
|
|||||||
gs contentstream.GraphicsState
|
gs contentstream.GraphicsState
|
||||||
fontStack *fontStacker
|
fontStack *fontStacker
|
||||||
State *textState
|
State *textState
|
||||||
Tm model.Matrix // Text matrix. For the character pointer.
|
Tm contentstream.Matrix // Text matrix. For the character pointer.
|
||||||
Tlm model.Matrix // Text line matrix. For the start of line pointer.
|
Tlm contentstream.Matrix // Text line matrix. For the start of line pointer.
|
||||||
Texts []XYText // Text gets written here.
|
Texts []XYText // Text gets written here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTextState returns a default textState.
|
// newTextState returns a default textState.
|
||||||
@ -595,8 +595,8 @@ func newTextObject(e *Extractor, gs contentstream.GraphicsState, state *textStat
|
|||||||
gs: gs,
|
gs: gs,
|
||||||
fontStack: fontStack,
|
fontStack: fontStack,
|
||||||
State: state,
|
State: state,
|
||||||
Tm: model.IdentityMatrix(),
|
Tm: contentstream.IdentityMatrix(),
|
||||||
Tlm: model.IdentityMatrix(),
|
Tlm: contentstream.IdentityMatrix(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +624,7 @@ func (to *textObject) renderText(data []byte) error {
|
|||||||
spaceWidth := spaceMetrics.Wx * glyphTextRatio
|
spaceWidth := spaceMetrics.Wx * glyphTextRatio
|
||||||
common.Log.Trace("spaceWidth=%.2f text=%q font=%s fontSize=%.1f", spaceWidth, runes, font, tfs)
|
common.Log.Trace("spaceWidth=%.2f text=%q font=%s fontSize=%.1f", spaceWidth, runes, font, tfs)
|
||||||
|
|
||||||
stateMatrix := model.NewMatrix(
|
stateMatrix := contentstream.NewMatrix(
|
||||||
tfs*th, 0,
|
tfs*th, 0,
|
||||||
0, tfs,
|
0, tfs,
|
||||||
0, state.Trise)
|
0, state.Trise)
|
||||||
@ -658,12 +658,12 @@ func (to *textObject) renderText(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// c is the character size in unscaled text units.
|
// c is the character size in unscaled text units.
|
||||||
c := model.Point{X: m.Wx * glyphTextRatio, Y: m.Wy * glyphTextRatio}
|
c := Point{X: m.Wx * glyphTextRatio, Y: m.Wy * glyphTextRatio}
|
||||||
|
|
||||||
// t0 is the end of this character.
|
// t0 is the end of this character.
|
||||||
// t is the displacement of the text cursor when the character is rendered.
|
// t is the displacement of the text cursor when the character is rendered.
|
||||||
t0 := model.Point{X: (c.X*tfs + w) * th}
|
t0 := Point{X: (c.X*tfs + w) * th}
|
||||||
t := model.Point{X: (c.X*tfs + state.Tc + w) * th}
|
t := Point{X: (c.X*tfs + state.Tc + w) * th}
|
||||||
|
|
||||||
// td, td0 are t, t0 in matrix form.
|
// td, td0 are t, t0 in matrix form.
|
||||||
// td0 is where this character ends. td is where the next character starts.
|
// td0 is where this character ends. td is where the next character starts.
|
||||||
@ -694,14 +694,14 @@ func (to *textObject) renderText(data []byte) error {
|
|||||||
const glyphTextRatio = 1.0 / 1000.0
|
const glyphTextRatio = 1.0 / 1000.0
|
||||||
|
|
||||||
// translation returns the translation part of `m`.
|
// translation returns the translation part of `m`.
|
||||||
func translation(m model.Matrix) model.Point {
|
func translation(m contentstream.Matrix) Point {
|
||||||
tx, ty := m.Translation()
|
tx, ty := m.Translation()
|
||||||
return model.Point{tx, ty}
|
return Point{tx, ty}
|
||||||
}
|
}
|
||||||
|
|
||||||
// translationMatrix returns a matrix that translates by `p`.
|
// translationMatrix returns a matrix that translates by `p`.
|
||||||
func translationMatrix(p model.Point) model.Matrix {
|
func translationMatrix(p Point) contentstream.Matrix {
|
||||||
return model.TranslationMatrix(p.X, p.Y)
|
return contentstream.TranslationMatrix(p.X, p.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveTo moves the start of line pointer by `tx`,`ty` and sets the text pointer to the
|
// moveTo moves the start of line pointer by `tx`,`ty` and sets the text pointer to the
|
||||||
@ -709,26 +709,26 @@ func translationMatrix(p model.Point) model.Matrix {
|
|||||||
// Move to the start of the next line, offset from the start of the current line by (tx, ty).
|
// Move to the start of the next line, offset from the start of the current line by (tx, ty).
|
||||||
// `tx` and `ty` are in unscaled text space units.
|
// `tx` and `ty` are in unscaled text space units.
|
||||||
func (to *textObject) moveTo(tx, ty float64) {
|
func (to *textObject) moveTo(tx, ty float64) {
|
||||||
to.Tlm = model.NewMatrix(1, 0, 0, 1, tx, ty).Mult(to.Tlm)
|
to.Tlm = contentstream.NewMatrix(1, 0, 0, 1, tx, ty).Mult(to.Tlm)
|
||||||
to.Tm = to.Tlm
|
to.Tm = to.Tlm
|
||||||
}
|
}
|
||||||
|
|
||||||
// XYText represents text drawn on a page and its position in device coordinates.
|
// XYText represents text drawn on a page and its position in device coordinates.
|
||||||
// All dimensions are in device coordinates.
|
// All dimensions are in device coordinates.
|
||||||
type XYText struct {
|
type XYText struct {
|
||||||
Text string // The text.
|
Text string // The text.
|
||||||
Orient int // The text orientation in degrees. This is the current trm rounded to 10°.
|
Orient int // The text orientation in degrees. This is the current trm rounded to 10°.
|
||||||
OrientedStart model.Point // Left of text in orientation where text is horizontal.
|
OrientedStart Point // Left of text in orientation where text is horizontal.
|
||||||
OrientedEnd model.Point // Right of text in orientation where text is horizontal.
|
OrientedEnd Point // Right of text in orientation where text is horizontal.
|
||||||
Height float64 // Text height.
|
Height float64 // Text height.
|
||||||
SpaceWidth float64 // Best guess at the width of a space in the font the text was rendered with.
|
SpaceWidth float64 // Best guess at the width of a space in the font the text was rendered with.
|
||||||
count int64 // To help with reading debug logs.
|
count int64 // To help with reading debug logs.
|
||||||
}
|
}
|
||||||
|
|
||||||
// newXYText returns an XYText for text `text` rendered with text rendering matrix `trm` and end
|
// newXYText returns an XYText for text `text` rendered with text rendering matrix `trm` and end
|
||||||
// of character device coordinates `end`. `spaceWidth` is our best guess at the width of a space in
|
// of character device coordinates `end`. `spaceWidth` is our best guess at the width of a space in
|
||||||
// the font the text is rendered in device coordinates.
|
// the font the text is rendered in device coordinates.
|
||||||
func (to *textObject) newXYText(text string, trm model.Matrix, end model.Point, spaceWidth float64) XYText {
|
func (to *textObject) newXYText(text string, trm contentstream.Matrix, end Point, spaceWidth float64) XYText {
|
||||||
to.e.textCount++
|
to.e.textCount++
|
||||||
theta := trm.Angle()
|
theta := trm.Angle()
|
||||||
orient := nearestMultiple(theta, 10)
|
orient := nearestMultiple(theta, 10)
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/unidoc/unidoc/common/license"
|
"github.com/unidoc/unidoc/common/license"
|
||||||
"github.com/unidoc/unidoc/pdf/contentstream"
|
"github.com/unidoc/unidoc/pdf/contentstream"
|
||||||
"github.com/unidoc/unidoc/pdf/core"
|
"github.com/unidoc/unidoc/pdf/core"
|
||||||
"github.com/unidoc/unidoc/pdf/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The text rendering mode, Tmode, determines whether showing text shall cause glyph outlines to be
|
// The text rendering mode, Tmode, determines whether showing text shall cause glyph outlines to be
|
||||||
@ -28,15 +27,15 @@ const (
|
|||||||
RenderModeClip
|
RenderModeClip
|
||||||
)
|
)
|
||||||
|
|
||||||
func toPageCoords(gs contentstream.GraphicsState, objs []core.PdfObject) (model.Point, error) {
|
func toPageCoords(gs contentstream.GraphicsState, objs []core.PdfObject) (Point, error) {
|
||||||
x, y, err := toFloatXY(objs)
|
x, y, err := toFloatXY(objs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Point{}, err
|
return Point{}, err
|
||||||
}
|
}
|
||||||
return toPagePoint(gs, x, y), nil
|
return toPagePoint(gs, x, y), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPagePointList(gs contentstream.GraphicsState, objs []core.PdfObject) (points []model.Point, err error) {
|
func toPagePointList(gs contentstream.GraphicsState, objs []core.PdfObject) (points []Point, err error) {
|
||||||
if len(objs)%2 != 0 {
|
if len(objs)%2 != 0 {
|
||||||
err = fmt.Errorf("Invalid number of params: %d", len(objs))
|
err = fmt.Errorf("Invalid number of params: %d", len(objs))
|
||||||
common.Log.Debug("toPagePointList: err=%v", err)
|
common.Log.Debug("toPagePointList: err=%v", err)
|
||||||
@ -53,9 +52,9 @@ func toPagePointList(gs contentstream.GraphicsState, objs []core.PdfObject) (poi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPagePoint(gs contentstream.GraphicsState, x, y float64) model.Point {
|
func toPagePoint(gs contentstream.GraphicsState, x, y float64) Point {
|
||||||
xp, yp := gs.Transform(x, y)
|
xp, yp := gs.Transform(x, y)
|
||||||
return model.Point{xp, yp}
|
return Point{xp, yp}
|
||||||
}
|
}
|
||||||
|
|
||||||
// toFloatXY returns `objs` as 2 floats, if that's what `objs` is, or an error if it isn't.
|
// toFloatXY returns `objs` as 2 floats, if that's what `objs` is, or an error if it isn't.
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is subject to the terms and conditions defined in
|
|
||||||
* file 'LICENSE.md', which is part of this source code package.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Matrix is a linear transform matrix in homogenous coordinates.
|
|
||||||
// PDF coordinate transforms are always affine so we only need 6 of these. See newMatrix.
|
|
||||||
type Matrix [9]float64
|
|
||||||
|
|
||||||
// IdentityMatrix returns the identity transform.
|
|
||||||
func IdentityMatrix() Matrix {
|
|
||||||
return NewMatrix(1, 0, 0, 1, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TranslationMatrix returns a matrix that translates by `tx`, `ty`.
|
|
||||||
func TranslationMatrix(tx, ty float64) Matrix {
|
|
||||||
return NewMatrix(1, 0, 0, 1, tx, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMatrix returns an affine transform matrix laid out in homogenous coordinates as
|
|
||||||
// a b 0
|
|
||||||
// c d 0
|
|
||||||
// tx ty 1
|
|
||||||
func NewMatrix(a, b, c, d, tx, ty float64) Matrix {
|
|
||||||
m := Matrix{
|
|
||||||
a, b, 0,
|
|
||||||
c, d, 0,
|
|
||||||
tx, ty, 1,
|
|
||||||
}
|
|
||||||
m.fixup()
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string describing `m`.
|
|
||||||
func (m Matrix) String() string {
|
|
||||||
a, b, c, d, tx, ty := m[0], m[1], m[3], m[4], m[6], m[7]
|
|
||||||
return fmt.Sprintf("[%.4f,%.4f,%.4f,%.4f:%.4f,%.4f]", a, b, c, d, tx, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets `m` to affine transform a,b,c,d,tx,ty.
|
|
||||||
func (m *Matrix) Set(a, b, c, d, tx, ty float64) {
|
|
||||||
m[0], m[1] = a, b
|
|
||||||
m[3], m[4] = c, d
|
|
||||||
m[6], m[7] = tx, ty
|
|
||||||
m.fixup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concat sets `m` to `m` × `b`.
|
|
||||||
// `b` needs to be created by newMatrix. i.e. It must be an affine transform.
|
|
||||||
// m00 m01 0 b00 b01 0 m00*b00 + m01*b01 m00*b10 + m01*b11 0
|
|
||||||
// m10 m11 0 × b10 b11 0 = m10*b00 + m11*b01 m10*b10 + m11*b11 0
|
|
||||||
// m20 m21 1 b20 b21 1 m20*b00 + m21*b10 + b20 m20*b01 + m21*b11 + b21 1
|
|
||||||
func (m *Matrix) Concat(b Matrix) {
|
|
||||||
*m = Matrix{
|
|
||||||
m[0]*b[0] + m[1]*b[3], m[0]*b[1] + m[1]*b[4], 0,
|
|
||||||
m[3]*b[0] + m[4]*b[3], m[3]*b[1] + m[4]*b[4], 0,
|
|
||||||
m[6]*b[0] + m[7]*b[3] + b[6], m[6]*b[1] + m[7]*b[4] + b[7], 1,
|
|
||||||
}
|
|
||||||
m.fixup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mult returns `m` × `b`.
|
|
||||||
func (m Matrix) Mult(b Matrix) Matrix {
|
|
||||||
m.Concat(b)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate appends a translation of `dx`,`dy` to `m`.
|
|
||||||
// m.Translate(dx, dy) is equivalent to m.Concat(NewMatrix(1, 0, 0, 1, dx, dy))
|
|
||||||
func (m *Matrix) Translate(dx, dy float64) {
|
|
||||||
m[6] += dx
|
|
||||||
m[7] += dy
|
|
||||||
m.fixup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translation returns the translation part of `m`.
|
|
||||||
func (m *Matrix) Translation() (float64, float64) {
|
|
||||||
return m[6], m[7]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform returns coordinates `x`,`y` transformed by `m`.
|
|
||||||
func (m *Matrix) Transform(x, y float64) (float64, float64) {
|
|
||||||
xp := x*m[0] + y*m[1] + m[6]
|
|
||||||
yp := x*m[3] + y*m[4] + m[7]
|
|
||||||
return xp, yp
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScalingFactorX returns the X scaling of the affine transform.
|
|
||||||
func (m *Matrix) ScalingFactorX() float64 {
|
|
||||||
return math.Hypot(m[0], m[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScalingFactorY returns the Y scaling of the affine transform.
|
|
||||||
func (m *Matrix) ScalingFactorY() float64 {
|
|
||||||
return math.Hypot(m[3], m[4])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Angle returns the angle of the affine transform in `m` in degrees.
|
|
||||||
func (m *Matrix) Angle() float64 {
|
|
||||||
theta := math.Atan2(-m[1], m[0])
|
|
||||||
if theta < 0.0 {
|
|
||||||
theta += 2 * math.Pi
|
|
||||||
}
|
|
||||||
return theta / math.Pi * 180.0
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixup forces `m` to have reasonable values. It is a guard against crazy values in corrupt PDF
|
|
||||||
// files.
|
|
||||||
// Currently it clamps elements to [-maxAbsNumber, -maxAbsNumber] to avoid floating point exceptions.
|
|
||||||
func (m *Matrix) fixup() {
|
|
||||||
for i, x := range m {
|
|
||||||
if x > maxAbsNumber {
|
|
||||||
m[i] = maxAbsNumber
|
|
||||||
} else if x < -maxAbsNumber {
|
|
||||||
m[i] = -maxAbsNumber
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// largest numbers needed in PDF transforms. Is this correct?
|
|
||||||
const maxAbsNumber = 1e9
|
|
Loading…
x
Reference in New Issue
Block a user