From 61e84f934d44bfceb3b7e8838beae9f87f6c7a0b Mon Sep 17 00:00:00 2001 From: filifa Date: Mon, 8 Sep 2025 23:15:53 -0400 Subject: [PATCH] add shoelace command --- cmd/shoelace.go | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 cmd/shoelace.go diff --git a/cmd/shoelace.go b/cmd/shoelace.go new file mode 100644 index 0000000..0fc033b --- /dev/null +++ b/cmd/shoelace.go @@ -0,0 +1,123 @@ +/* +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 . +*/ +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", + Short: "Compute the area of a simple polygon from the vertex coordinates", + Long: `Compute the area of a simple polygon from the vertex coordinates.`, + 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 +}