mathtools/cmd/shoelace.go

132 lines
3.0 KiB
Go

/*
Copyright © 2025 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
)
var shoelaceFile string
type point struct {
x float64
y float64
}
func splitLines(file *os.File) []string {
slice := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
slice = append(slice, scanner.Text())
}
return slice
}
func readFromFile(filepath string) ([]point, error) {
points := make([]point, 0)
file, err := os.Open(filepath)
if err != nil {
return points, err
}
defer file.Close()
lines := splitLines(file)
for _, line := range lines {
fields := strings.Fields(line)
x, err := strconv.ParseFloat(fields[0], 64)
if err != nil {
return points, err
}
y, err := strconv.ParseFloat(fields[1], 64)
if err != nil {
return points, err
}
points = append(points, point{x, y})
}
return points, nil
}
func area(points []point) float64 {
total := float64(0)
n := len(points)
for i, p := range points {
q := points[(i+1)%n]
total += (p.y + q.y) * (p.x - q.x)
}
return total / 2
}
func shoelace(cmd *cobra.Command, args []string) {
var coordinates []point
var err error
if shoelaceFile != "" {
coordinates, err = readFromFile(shoelaceFile)
if err != nil {
cobra.CheckErr(err)
}
} else {
cobra.CheckErr("filename required")
}
fmt.Println(area(coordinates))
}
// shoelaceCmd represents the shoelace command
var shoelaceCmd = &cobra.Command{
Use: "shoelace -f FILE",
Short: "Compute the area of a simple polygon from the vertex coordinates",
Long: `Compute the area of a simple polygon from the vertex coordinates.
Put each point on its own line, with each coordinate separated by whitespace. For example, a file with
1 6
3 1
7 2
4 4
8 5
will output an area of 16.5.`,
Run: shoelace,
}
func init() {
rootCmd.AddCommand(shoelaceCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// shoelaceCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// shoelaceCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
shoelaceCmd.Flags().StringVarP(&shoelaceFile, "file", "f", "", "file with coordinates")
// TODO: add flag for inputting coordinates from cli
}