mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-30 13:48:54 +08:00
162 lines
4.2 KiB
Go
162 lines
4.2 KiB
Go
![]() |
// Copyright 2018 Google Inc.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package draw
|
||
|
|
||
|
// braille_line.go contains code that draws lines on a braille canvas.
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"image"
|
||
|
|
||
|
"github.com/mum4k/termdash/canvas/braille"
|
||
|
"github.com/mum4k/termdash/cell"
|
||
|
)
|
||
|
|
||
|
// BrailleLineOption is used to provide options to BrailleLine().
|
||
|
type BrailleLineOption interface {
|
||
|
// set sets the provided option.
|
||
|
set(*brailleLineOptions)
|
||
|
}
|
||
|
|
||
|
// brailleLineOptions stores the provided options.
|
||
|
type brailleLineOptions struct {
|
||
|
cellOpts []cell.Option
|
||
|
}
|
||
|
|
||
|
// newBrailleLineOptions returns a new brailleLineOptions instance.
|
||
|
func newBrailleLineOptions() *brailleLineOptions {
|
||
|
return &brailleLineOptions{}
|
||
|
}
|
||
|
|
||
|
// brailleLineOption implements BrailleLineOption.
|
||
|
type brailleLineOption func(*brailleLineOptions)
|
||
|
|
||
|
// set implements BrailleLineOption.set.
|
||
|
func (o brailleLineOption) set(opts *brailleLineOptions) {
|
||
|
o(opts)
|
||
|
}
|
||
|
|
||
|
// BrailleLineCellOpts sets options on the cells that contain the line.
|
||
|
func BrailleLineCellOpts(cOpts ...cell.Option) BrailleLineOption {
|
||
|
return brailleLineOption(func(opts *brailleLineOptions) {
|
||
|
opts.cellOpts = cOpts
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// BrailleLine draws an approximated line segment on the braille canvas between
|
||
|
// the two provided points.
|
||
|
// Both start and end must be valid points within the canvas. Start and end can
|
||
|
// be the same point in which case only one pixel will be set on the braille
|
||
|
// canvas.
|
||
|
// The start or end coordinates must not be negative.
|
||
|
func BrailleLine(bc *braille.Canvas, start, end image.Point, opts ...BrailleLineOption) error {
|
||
|
if start.X < 0 || start.Y < 0 {
|
||
|
return fmt.Errorf("the start coordinates cannot be negative, got: %v", start)
|
||
|
}
|
||
|
if end.X < 0 || end.Y < 0 {
|
||
|
return fmt.Errorf("the end coordinates cannot be negative, got: %v", end)
|
||
|
}
|
||
|
|
||
|
opt := newBrailleLineOptions()
|
||
|
for _, o := range opts {
|
||
|
o.set(opt)
|
||
|
}
|
||
|
|
||
|
// Implements Bresenham's line algorithm.
|
||
|
// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
|
||
|
|
||
|
vertProj := abs(end.Y - start.Y)
|
||
|
horizProj := abs(end.X - start.X)
|
||
|
if vertProj < horizProj {
|
||
|
if start.X > end.X {
|
||
|
return lineLow(bc, end.X, end.Y, start.X, start.Y, opt)
|
||
|
} else {
|
||
|
return lineLow(bc, start.X, start.Y, end.X, end.Y, opt)
|
||
|
}
|
||
|
} else {
|
||
|
if start.Y > end.Y {
|
||
|
return lineHigh(bc, end.X, end.Y, start.X, start.Y, opt)
|
||
|
} else {
|
||
|
return lineHigh(bc, start.X, start.Y, end.X, end.Y, opt)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// lineLow draws a line whose horizontal projection (end.X - start.X) is longer
|
||
|
// than its vertical projection (end.Y - start.Y).
|
||
|
func lineLow(bc *braille.Canvas, x0, y0, x1, y1 int, opt *brailleLineOptions) error {
|
||
|
deltaX := x1 - x0
|
||
|
deltaY := y1 - y0
|
||
|
|
||
|
stepY := 1
|
||
|
if deltaY < 0 {
|
||
|
stepY = -1
|
||
|
deltaY = -deltaY
|
||
|
}
|
||
|
|
||
|
diff := 2*deltaY - deltaX
|
||
|
y := y0
|
||
|
for x := x0; x <= x1; x++ {
|
||
|
p := image.Point{x, y}
|
||
|
if err := bc.SetPixel(p, opt.cellOpts...); err != nil {
|
||
|
return fmt.Errorf("lineLow bc.SetPixel(%v) => %v", p, err)
|
||
|
}
|
||
|
|
||
|
if diff > 0 {
|
||
|
y += stepY
|
||
|
diff -= 2 * deltaX
|
||
|
}
|
||
|
diff += 2 * deltaY
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// lineHigh draws a line whose vertical projection (end.Y - start.Y) is longer
|
||
|
// than its horizontal projection (end.X - start.X).
|
||
|
func lineHigh(bc *braille.Canvas, x0, y0, x1, y1 int, opt *brailleLineOptions) error {
|
||
|
deltaX := x1 - x0
|
||
|
deltaY := y1 - y0
|
||
|
|
||
|
stepX := 1
|
||
|
if deltaX < 0 {
|
||
|
stepX = -1
|
||
|
deltaX = -deltaX
|
||
|
}
|
||
|
|
||
|
diff := 2*deltaX - deltaY
|
||
|
x := x0
|
||
|
for y := y0; y <= y1; y++ {
|
||
|
p := image.Point{x, y}
|
||
|
if err := bc.SetPixel(p, opt.cellOpts...); err != nil {
|
||
|
return fmt.Errorf("lineHigh bc.SetPixel(%v) => %v", p, err)
|
||
|
}
|
||
|
|
||
|
if diff > 0 {
|
||
|
x += stepX
|
||
|
diff -= 2 * deltaY
|
||
|
}
|
||
|
diff += 2 * deltaX
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// abs returns the absolute value of x.
|
||
|
func abs(x int) int {
|
||
|
if x < 0 {
|
||
|
return -x
|
||
|
}
|
||
|
return x
|
||
|
}
|