@@ -402,7 +402,9 @@ function Diag:has_point(x, y)
402402 end
403403end
404404
405- Line = defclass (Line , Shape )
405+ LineDrawer = defclass (LineDrawer , Shape )
406+
407+ Line = defclass (Line , LineDrawer )
406408Line .ATTRS {
407409 name = " Line" ,
408410 extra_points = { { label = " Curve Point" }, { label = " Second Curve Point" } },
@@ -431,18 +433,19 @@ function Line:init()
431433 }
432434end
433435
434- function Line :plot_bresenham (x0 , y0 , x1 , y1 , thickness )
436+ function LineDrawer :plot_bresenham (x0 , y0 , x1 , y1 , thickness )
435437 local dx = math.abs (x1 - x0 )
436438 local dy = math.abs (y1 - y0 )
437439 local sx = x0 < x1 and 1 or - 1
438440 local sy = y0 < y1 and 1 or - 1
439- local err = dx - dy
440441 local e2 , x , y
441442
442443 for i = 0 , thickness - 1 do
443444 x = x0
444445 y = y0 + i
445- while true do
446+ local err = dx - dy
447+ local p = math.max (dx , dy )
448+ while p >= 0 do
446449 for j = - math.floor (thickness / 2 ), math.ceil (thickness / 2 ) - 1 do
447450 if not self .arr [x + j ] then self .arr [x + j ] = {} end
448451 if not self .arr [x + j ][y ] then
@@ -451,7 +454,7 @@ function Line:plot_bresenham(x0, y0, x1, y1, thickness)
451454 end
452455 end
453456
454- if x == x1 and y == y1 + i then
457+ if sx * x >= sx * x1 and sy * y >= sy * ( y1 + i ) then
455458 break
456459 end
457460
@@ -466,6 +469,7 @@ function Line:plot_bresenham(x0, y0, x1, y1, thickness)
466469 err = err + dx
467470 y = y + sy
468471 end
472+ p = p - 1
469473 end
470474 end
471475
@@ -684,7 +688,137 @@ function FreeForm:point_in_polygon(x, y)
684688 return inside
685689end
686690
691+ Star = defclass (Star , LineDrawer )
692+ Star .ATTRS {
693+ name = " Star" ,
694+ texture_offset = 25 ,
695+ button_chars = util .make_ascii_button (' *' , 15 ),
696+ extra_points = { { label = " Main Axis" } }
697+ }
698+
699+ function Star :init ()
700+ self .options = {
701+ hollow = {
702+ name = " Hollow" ,
703+ type = " bool" ,
704+ value = false ,
705+ key = " CUSTOM_H" ,
706+ },
707+ thickness = {
708+ name = " Line thickness" ,
709+ type = " plusminus" ,
710+ value = 1 ,
711+ enabled = { " hollow" , true },
712+ min = 1 ,
713+ max = function (shape ) if not shape .height or not shape .width then
714+ return nil
715+ else
716+ return math.ceil (math.min (shape .height , shape .width ) / 2 )
717+
718+ end
719+ end ,
720+ keys = { " CUSTOM_T" , " CUSTOM_SHIFT_T" },
721+ },
722+ total_points = {
723+ name = " Total points" ,
724+ type = " plusminus" ,
725+ value = 5 ,
726+ min = 3 ,
727+ max = 100 ,
728+ keys = { " CUSTOM_B" , " CUSTOM_SHIFT_B" },
729+ },
730+ next_point_offset = {
731+ name = " Next point offset" ,
732+ type = " plusminus" ,
733+ value = 2 ,
734+ min = 1 ,
735+ max = 100 ,
736+ keys = { " CUSTOM_N" , " CUSTOM_SHIFT_N" },
737+ },
738+ }
739+ end
740+
741+ function Star :has_point (x , y )
742+ if 1 < ((x - self .center .x ) / self .center .x ) ^ 2 + ((y - self .center .y ) / self .center .y ) ^ 2 then return false end
743+
744+ local inside = 0
745+ for l = 1 , self .options .total_points .value do
746+ if x * self .lines [l ].slope .x - self .lines [l ].intercept .x < y * self .lines [l ].slope .y - self .lines [l ].intercept .y then
747+ inside = inside + 1
748+ else
749+ inside = inside - 1
750+ end
751+ end
752+ return self .threshold > 0 and inside > self .threshold or inside < self .threshold
753+ end
754+
755+ function vmagnitude (point )
756+ return math.sqrt (point .x * point .x + point .y * point .y )
757+ end
758+
759+ function vnormalize (point )
760+ local magnitude = vmagnitude (point )
761+ return { x = point .x / magnitude , y = point .y / magnitude }
762+ end
763+
764+ function add_offset (coord , offset )
765+ return coord + (offset > 0 and math.floor (offset + 0.5 ) or math.ceil (offset - 0.5 ))
766+ end
767+
768+ function Star :update (points , extra_points )
769+ self .num_tiles = 0
770+ self .points = copyall (points )
771+ self .arr = {}
772+ if # points < self .min_points then return end
773+ self .threshold = self .options .total_points .value - 2 * self .options .next_point_offset .value
774+ local top_left , bot_right = self :get_point_dims ()
775+ self .height = bot_right .y - top_left .y
776+ self .width = bot_right .x - top_left .x
777+ if self .height == 1 or self .width == 1 then return end
778+ self .center = { x = self .width * 0.5 , y = self .height * 0.5 }
779+ local axes = {}
780+
781+ axes [1 ] = (# extra_points > 0 ) and { x = extra_points [1 ].x - self .center .x - top_left .x , y = extra_points [1 ].y - self .center .y - top_left .y } or { x = 0 , y = - self .center .y }
782+ if vmagnitude (axes [1 ]) < 0.5 then axes [1 ].y = - self .center .y end
783+ axes [1 ] = vnormalize (axes [1 ])
784+
785+ for a = 2 , self .options .total_points .value do
786+ local angle = math.pi * (a - 1.0 ) * 2.0 / self .options .total_points .value
787+ axes [a ] = { x = math.cos (angle ) * axes [1 ].x - math.sin (angle ) * axes [1 ].y , y = math.sin (angle ) * axes [1 ].x + math.cos (angle ) * axes [1 ].y }
788+ end
789+
790+ local thickness = 1
791+ if self .options .hollow .value then
792+ thickness = self .options .thickness .value
793+ end
794+
795+ self .lines = {}
796+ for l = 1 , self .options .total_points .value do
797+ local p1 = { x = self .center .x + axes [l ].x * self .width * 0.5 , y = self .center .y + axes [l ].y * self .height * 0.5 }
798+ local next_axis = axes [(l - 1 + self .options .next_point_offset .value ) % self .options .total_points .value + 1 ]
799+ local p2 = { x = self .center .x + next_axis .x * self .width * 0.5 , y = self .center .y + next_axis .y * self .height * 0.5 }
800+ self .lines [l ] = { slope = { x = p2 .y - p1 .y , y = p2 .x - p1 .x }, intercept = { x = (p2 .y - p1 .y ) * p1 .x , y = (p2 .x - p1 .x ) * p1 .y } }
801+ self :plot_bresenham (add_offset (top_left .x , p1 .x ), add_offset (top_left .y , p1 .y ), add_offset (top_left .x , p2 .x ), add_offset (top_left .y , p2 .y ), thickness )
802+ self :plot_bresenham (add_offset (top_left .x , p2 .x ), add_offset (top_left .y , p2 .y ), add_offset (top_left .x , p1 .x ), add_offset (top_left .y , p1 .y ), thickness )
803+ end
804+
805+ if not self .options .hollow .value or self .invert then
806+ for x = top_left .x , bot_right .x do
807+ if not self .arr [x ] then self .arr [x ] = {} end
808+ for y = top_left .y , bot_right .y do
809+ local value = self .arr [x ][y ] or (not self .options .hollow .value and self :has_point (x - top_left .x , y - top_left .y ))
810+ if self .invert then
811+ self .arr [x ][y ] = not value
812+ else
813+ self .arr [x ][y ] = value
814+ end
815+
816+ self .num_tiles = self .num_tiles + (self .arr [x ][y ] and 1 or 0 )
817+ end
818+ end
819+ end
820+ end
687821-- module users can get shapes through this global, shape option values
688822-- persist in these as long as the module is loaded
689823-- idk enough lua to know if this is okay to do or not
690- all_shapes = { Rectangle {}, Ellipse {}, Rows {}, Diag {}, Line {}, FreeForm {} }
824+ all_shapes = { Rectangle {}, Ellipse {}, Rows {}, Diag {}, Line {}, FreeForm {}, Star {} }
0 commit comments