func traverseTiles(Style style, Area area) (image.Image, error) { m := image.NewRGBA(image.Rect(-Margins, -Margins, Area.TileRange.Dx()*TileSize+Margins, Area.TileRange.Dy()*TileSize+Margins)) whiteColor := color.RGBA{255, 255, 255, 255} draw.Draw(m, m.Bounds(), &image.Uniform{whiteColor}, image.ZP, draw.Src) frameColor := color.RGBA{50, 50, 50, 255} draw.Draw(m, image.Rect(-Frame, -Frame, Area.TileRange.Dx()*TileSize+Frame, Area.TileRange.Dy()*TileSize+Frame), &image.Uniform{frameColor}, image.ZP, draw.Src) position := image.Rectangle{image.ZP, image.Point{X: TileSize, Y: TileSize}} for y := Area.TileRange.Min.Y; y < Area.TileRange.Max.Y; y++ { for x := Area.TileRange.Min.X; x < Area.TileRange.Max.X; x++ { img, err := readTile(Style.Mapid, Area.Z, uint(x), uint(y)) if err != nil { fmt.Printf("no luck reading this tile: z:%v, %vx %v \n", Area.Z, x, y) fmt.Println(err) break } fmt.Printf("...adding %vx%v \n", x, y) draw.Draw(m, position, img, image.ZP, draw.Src) position = position.Add(image.Point{X: TileSize, Y: 0}) } position = image.Rectangle{image.Point{X: 0, Y: (y - Area.TileRange.Min.Y + 1) * TileSize}, image.Point{X: TileSize, Y: (y - Area.TileRange.Min.Y + 2) * TileSize}} } fmt.Println("Great success!") return m, nil }
// Copy copies the part of the source image defined by src and sr and writes to // the part of the destination image defined by dst and the translation of sr // so that sr.Min translates to dp. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) { mask, mp, op := image.Image(nil), image.Point{}, Over if opts != nil { // TODO: set mask, mp and op. } dr := sr.Add(dp.Sub(sr.Min)) DrawMask(dst, dr, src, sr.Min, mask, mp, op) }
func (m *mover) Draw(img draw.Image, clipr image.Rectangle) { //debugp("mover draw clipr %v; centre %v; delta %v\n", clipr, centre(m.item.Bbox()), m.delta) clipr = clipr.Add(m.delta) i := SliceImage(clipr.Max.X, clipr.Max.Y, clipr, img, m.delta) //debugp("item draw clipr %v; delta %v; bbox %v\n", clipr, m.delta, m.item.Bbox()) m.item.Draw(i, clipr) //debugp("item drawn\n") }
// ToImageRect converts a point in the feature pyramid to a rectangle in the image. func (pyr *Generator) ToImageRect(level int, pt image.Point, interior image.Rectangle) image.Rectangle { // Translate interior by position (scaled by rate) and subtract margin offset. rate := pyr.Transform.Rate() offset := pyr.Pad.Margin.TopLeft() scale := pyr.Image.Scales[level] rect := interior.Add(pt.Mul(rate)).Sub(offset) return scaleRect(1/scale, rect) }
// Copy copies the part of the source image defined by src and sr and writes to // the part of the destination image defined by dst and the translation of sr // so that sr.Min translates to dp. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) { var o Options if opts != nil { o = *opts } dr := sr.Add(dp.Sub(sr.Min)) // TODO: honor o.DstMask and o.SrcMask. DrawMask(dst, dr, src, sr.Min, nil, image.Point{}, o.Op) }
// initLayout constructs two masks for drawing the battery and the remaining // energy as well as sets the pixel bounds for drawing energy capacity. the // masks allow for simplified space-fills and reduced chance of pixel gaps. func (app *App) initLayout() { var zeropt image.Point rectOutTop := image.Rectangle{Min: app.Layout.battRect.Min, Max: app.Layout.battRect.Min.Add(image.Point{2, 2})} rectOutBottom := rectOutTop.Add(image.Point{Y: app.Layout.battRect.Size().Y - rectOutTop.Size().Y}) capRect := image.Rectangle{ Min: image.Point{X: rectOutTop.Min.X, Y: rectOutTop.Max.Y}, Max: image.Point{X: rectOutBottom.Max.X, Y: rectOutBottom.Min.Y}, } bodyRect := app.Layout.battRect bodyRect.Min.X = capRect.Max.X // energy will be drawn under the battery shell. The only place where it // is not safe to draw energy is outside the battery on the positive end. energyMask := image.NewAlpha(app.Layout.battRect) draw.Draw(energyMask, app.Layout.battRect, opaque, zeropt, draw.Over) draw.Draw(energyMask, rectOutTop, transparent, zeropt, draw.Src) draw.Draw(energyMask, rectOutBottom, transparent, zeropt, draw.Src) app.maskEnergy = energyMask // the body uses the same mask as the energy with additional transparency // inside the battery's shell. the mask construction is complex because // area inside the cap may be exposed. bodyMask := image.NewAlpha(app.Layout.battRect) draw.Draw(bodyMask, app.Layout.battRect, energyMask, app.Layout.battRect.Min, draw.Over) bodyMaskRect := shrinkRect(bodyRect, app.Layout.thickness) draw.Draw(bodyMask, bodyMaskRect, transparent, zeropt, draw.Src) capMaskRect := shrinkRect(capRect, app.Layout.thickness) capMaskRect.Max.X += 2 * app.Layout.thickness draw.Draw(bodyMask, capMaskRect, transparent, zeropt, draw.Src) app.maskBattery = bodyMask // create a freetype.Context to render text. each time the context is used // it must have its SetDst method called. app.tt = freetype.NewContext() app.tt.SetSrc(black) app.tt.SetClip(app.Layout.textRect) app.tt.SetDPI(app.Layout.DPI) app.tt.SetFont(app.Layout.font) app.tt.SetFontSize(app.Layout.fontSize) ttopt := &truetype.Options{ Size: app.Layout.fontSize, DPI: app.Layout.DPI, } ttface := truetype.NewFace(app.Layout.font, ttopt) app.font = &font.Drawer{ Src: black, Face: ttface, } // the rectangle in which energy is drawn needs to account for thickness to // make the visible percentage more accurate. after adjustment reduce the // energy rect to account for the account of energy drained. the energy // mask makes computing Y bounds largely irrelevant. app.minEnergy = capMaskRect.Min.X app.maxEnergy = bodyMaskRect.Max.X }
// Copy copies the part of the source image defined by src and sr and writes // the result of a Porter-Duff composition to the part of the destination image // defined by dst and the translation of sr so that sr.Min translates to dp. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, op Op, opts *Options) { var o Options if opts != nil { o = *opts } dr := sr.Add(dp.Sub(sr.Min)) if o.DstMask == nil { DrawMask(dst, dr, src, sr.Min, o.SrcMask, o.SrcMaskP.Add(sr.Min), op) } else { NearestNeighbor.Scale(dst, dr, src, sr, op, opts) } }
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y sx0 := sp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X sx1 := sx0 + (x1 - x0) i0 := dst.PixOffset(x0, y0) di := dx * 4 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } sr, sg, sb, sa := src.At(sx, sy).RGBA() if op == Over { dr := uint32(dst.Pix[i+0]) dg := uint32(dst.Pix[i+1]) db := uint32(dst.Pix[i+2]) da := uint32(dst.Pix[i+3]) // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. // We work in 16-bit color, and so would normally do: // dr |= dr << 8 // and similarly for dg, db and da, but instead we multiply a // (which is a 16-bit color, ranging in [0,65535]) by 0x101. // This yields the same result, but is fewer arithmetic operations. a := (m - (sa * ma / m)) * 0x101 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8) dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8) dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8) dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8) } else { dst.Pix[i+0] = uint8(sr * ma / m >> 8) dst.Pix[i+1] = uint8(sg * ma / m >> 8) dst.Pix[i+2] = uint8(sb * ma / m >> 8) dst.Pix[i+3] = uint8(sa * ma / m >> 8) } } i0 += dy * dst.Stride } }
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y sx0 := sp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X i0 := (y0 - dst.Rect.Min.Y) * dst.Stride for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { dpix := dst.Pix[i0:] for x, sx, mx := x0, sx0, mx0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } sr, sg, sb, sa := src.At(sx, sy).RGBA() var dr, dg, db, da uint32 if op == Over { rgba := dpix[x-dst.Rect.Min.X] dr = uint32(rgba.R) dg = uint32(rgba.G) db = uint32(rgba.B) da = uint32(rgba.A) // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. // We work in 16-bit color, and so would normally do: // dr |= dr << 8 // and similarly for dg, db and da, but instead we multiply a // (which is a 16-bit color, ranging in [0,65535]) by 0x101. // This yields the same result, but is fewer arithmetic operations. a := (m - (sa * ma / m)) * 0x101 dr = (dr*a + sr*ma) / m dg = (dg*a + sg*ma) / m db = (db*a + sb*ma) / m da = (da*a + sa*ma) / m } else { dr = sr * ma / m dg = sg * ma / m db = sb * ma / m da = sa * ma / m } dpix[x-dst.Rect.Min.X] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } i0 += dy * dst.Stride } }
// Check if the selected object would overlap with any other // if we move it to p coordinates: func overlaps(list []graph.NodeIf, p image.Point) bool { var box image.Rectangle for _, n := range list { if n.IsSelected() { box = n.BBox() break } } newBox := box.Add(p.Sub(box.Min)) for _, n := range list { if !n.IsSelected() && newBox.Overlaps(n.BBox()) { return true } } return false }
func (a *area) Repaint(r image.Rectangle) { var hscroll, vscroll C.int var rect C.RECT C.SendMessageW(a.hwnd, C.msgAreaGetScroll, C.WPARAM(uintptr(unsafe.Pointer(&hscroll))), C.LPARAM(uintptr(unsafe.Pointer(&vscroll)))) r = r.Add(image.Pt(int(hscroll), int(vscroll))) // adjust by scroll position r = image.Rect(0, 0, a.width, a.height).Intersect(r) if r.Empty() { return } rect.left = C.LONG(r.Min.X) rect.top = C.LONG(r.Min.Y) rect.right = C.LONG(r.Max.X) rect.bottom = C.LONG(r.Max.Y) C.SendMessageW(a.hwnd, C.msgAreaRepaint, 0, C.LPARAM(uintptr(unsafe.Pointer(&rect)))) }
func anchor(r image.Rectangle, flags Anchor, p image.Point) image.Rectangle { var dp image.Point switch flags & (E | W) { case E: dp.X = r.Dx() case E | W, 0: dp.X = r.Dx() / 2 } switch flags & (N | S) { case S: dp.Y = r.Dy() case S | N, 0: dp.Y = r.Dy() / 2 } return r.Add(p.Sub(r.Min).Sub(dp)) }
func setpiece(p *Piece) { bb.Draw(bb.R, display.White, nil, image.ZP) bbmask.Draw(bb.R, display.Transparent, nil, image.ZP) br = image.Rect(0, 0, 0, 0) br2 = br piece = p if p == nil { return } var op image.Point var r image.Rectangle r.Min = bb.R.Min for i, pt := range p.d { r.Min.X += pt.X * pcsz r.Min.Y += pt.Y * pcsz r.Max.X = r.Min.X + pcsz r.Max.Y = r.Min.Y + pcsz if i == 0 { bb.Draw(r, display.Black, nil, image.ZP) bb.Draw(r.Inset(1), tx[piece.tx], nil, image.ZP) bbmask.Draw(r, display.Opaque, nil, image.ZP) op = r.Min } else { bb.Draw(r, bb, nil, op) bbmask.Draw(r, bbmask, nil, op) } if br.Max.X < r.Max.X { br.Max.X = r.Max.X } if br.Max.Y < r.Max.Y { br.Max.Y = r.Max.Y } } br.Max = br.Max.Sub(bb.R.Min) delta := image.Pt(0, DY) br2.Max = br.Max.Add(delta) r = br.Add(bb2.R.Min) r2 := br2.Add(bb2.R.Min) bb2.Draw(r2, display.White, nil, image.ZP) bb2.Draw(r.Add(delta), bb, nil, bb.R.Min) bb2mask.Draw(r2, display.Transparent, nil, image.ZP) bb2mask.Draw(r, display.Opaque, bbmask, bb.R.Min) bb2mask.Draw(r.Add(delta), display.Opaque, bbmask, bb.R.Min) }
func testYCbCr(t *testing.T, r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio, delta image.Point) { // Create a YCbCr image m, whose bounds are r translated by (delta.X, delta.Y). r1 := r.Add(delta) img := image.NewYCbCr(r1, subsampleRatio) // Initialize img's pixels. For 422 and 420 subsampling, some of the Cb and Cr elements // will be set multiple times. That's OK. We just want to avoid a uniform image. for y := r1.Min.Y; y < r1.Max.Y; y++ { for x := r1.Min.X; x < r1.Max.X; x++ { yi := img.YOffset(x, y) ci := img.COffset(x, y) img.Y[yi] = uint8(16*y + x) img.Cb[ci] = uint8(y + 16*x) img.Cr[ci] = uint8(y + 16*x) } } m := imageYCbCrToYCC(img) // Make various sub-images of m. for y0 := delta.Y + 3; y0 < delta.Y+7; y0++ { for y1 := delta.Y + 8; y1 < delta.Y+13; y1++ { for x0 := delta.X + 3; x0 < delta.X+7; x0++ { for x1 := delta.X + 8; x1 < delta.X+13; x1++ { subRect := image.Rect(x0, y0, x1, y1) sub := m.SubImage(subRect).(*ycc) // For each point in the sub-image's bounds, check that m.At(x, y) equals sub.At(x, y). for y := sub.Rect.Min.Y; y < sub.Rect.Max.Y; y++ { for x := sub.Rect.Min.X; x < sub.Rect.Max.X; x++ { color0 := m.At(x, y).(color.YCbCr) color1 := sub.At(x, y).(color.YCbCr) if color0 != color1 { t.Errorf("r=%v, subsampleRatio=%v, delta=%v, x=%d, y=%d, color0=%v, color1=%v", r, subsampleRatio, delta, x, y, color0, color1) return } } } } } } } }
func setpiece(p *Piece) { draw.Draw(bb, bbr, image.White, image.ZP) draw.Draw(bbmask, bbr, image.Transparent, image.ZP) br = image.Rect(0, 0, 0, 0) br2 = br piece = p if p == nil { return } var op image.Point var r image.Rectangle r.Min = bbr.Min for i, pt := range p.d { r.Min.X += pt.X * pcsz r.Min.Y += pt.Y * pcsz r.Max.X = r.Min.X + pcsz r.Max.Y = r.Min.Y + pcsz if i == 0 { draw.Draw(bb, r, image.Black, image.ZP) draw.Draw(bb, r.Inset(1), txpix[piece.tx], image.ZP) draw.Draw(bbmask, r, image.Opaque, image.ZP) op = r.Min } else { draw.Draw(bb, r, bb, op) draw.Draw(bbmask, r, bbmask, op) } if br.Max.X < r.Max.X { br.Max.X = r.Max.X } if br.Max.Y < r.Max.Y { br.Max.Y = r.Max.Y } } br.Max = br.Max.Sub(bbr.Min) delta := image.Pt(0, DY) br2.Max = br.Max.Add(delta) r = br.Add(bb2r.Min) r2 := br2.Add(bb2r.Min) draw.Draw(bb2, r2, image.White, image.ZP) draw.Draw(bb2, r.Add(delta), bb, bbr.Min) draw.Draw(bb2mask, r2, image.Transparent, image.ZP) draw.DrawMask(bb2mask, r, image.Opaque, bbr.Min, bbmask, image.ZP, draw.Over) draw.DrawMask(bb2mask, r.Add(delta), image.Opaque, bbr.Min, bbmask, image.ZP, draw.Over) }
func fitted(into image.Rectangle, size image.Point) image.Rectangle { ratio := float64(size.X) / float64(size.Y) if size.X > into.Dx() { size.X = into.Dx() size.Y = int(float64(into.Dx()) / ratio) } if size.Y > into.Dy() { size.X = int(float64(into.Dy()) * ratio) size.Y = into.Dy() } r := image.Rectangle{ Min: into.Min, Max: into.Min.Add(size), } r = r.Add(image.Point{into.Dx() / 2, into.Dy() / 2}). Sub(image.Point{size.X / 2, size.Y / 2}) return r }
func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { buf := src.(*bufferImpl) buf.preUpload() // src2dst is added to convert from the src coordinate space to the dst // coordinate space. It is subtracted to convert the other way. src2dst := dp.Sub(sr.Min) // Clip to the source. sr = sr.Intersect(buf.Bounds()) // Clip to the destination. dr := sr.Add(src2dst) dr = dr.Intersect(t.Bounds()) if dr.Empty() { return } // Bring dr.Min in dst-space back to src-space to get the pixel buffer offset. pix := buf.rgba.Pix[buf.rgba.PixOffset(dr.Min.X-src2dst.X, dr.Min.Y-src2dst.Y):] t.w.glctxMu.Lock() defer t.w.glctxMu.Unlock() t.w.glctx.BindTexture(gl.TEXTURE_2D, t.id) width := dr.Dx() if width*4 == buf.rgba.Stride { t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, dr.Min.Y, width, dr.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pix) return } // TODO: can we use GL_UNPACK_ROW_LENGTH with glPixelStorei for stride in // ES 3.0, instead of uploading the pixels row-by-row? for y, p := dr.Min.Y, 0; y < dr.Max.Y; y++ { t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, y, width, 1, gl.RGBA, gl.UNSIGNED_BYTE, pix[p:]) p += buf.rgba.Stride } }
func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool { return image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) && (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X)) }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { if mask == nil { switch src0 := src.(type) { case *image.ColorImage: drawFillOver(dst0, r, src0) return case *image.RGBA: drawCopyOver(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBAOver(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { case *image.ColorImage: drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { switch src0 := src.(type) { case *image.ColorImage: drawFillSrc(dst0, r, src0) return case *image.RGBA: drawCopySrc(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBASrc(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return } x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { // Rectangles overlap: process backward? if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } var out *image.RGBA64Color sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, zeroColor) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if out == nil { out = new(image.RGBA64Color) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } dst.Set(x, y, out) } } } }
// Converts the position of a detection in a feature image to a rectangle in the intensity image. // // Additional arguments are: // the integer downsample rate of the feature transform, // the margin which was added to the image before taking the feature transform, // the rectangular region within the window which corresponds to the annotation. func featPtToImRect(pt image.Point, rate int, margin feat.Margin, interior image.Rectangle) image.Rectangle { return interior.Add(pt.Mul(rate)).Sub(margin.TopLeft()) }
// Converts a point in the feature pyramid to a rectangle in the image. func (pyr *Pyramid) ToImageRect(pt imgpyr.Point, interior image.Rectangle) image.Rectangle { // Translate interior by position (scaled by rate) and subtract margin offset. rect := interior.Add(pt.Pos.Mul(pyr.Rate)).Sub(pyr.Margin.TopLeft()) // Scale rectangle. return scaleRect(1/pyr.Scale(pt.Level), rect) }