Diff
diff --git a/border/main.go b/border/main.go
index af56702..caafda3 100644
--- a/border/main.go
+++ b/border/main.go
@@ -9,0 +10 @@ import (
+ ic "image/color"
@@ -193 +194 @@ func Draw(n *element.State, shelf *library.Shelf) {
- ctx.SetStrokeStyle(0, 0, 0, 255)
+ ctx.StrokeStyle = ic.RGBA{0, 0, 0, 255}
@@ -206 +207 @@ func Draw(n *element.State, shelf *library.Shelf) {
- n.Textures = append(n.Textures, shelf.Set(key, ctx.RGBA))
+ n.Textures = append(n.Textures, shelf.Set(key, ctx.Context))
@@ -219 +220 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawDashedBorder(ctx, side, border, s)
+ drawDashedBorder(ctx, side, border, s)
@@ -221 +222 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawDottedBorder(ctx, side, border, s)
+ drawDottedBorder(ctx, side, border, s)
@@ -223 +224 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawDoubleBorder(ctx, side, border, s)
+ drawDoubleBorder(ctx, side, border, s)
@@ -225 +226 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawGrooveBorder(ctx, side, border, s)
+ drawGrooveBorder(ctx, side, border, s)
@@ -227 +228 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawRidgeBorder(ctx, side, border, s)
+ drawRidgeBorder(ctx, side, border, s)
@@ -229 +230 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawInsetBorder(ctx, side, border, s)
+ drawInsetBorder(ctx, side, border, s)
@@ -231 +232 @@ func drawBorderSide(ctx *canvas.Canvas, side string, border element.BorderSide,
- // drawOutsetBorder(ctx, side, border, s)
+ drawOutsetBorder(ctx, side, border, s)
@@ -245,0 +247,3 @@ func isWidthComponent(component string, suffixes []string) bool {
+func degToRad(degrees float64) float64 {
+ return degrees * (math.Pi / 180.0)
+}
@@ -248,4 +252,2 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.SetFillStyle(border.Color.R, border.Color.G, border.Color.B, border.Color.A)
-
- width := float64(s.Width + s.Border.Left.Width + s.Border.Right.Width)
- height := float64(s.Height + s.Border.Top.Width + s.Border.Bottom.Width)
+ ctx.FillStyle = border.Color
+ ctx.StrokeStyle = border.Color
@@ -252,0 +255,2 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ width := s.Width + s.Border.Left.Width + s.Border.Right.Width
+ height := s.Height + s.Border.Top.Width + s.Border.Bottom.Width
@@ -254,2 +257,0 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.Save()
-
@@ -257,0 +260 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ // fmt.Println(1)
@@ -261 +263,0 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
-
@@ -262,0 +265 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ // fmt.Println(2)
@@ -265,2 +267,0 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.Translate(width, 0)
- ctx.Rotate(math.Pi / 2)
@@ -268,0 +270,2 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ ctx.Translate(float64(width), 0)
+ ctx.Rotate(math.Pi / 2)
@@ -269,0 +273 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ // fmt.Println(3)
@@ -272,2 +275,0 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.Translate(float64(width), float64(height))
- ctx.Rotate(math.Pi)
@@ -275,0 +278,2 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ ctx.Translate(float64(width), float64(height))
+ ctx.Rotate(math.Pi)
@@ -276,0 +281 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ // fmt.Println(4)
@@ -279,2 +283,0 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.Translate(0, float64(height))
- ctx.Rotate(-math.Pi / 2)
@@ -281,0 +285,42 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+
+ ctx.Translate(0, float64(height))
+ ctx.Rotate((-math.Pi / 2))
+ }
+ ctx.Fill()
+ // ctx.Stroke()
+ ctx.Reset()
+ ctx.ClosePath()
+}
+
+func genSolidBorder(ctx *canvas.Canvas, width float32, v1, v2 float64, border, side1, side2 element.BorderSide) {
+ s1w, s2w := float32(side1.Width), float32(side2.Width)
+ if s1w < 1 {
+ s1w = 1
+ }
+ if s2w < 1 {
+ s2w = 1
+ }
+ startAngleLeft := FindBorderStopAngle(image.Point{X: 0, Y: 0}, image.Point{X: int(s1w), Y: int(border.Width)}, image.Point{X: int(v1), Y: int(v1)}, v1)
+
+ ctx.Arc(v1, v1, v1, -math.Pi/2, startAngleLeft[0]-math.Pi, false) // top arc left
+ lineStart := ctx.Path[len(ctx.Path)-1]
+
+ anglePercent := ((startAngleLeft[0] - math.Pi) - (-math.Pi / 2)) / ((-math.Pi) - (-math.Pi / 2))
+ midBorderWidth := math.Max(math.Abs(float64(border.Width)-float64(s1w)), math.Min(float64(border.Width), float64(s1w)))
+
+ bottomBorderEnd := FindPointOnLine(image.Point{X: 0, Y: 0}, lineStart, midBorderWidth)
+
+ var lineEnd, parallelLineStart, parallelLineEnd image.Point
+ if v1 <= float64(border.Width) {
+ lineEnd = image.Point{X: int(s1w), Y: int(border.Width)}
+ parallelLineStart = lineEnd
+
+ } else {
+ controlPoint1 := FindQuadraticControlPoint(image.Point{X: int(v1), Y: int(border.Width)}, bottomBorderEnd, image.Point{X: int(s1w), Y: int(v1)}, anglePercent)
+
+ endPoints := canvas.QuadraticBezier(image.Point{X: int(v1), Y: int(border.Width)}, controlPoint1, image.Point{X: int(s1w), Y: int(v1)})
+
+ var index int
+ lineEnd, index, _ = FindClosestPoint(endPoints, bottomBorderEnd)
+ parallelLineStart = endPoints[0]
+ ctx.Path = append(ctx.Path, endPoints[:index]...)
@@ -284,2 +329,21 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- if border.Width == 1 {
- ctx.Stroke()
+ ctx.MoveTo(lineStart.X, lineStart.Y)
+ ctx.LineTo(lineEnd.X, lineEnd.Y) // cap the end of the arc (other side)
+
+ // These are the paralle lines pt 1
+ ctx.MoveTo(int(float64(width)-(v2)), 0)
+ ctx.LineTo(int(v1), 0)
+
+ // Move to start the second corner
+ ctx.MoveTo(int(float64(width)-(v2)), 0)
+
+ startAngleRight := FindBorderStopAngle(image.Point{X: int(width), Y: 0}, image.Point{X: int(width - s2w), Y: int(border.Width)}, image.Point{X: int(float64(width) - v2), Y: int(v2)}, v2)
+ ctx.Arc(float64(width)-v2, v2, v2, -math.Pi/2, startAngleRight[0]-math.Pi, false) // top arc Right
+ lineStart = ctx.Path[len(ctx.Path)-1]
+ anglePercent = math.Abs(((startAngleRight[0] - math.Pi) - (-math.Pi / 2)) / ((-math.Pi) - (-math.Pi / 2)))
+ midBorderWidth = math.Max(math.Abs(float64(border.Width)-float64(s2w)), math.Min(float64(border.Width), float64(s2w)))
+
+ bottomBorderEnd = FindPointOnLine(image.Point{X: int(width), Y: 0}, lineStart, midBorderWidth)
+
+ if v2 <= float64(border.Width) {
+ lineEnd = image.Point{X: int(width - s2w), Y: int(border.Width)}
+ parallelLineEnd = lineEnd
@@ -287 +351,50 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
- ctx.Stroke()
+ controlPoint := FindQuadraticControlPoint(image.Point{X: int(float64(width) - v2), Y: int(border.Width)}, bottomBorderEnd, image.Point{X: int(width - s2w), Y: int(v2)}, anglePercent)
+ endPoints := canvas.QuadraticBezier(image.Point{X: int(float64(width) - v2), Y: int(border.Width)}, controlPoint, image.Point{X: int(width - s2w), Y: int(v2)})
+ var index int
+ lineEnd, index, _ = FindClosestPoint(endPoints, bottomBorderEnd)
+ parallelLineEnd = endPoints[0]
+ ctx.Path = append(ctx.Path, endPoints[:index]...)
+ }
+
+ // These are the paralle lines pt 2
+ ctx.MoveTo(parallelLineStart.X, parallelLineStart.Y)
+ ctx.LineTo(parallelLineEnd.X, parallelLineEnd.Y)
+
+ ctx.MoveTo(lineStart.X, lineStart.Y)
+ ctx.LineTo(lineEnd.X, lineEnd.Y) // cap the end of the arc (other side)
+}
+
+func drawDashedBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ ctx.FillStyle = border.Color
+ ctx.StrokeStyle = border.Color
+
+ width := s.Width + s.Border.Left.Width + s.Border.Right.Width
+ height := s.Height + s.Border.Top.Width + s.Border.Bottom.Width
+ ctx.BeginPath()
+ switch side {
+ case "top":
+ v1 := math.Max(float64(s.Border.Radius.TopLeft), 1)
+ v2 := math.Max(float64(s.Border.Radius.TopRight), 1)
+ genDashedBorder(ctx, width, v1, v2, border, s.Border.Left, s.Border.Right, 10, 10)
+ case "right":
+ v1 := math.Max(float64(s.Border.Radius.TopRight), 1)
+ v2 := math.Max(float64(s.Border.Radius.BottomRight), 1)
+ genDashedBorder(ctx, height, v1, v2, border, s.Border.Top, s.Border.Bottom, 10, 10)
+
+ ctx.Translate(float64(width), 0)
+ ctx.Rotate(math.Pi / 2)
+ case "bottom":
+ v1 := math.Max(float64(s.Border.Radius.BottomLeft), 1)
+ v2 := math.Max(float64(s.Border.Radius.BottomRight), 1)
+ genDashedBorder(ctx, width, v2, v1, border, s.Border.Right, s.Border.Left, 10, 10)
+
+ ctx.Translate(float64(width), float64(height))
+ ctx.Rotate(math.Pi)
+ case "left":
+ // Top Right
+ v1 := math.Max(float64(s.Border.Radius.TopLeft), 1)
+ v2 := math.Max(float64(s.Border.Radius.BottomLeft), 1)
+ genDashedBorder(ctx, height, v2, v1, border, s.Border.Top, s.Border.Bottom, 10, 10)
+
+ ctx.Translate(0, float64(height))
+ ctx.Rotate(-math.Pi / 2)
@@ -288,0 +402,2 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
+ ctx.Fill()
+ // ctx.Stroke()
@@ -293,28 +408,54 @@ func drawSolidBorder(ctx *canvas.Canvas, side string, border element.BorderSide,
-func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, side1, side2 element.BorderSide) {
- s1w := math.Max(float64(side1.Width), 1)
- s2w := math.Max(float64(side2.Width), 1)
-
- ctx.SetLineWidth(1)
- // Top-left corner arc
- startAngleLeft := FindBorderStopAngle(
- image.Point{X: 0, Y: 0},
- image.Point{X: int(s1w), Y: int(border.Width)},
- image.Point{X: int(v1), Y: int(v1)},
- v1,
- )
- ctx.Arc(v1, v1, v1, startAngleLeft[0]-math.Pi, -math.Pi/2)
-
- // top line
- ctx.LineTo(width-v2, 0)
-
- // Top-right corner arc
- startAngleRight := FindBorderStopAngle(
- image.Point{X: int(width), Y: 0},
- image.Point{X: int(width - s2w), Y: int(border.Width)},
- image.Point{X: int(width - v2), Y: int(v2)},
- v2,
- )
- ctx.Arc(width-v2, v2, v2, -math.Pi/2, startAngleRight[0]-math.Pi)
-
- if border.Width == 1 {
- return
+func genDashedBorder(ctx *canvas.Canvas, width float32, v1, v2 float64, border, side1, side2 element.BorderSide, dashLength, gapLength float32) {
+ s1w, s2w := float32(side1.Width), float32(side2.Width)
+ if s1w < 1 {
+ s1w = 1
+ }
+ if s2w < 1 {
+ s2w = 1
+ }
+
+ // Helper function to draw dashed lines
+ drawDashedLine := func(start, end image.Point, dashLength, gapLength float32) {
+ lineLength := float32(math.Hypot(float64(end.X-start.X), float64(end.Y-start.Y)))
+ dashCount := int(lineLength / (dashLength + gapLength))
+ direction := image.Point{X: end.X - start.X, Y: end.Y - start.Y}
+
+ // Normalize the direction vector
+ directionLength := float32(math.Hypot(float64(direction.X), float64(direction.Y)))
+ direction = image.Point{X: int(float32(direction.X) / directionLength * dashLength), Y: int(float32(direction.Y) / directionLength * dashLength)}
+
+ for i := 0; i < dashCount; i++ {
+ dashStart := image.Point{X: start.X + direction.X*i, Y: start.Y + direction.Y*i}
+ dashEnd := image.Point{X: dashStart.X + direction.X, Y: dashStart.Y + direction.Y}
+
+ // Only draw the dash if it's within the bounds
+ if (i % 2) == 0 {
+ ctx.MoveTo(dashStart.X, dashStart.Y)
+ ctx.LineTo(dashEnd.X, dashEnd.Y)
+ }
+ }
+ }
+
+ startAngleLeft := FindBorderStopAngle(image.Point{X: 0, Y: 0}, image.Point{X: int(s1w), Y: int(border.Width)}, image.Point{X: int(v1), Y: int(v1)}, v1)
+
+ ctx.Arc(v1, v1, v1, -math.Pi/2, startAngleLeft[0]-math.Pi, false) // top arc left
+ lineStart := ctx.Path[len(ctx.Path)-1]
+
+ anglePercent := ((startAngleLeft[0] - math.Pi) - (-math.Pi / 2)) / ((-math.Pi) - (-math.Pi / 2))
+ midBorderWidth := math.Max(math.Abs(float64(border.Width)-float64(s1w)), math.Min(float64(border.Width), float64(s1w)))
+
+ bottomBorderEnd := FindPointOnLine(image.Point{X: 0, Y: 0}, lineStart, midBorderWidth)
+
+ var lineEnd, parallelLineStart, parallelLineEnd image.Point
+ if v1 <= float64(border.Width) {
+ lineEnd = image.Point{X: int(s1w), Y: int(border.Width)}
+ parallelLineStart = lineEnd
+ } else {
+ controlPoint1 := FindQuadraticControlPoint(image.Point{X: int(v1), Y: int(border.Width)}, bottomBorderEnd, image.Point{X: int(s1w), Y: int(v1)}, anglePercent)
+
+ endPoints := canvas.QuadraticBezier(image.Point{X: int(v1), Y: int(border.Width)}, controlPoint1, image.Point{X: int(s1w), Y: int(v1)})
+
+ var index int
+ lineEnd, index, _ = FindClosestPoint(endPoints, bottomBorderEnd)
+ parallelLineStart = endPoints[0]
+ ctx.Path = append(ctx.Path, endPoints[:index]...)
@@ -323,4 +464,28 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Right flat
- min := float64(border.Width)
- if min > s2w {
- min = s2w
+ // Draw dashed top border part 1
+ drawDashedLine(lineStart, lineEnd, dashLength, gapLength)
+
+ // These are the parallel lines part 1
+ ctx.MoveTo(int(float64(width)-(v2)), 0)
+ ctx.LineTo(int(v1), 0)
+
+ // Start the second corner
+ ctx.MoveTo(int(float64(width)-(v2)), 0)
+
+ startAngleRight := FindBorderStopAngle(image.Point{X: int(width), Y: 0}, image.Point{X: int(width - s2w), Y: int(border.Width)}, image.Point{X: int(float64(width) - v2), Y: int(v2)}, v2)
+ ctx.Arc(float64(width)-v2, v2, v2, -math.Pi/2, startAngleRight[0]-math.Pi, false) // top arc Right
+ lineStart = ctx.Path[len(ctx.Path)-1]
+ anglePercent = math.Abs(((startAngleRight[0] - math.Pi) - (-math.Pi / 2)) / ((-math.Pi) - (-math.Pi / 2)))
+ midBorderWidth = math.Max(math.Abs(float64(border.Width)-float64(s2w)), math.Min(float64(border.Width), float64(s2w)))
+
+ bottomBorderEnd = FindPointOnLine(image.Point{X: int(width), Y: 0}, lineStart, midBorderWidth)
+
+ if v2 <= float64(border.Width) {
+ lineEnd = image.Point{X: int(width - s2w), Y: int(border.Width)}
+ parallelLineEnd = lineEnd
+ } else {
+ controlPoint := FindQuadraticControlPoint(image.Point{X: int(float64(width) - v2), Y: int(border.Width)}, bottomBorderEnd, image.Point{X: int(width - s2w), Y: int(v2)}, anglePercent)
+ endPoints := canvas.QuadraticBezier(image.Point{X: int(float64(width) - v2), Y: int(border.Width)}, controlPoint, image.Point{X: int(width - s2w), Y: int(v2)})
+ var index int
+ lineEnd, index, _ = FindClosestPoint(endPoints, bottomBorderEnd)
+ parallelLineEnd = endPoints[0]
+ ctx.Path = append(ctx.Path, endPoints[:index]...)
@@ -329 +494,60 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- d := (math.Abs(float64(border.Width)-s2w) / 2) + min
+ // Draw dashed top border part 2
+ drawDashedLine(parallelLineStart, parallelLineEnd, dashLength, gapLength)
+
+ // Close the loop with dashed lines
+ ctx.MoveTo(lineStart.X, lineStart.Y)
+ ctx.LineTo(lineEnd.X, lineEnd.Y) // cap the end of the arc (other side)
+}
+
+func drawDottedBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // dotSize := int(border.Width)
+ // drawDot := func(x, y int) {
+ // for i := 0; i < dotSize; i++ {
+ // for j := 0; j < dotSize; j++ {
+ // img.Set(x+i, y+j, border.Color)
+ // }
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // for i := rect.Min.X; i < rect.Max.X; i += dotSize * 2 {
+ // drawDot(i, rect.Min.Y)
+ // }
+ // case "right":
+ // for i := rect.Min.Y; i < rect.Max.Y; i += dotSize * 2 {
+ // drawDot(rect.Max.X-dotSize, i)
+ // }
+ // case "bottom":
+ // for i := rect.Min.X; i < rect.Max.X; i += dotSize * 2 {
+ // drawDot(i, rect.Max.Y-dotSize)
+ // }
+ // case "left":
+ // for i := rect.Min.Y; i < rect.Max.Y; i += dotSize * 2 {
+ // drawDot(rect.Min.X, i)
+ // }
+ // }
+}
+
+func drawDoubleBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // innerOffset := int(border.Width / 3)
+ // outerOffset := innerOffset * 2
+ // drawLine := func(x1, y1, x2, y2, offset int) {
+ // for i := 0; i < innerOffset; i++ {
+ // drawLineHelper(img, x1, y1+i+offset, x2, y2+i+offset, border.Color)
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Min.Y, 0)
+ // drawLine(rect.Min.X, rect.Min.Y+outerOffset, rect.Max.X, rect.Min.Y+outerOffset, 0)
+ // case "right":
+ // drawLine(rect.Max.X-innerOffset, rect.Min.Y, rect.Max.X-innerOffset, rect.Max.Y, 0)
+ // drawLine(rect.Max.X-outerOffset, rect.Min.Y, rect.Max.X-outerOffset, rect.Max.Y, 0)
+ // case "bottom":
+ // drawLine(rect.Min.X, rect.Max.Y-innerOffset, rect.Max.X, rect.Max.Y-innerOffset, 0)
+ // drawLine(rect.Min.X, rect.Max.Y-outerOffset, rect.Max.X, rect.Max.Y-outerOffset, 0)
+ // case "left":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Min.X, rect.Max.Y, 0)
+ // drawLine(rect.Min.X+outerOffset, rect.Min.Y, rect.Min.X+outerOffset, rect.Max.Y, 0)
+ // }
+}
@@ -331 +555,23 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- xe, ye := EndPointFromDirection(width-(s2w), float64(border.Width), width-(s2w*2), float64(border.Width*2), d)
+func drawGrooveBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // shadowColor := ic.RGBA{border.Color.R / 2, border.Color.G / 2, border.Color.B / 2, border.Color.A}
+ // highlightColor := ic.RGBA{border.Color.R * 2 / 3, border.Color.G * 2 / 3, border.Color.B * 2 / 3, border.Color.A}
+ // drawLine := func(x1, y1, x2, y2 int, col ic.RGBA) {
+ // for i := 0; i < int(border.Width/2); i++ {
+ // drawLineHelper(img, x1, y1+i, x2, y2+i, col)
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Min.Y, shadowColor)
+ // drawLine(rect.Min.X, rect.Min.Y+int(border.Width/2), rect.Max.X, rect.Min.Y+int(border.Width/2), highlightColor)
+ // case "right":
+ // drawLine(rect.Max.X-int(border.Width/2), rect.Min.Y, rect.Max.X-int(border.Width/2), rect.Max.Y, shadowColor)
+ // drawLine(rect.Max.X-int(border.Width), rect.Min.Y, rect.Max.X-int(border.Width), rect.Max.Y, highlightColor)
+ // case "bottom":
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width/2), rect.Max.X, rect.Max.Y-int(border.Width/2), highlightColor)
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width), rect.Max.X, rect.Max.Y-int(border.Width), shadowColor)
+ // case "left":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Min.X, rect.Max.Y, highlightColor)
+ // drawLine(rect.Min.X+int(border.Width/2), rect.Min.Y, rect.Min.X+int(border.Width/2), rect.Max.Y, shadowColor)
+ // }
+}
@@ -333 +579,23 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- ctx.LineTo(xe, ye)
+func drawRidgeBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // shadowColor := ic.RGBA{border.Color.R / 2, border.Color.G / 2, border.Color.B / 2, border.Color.A}
+ // highlightColor := ic.RGBA{border.Color.R * 2 / 3, border.Color.G * 2 / 3, border.Color.B * 2 / 3, border.Color.A}
+ // drawLine := func(x1, y1, x2, y2 int, col ic.RGBA) {
+ // for i := 0; i < int(border.Width/2); i++ {
+ // drawLineHelper(img, x1, y1+i, x2, y2+i, col)
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Min.Y, highlightColor)
+ // drawLine(rect.Min.X, rect.Min.Y+int(border.Width/2), rect.Max.X, rect.Min.Y+int(border.Width/2), shadowColor)
+ // case "right":
+ // drawLine(rect.Max.X-int(border.Width/2), rect.Min.Y, rect.Max.X-int(border.Width/2), rect.Max.Y, highlightColor)
+ // drawLine(rect.Max.X-int(border.Width), rect.Min.Y, rect.Max.X-int(border.Width), rect.Max.Y, shadowColor)
+ // case "bottom":
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width/2), rect.Max.X, rect.Max.Y-int(border.Width/2), shadowColor)
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width), rect.Max.X, rect.Max.Y-int(border.Width), highlightColor)
+ // case "left":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Min.X, rect.Max.Y, shadowColor)
+ // drawLine(rect.Min.X+int(border.Width/2), rect.Min.Y, rect.Min.X+int(border.Width/2), rect.Max.Y, highlightColor)
+ // }
+}
@@ -335 +603,23 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Ellipse right
+func drawInsetBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // shadowColor := ic.RGBA{border.Color.R / 2, border.Color.G / 2, border.Color.B / 2, border.Color.A}
+ // highlightColor := ic.RGBA{border.Color.R * 2 / 3, border.Color.G * 2 / 3, border.Color.B * 2 / 3, border.Color.A}
+ // drawLine := func(x1, y1, x2, y2 int, col ic.RGBA) {
+ // for i := 0; i < int(border.Width/2); i++ {
+ // drawLineHelper(img, x1, y1+i, x2, y2+i, col)
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Min.Y, shadowColor)
+ // drawLine(rect.Min.X, rect.Min.Y+int(border.Width/2), rect.Max.X, rect.Min.Y+int(border.Width/2), highlightColor)
+ // case "right":
+ // drawLine(rect.Max.X-int(border.Width/2), rect.Min.Y, rect.Max.X-int(border.Width/2), rect.Max.Y, shadowColor)
+ // drawLine(rect.Max.X-int(border.Width), rect.Min.Y, rect.Max.X-int(border.Width), rect.Max.Y, highlightColor)
+ // case "bottom":
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width/2), rect.Max.X, rect.Max.Y-int(border.Width/2), highlightColor)
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width), rect.Max.X, rect.Max.Y-int(border.Width), shadowColor)
+ // case "left":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Min.X, rect.Max.Y, highlightColor)
+ // drawLine(rect.Min.X+int(border.Width/2), rect.Min.Y, rect.Min.X+int(border.Width/2), rect.Max.Y, shadowColor)
+ // }
+}
@@ -337 +627,30 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- xr, yr := CalculateInnerRadii(v2, s2w, float64(border.Width))
+func drawOutsetBorder(ctx *canvas.Canvas, side string, border element.BorderSide, s *element.State) {
+ // shadowColor := ic.RGBA{border.Color.R * 2 / 3, border.Color.G * 2 / 3, border.Color.B * 2 / 3, border.Color.A}
+ // highlightColor := ic.RGBA{border.Color.R / 2, border.Color.G / 2, border.Color.B / 2, border.Color.A}
+ // drawLine := func(x1, y1, x2, y2 int, col ic.RGBA) {
+ // for i := 0; i < int(border.Width/2); i++ {
+ // drawLineHelper(img, x1, y1+i, x2, y2+i, col)
+ // }
+ // }
+ // switch side {
+ // case "top":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Min.Y, highlightColor)
+ // drawLine(rect.Min.X, rect.Min.Y+int(border.Width/2), rect.Max.X, rect.Min.Y+int(border.Width/2), shadowColor)
+ // case "right":
+ // drawLine(rect.Max.X-int(border.Width/2), rect.Min.Y, rect.Max.X-int(border.Width/2), rect.Max.Y, highlightColor)
+ // drawLine(rect.Max.X-int(border.Width), rect.Min.Y, rect.Max.X-int(border.Width), rect.Max.Y, shadowColor)
+ // case "bottom":
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width/2), rect.Max.X, rect.Max.Y-int(border.Width/2), shadowColor)
+ // drawLine(rect.Min.X, rect.Max.Y-int(border.Width), rect.Max.X, rect.Max.Y-int(border.Width), highlightColor)
+ // case "left":
+ // drawLine(rect.Min.X, rect.Min.Y, rect.Min.X, rect.Max.Y, shadowColor)
+ // drawLine(rect.Min.X+int(border.Width/2), rect.Min.Y, rect.Min.X+int(border.Width/2), rect.Max.Y, highlightColor)
+ // }
+}
+
+// FindPointOnLine calculates the coordinates of a point that is at a specified distance
+// from the second point (P2) along the line defined by two points (P1 and P2).
+func FindPointOnLine(P1, P2 image.Point, distance float64) image.Point {
+ // Calculate the difference in x and y between P1 and P2
+ dx := float64(P2.X - P1.X)
+ dy := float64(P2.Y - P1.Y)
@@ -339 +658,2 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- sa := AngleInRadians(width-(s2w+xr), float64(border.Width)+yr, xe, ye)
+ // Calculate the length of the line segment between P1 and P2
+ lineLength := math.Sqrt(dx*dx + dy*dy)
@@ -341 +661,3 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- ctx.Ellipse(width-(s2w+xr), float64(border.Width)+yr, xr, yr, 0, sa, -math.Pi/2, false)
+ // Normalize the direction vector (dx, dy) to unit length
+ ux := dx / lineLength
+ uy := dy / lineLength
@@ -343 +665,10 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Find top of left ellipse to draw line to
+ // Calculate the coordinates of the new point at the given distance from P2
+ newX := float64(P2.X) + distance*ux
+ newY := float64(P2.Y) + distance*uy
+
+ // Return the new point as an image.Point (rounded to the nearest integer)
+ return image.Point{
+ X: int(math.Round(newX)),
+ Y: int(math.Round(newY)),
+ }
+}
@@ -345 +676,4 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- xr, yr = CalculateInnerRadii(v1, s1w, float64(border.Width))
+// Distance calculates the Euclidean distance between two points.
+func Distance(p1, p2 image.Point) float64 {
+ return math.Sqrt(float64((p2.X-p1.X)*(p2.X-p1.X) + (p2.Y-p1.Y)*(p2.Y-p1.Y)))
+}
@@ -347 +681,5 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- ellipseEndX, ellipseEndY := EllipsePoint(s1w+xr, float64(border.Width)+yr, xr, yr, 0, -math.Pi/2)
+func FindClosestPoint(points []image.Point, target image.Point) (closestPoint image.Point, closestIndex int, minDistance float64) {
+ if len(points) == 0 {
+ // Return a zero-value point, -1 index, and infinity if the slice is empty
+ return image.Point{}, -1, math.Inf(1)
+ }
@@ -349,2 +687,3 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Bottom Line
- ctx.LineTo(ellipseEndX, ellipseEndY)
+ closestPoint = points[0]
+ closestIndex = 0
+ minDistance = Distance(points[0], target)
@@ -352,4 +691,7 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Left flat
- min = float64(border.Width)
- if min > s1w {
- min = s1w
+ for i, point := range points[1:] {
+ distance := Distance(point, target)
+ if distance < minDistance {
+ minDistance = distance
+ closestPoint = point
+ closestIndex = i + 1 // Adjust for the slice offset by 1
+ }
@@ -358,2 +700,2 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- d = (math.Abs(float64(border.Width)-s1w) / 2) + min
- xe, ye = EndPointFromDirection((s1w * 2), float64(border.Width*2), s1w, float64(border.Width), d)
+ return closestPoint, closestIndex, minDistance
+}
@@ -361 +703,17 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- sa = AngleInRadians(s1w+xr, float64(border.Width)+yr, xe, ye)
+// FindPointGivenAngleDistance calculates the coordinates of a point given the starting point,
+// an angle in radians, and a distance from the starting point.
+func FindPointGivenAngleDistance(start image.Point, angle float64, distance float64) image.Point {
+ // Calculate the change in x and y based on the angle and distance
+ deltaX := distance * math.Cos(angle)
+ deltaY := distance * math.Sin(angle)
+
+ // Calculate the new point coordinates
+ newX := float64(start.X) + deltaX
+ newY := float64(start.Y) + deltaY
+
+ // Return the new point as an image.Point (rounded to the nearest integer)
+ return image.Point{
+ X: int(math.Round(newX)),
+ Y: int(math.Round(newY)),
+ }
+}
@@ -363,2 +721,5 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Ellipse left
- ctx.Ellipse(s1w+xr, float64(border.Width)+yr, xr, yr, 0, -math.Pi/2, sa, false)
+func FindQuadraticControlPoint(P0, P2, P3 image.Point, t1 float64) image.Point {
+ // Calculate (1-t1)^2, 2(1-t1)t1, and t1^2
+ oneMinusT1 := 1 - t1
+ oneMinusT1Squared := oneMinusT1 * oneMinusT1
+ t1Squared := t1 * t1
@@ -366,2 +727,5 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
- // Left flat line
- ctx.ClosePath()
+ // Calculate the control point P1
+ P1 := image.Point{
+ X: int((float64(P2.X) - oneMinusT1Squared*float64(P0.X) - t1Squared*float64(P3.X)) / (2 * oneMinusT1 * t1)),
+ Y: int((float64(P2.Y) - oneMinusT1Squared*float64(P0.Y) - t1Squared*float64(P3.Y)) / (2 * oneMinusT1 * t1)),
+ }
@@ -368,0 +733,25 @@ func genSolidBorder(ctx *canvas.Canvas, width float64, v1, v2 float64, border, s
+ return P1
+}
+
+func FindCubicControlPoints(P0, P3, PA, PB image.Point, tA, tB float64) (P1, P2 image.Point) {
+ // Coefficients for the equations
+ A1 := 3 * tA * (1 - tA) * (1 - tA)
+ A2 := 3 * tB * (1 - tB) * (1 - tB)
+ B1 := 3 * (1 - tA) * tA * tA
+ B2 := 3 * (1 - tB) * tB * tB
+
+ // Solve for P1 and P2 using the given equations for PA and PB
+ P1X := (float64(PA.X) - (1-tA)*(1-tA)*(1-tA)*float64(P0.X) - tA*tA*tA*float64(P3.X) -
+ B1/B2*(float64(PB.X)-(1-tB)*(1-tB)*(1-tB)*float64(P0.X)-tB*tB*tB*float64(P3.X))) /
+ (A1 - B1*B1/B2)
+ P1Y := (float64(PA.Y) - (1-tA)*(1-tA)*(1-tA)*float64(P0.Y) - tA*tA*tA*float64(P3.Y) -
+ B1/B2*(float64(PB.Y)-(1-tB)*(1-tB)*(1-tB)*float64(P0.Y)-tB*tB*tB*float64(P3.Y))) /
+ (A1 - B1*B1/B2)
+
+ P2X := (float64(PB.X) - (1-tB)*(1-tB)*(1-tB)*float64(P0.X) - tB*tB*tB*float64(P3.X) - A2*P1X) / B2
+ P2Y := (float64(PB.Y) - (1-tB)*(1-tB)*(1-tB)*float64(P0.Y) - tB*tB*tB*float64(P3.Y) - A2*P1Y) / B2
+
+ P1 = image.Point{X: int(P1X), Y: int(P1Y)}
+ P2 = image.Point{X: int(P2X), Y: int(P2Y)}
+
+ return P1, P2
@@ -371,0 +761 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
+ // Calculate the difference between the points
@@ -374,0 +765 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
+ // Calculate the angle using Atan2
@@ -375,0 +767 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
+
@@ -378 +769,0 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
- // Validate points
@@ -383,8 +774,4 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
- var result []float64
- for _, p := range points {
- // Check if the point lies within the arc's visible range
- dx = float64(circleCenter.X - p.X)
- dy = float64(circleCenter.Y - p.Y)
- angle = math.Atan2(dy, dx)
- result = append(result, angle)
- }
+ // Convert the angle from radians to degrees if needed
+
+ dx = float64(circleCenter.X - points[0].X)
+ dy = float64(circleCenter.Y - points[0].Y)
@@ -392 +779,7 @@ func FindBorderStopAngle(origin, crossPoint, circleCenter image.Point, radius fl
- return result
+ angle2 := math.Atan2(dy, dx)
+
+ dx = float64(circleCenter.X - points[1].X)
+ dy = float64(circleCenter.Y - points[1].Y)
+
+ angle3 := math.Atan2(dy, dx)
+ return []float64{angle2, angle3}
@@ -395,0 +789,3 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
+ // Parametric equations for the line: x = x0 + t * cos(theta), y = y0 + t * sin(theta)
+ // Substitute these into the circle equation: (x - h)^2 + (y - k)^2 = r^2
+
@@ -398,0 +795 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
+ // Convert image.Point to float64 for calculations
@@ -401,0 +799 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
+ // Coefficients for the quadratic equation At^2 + Bt + C = 0
@@ -406,2 +804 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
- // Use a small epsilon to handle near-tangent cases
- const epsilon = 1e-10
+ // Discriminant
@@ -410,2 +807,3 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
- if discriminant < -epsilon {
- return nil // No intersection
+ // No intersection
+ if discriminant < 0 {
+ return nil
@@ -414,12 +812 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
- if math.Abs(discriminant) < epsilon {
- // Tangent case: return one intersection point
- t := -B / (2 * A)
- return []image.Point{
- {
- X: int(math.Round(x0 + t*cosTheta)),
- Y: int(math.Round(y0 + t*sinTheta)),
- },
- }
- }
-
- // Two intersection points
+ // Calculate the two solutions for t
@@ -429 +816,2 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
- return []image.Point{
+ // Calculate the intersection points
+ intersectionPoints := []image.Point{
@@ -439,52 +826,0 @@ func LineCircleIntersection(lineStart image.Point, angle float64, circleCenter i
-}
-
-// EndPointFromDirection computes an endpoint that is `distance` units
-// away from the start point in the direction of the line defined by the
-// start and midpoint.
-func EndPointFromDirection(x0, y0, xm, ym, distance float64) (xe, ye float64) {
- // Compute direction vector from start to midpoint
- dx := (xm - x0)
- dy := (ym - y0)
-
- // Handle the case where start and midpoint are the same
- if dx == 0 && dy == 0 {
- // Return a point directly along the x-axis for simplicity
- return x0 + distance, y0
- }
-
- // Normalize the direction vector
- length := math.Sqrt(dx*dx + dy*dy)
- ux := dx / length
- uy := dy / length
-
- // Calculate the endpoint
- xe = x0 + ux*distance
- ye = y0 + uy*distance
- return
-}
-
-// CalculateInnerRadii calculates the inner radii of a box corner given the outer radii and border widths.
-func CalculateInnerRadii(outerRadius, borderWidthLeft, borderWidthTop float64) (float64, float64) {
- innerRadiusX := outerRadius - borderWidthLeft
- innerRadiusY := outerRadius - borderWidthTop
-
- // Clamp the inner radii to zero if they go negative
- if innerRadiusX < 0 {
- innerRadiusX = 0
- }
- if innerRadiusY < 0 {
- innerRadiusY = 0
- }
-
- return innerRadiusX, innerRadiusY
-}
-
-// AngleInRadians calculates the angle from one point to another in radians
-func AngleInRadians(p1X, p1Y, p2X, p2Y float64) float64 {
- // Calculate the differences in x and y
- dx := p2X - p1X
- dy := p2Y - p1Y
-
- // Use math.Atan2 to calculate the angle in radians
- return math.Atan2(dy, dx)
-}
@@ -492,29 +828,3 @@ func AngleInRadians(p1X, p1Y, p2X, p2Y float64) float64 {
-func EllipsePoint(x, y, radiusX, radiusY, rotation, angle float64) (float64, float64) {
- // Calculate the point on the ellipse without rotation
- ellipseX := radiusX * math.Cos(angle)
- ellipseY := radiusY * math.Sin(angle)
-
- // Apply rotation to the point
- rotatedX := ellipseX*math.Cos(rotation) - ellipseY*math.Sin(rotation)
- rotatedY := ellipseX*math.Sin(rotation) + ellipseY*math.Cos(rotation)
-
- // Translate the point to the center of the ellipse
- finalX := rotatedX + x
- finalY := rotatedY + y
-
- return finalX, finalY
-}
-
-// AngleBetweenBorders calculates the arc angle of a rounded corner
-// formed by two border segments.
-func AngleBetweenBorders(angle1, angle2, borderWidth1, borderWidth2, radius float64) (arcAngle float64) {
- // Convert angles from degrees to radians
- angle1Rad := angle1 * math.Pi / 180.0
- angle2Rad := angle2 * math.Pi / 180.0
-
- // Calculate the intersection angle between the two borders
- intersectionAngle := math.Abs(angle1Rad - angle2Rad)
-
- // Ensure the angle is within [0, π]
- if intersectionAngle > math.Pi {
- intersectionAngle = 2*math.Pi - intersectionAngle
+ // If the discriminant is zero, the line is tangent to the circle, and there's only one intersection point.
+ if discriminant == 0 {
+ return []image.Point{intersectionPoints[0]}
@@ -523,9 +833 @@ func AngleBetweenBorders(angle1, angle2, borderWidth1, borderWidth2, radius floa
- // Adjust the arc span considering border widths
- adjustment1 := math.Atan(borderWidth1 / radius)
- adjustment2 := math.Atan(borderWidth2 / radius)
-
- // Calculate the resulting arc angle
- arcAngleRad := intersectionAngle - adjustment1 - adjustment2
- // arcAngle = arcAngleRad * 180.0 / math.Pi // Convert back to degrees
-
- return arcAngleRad
+ return intersectionPoints