Navigation

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Users
    • Groups
    • Solved
    • Unsolved
    • Search
    1. Home
    2. monomonnik
    3. Posts
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    Posts made by monomonnik

    • Round Corners

      The code responsible for the rounding of the paths in a small animation I made.

      DEBUG = False
      
      # A path has points.
      # A point has only an anchor [(x,y)], or has an anchor and in and out point [(x,y), (x,y), (x,y)].
      # To close a path, repeat the first point at the last position.
      jagged_line =[[(500,210)], [(400,250)], [(600,350)], [(450,600)], [(500,790)]]
      rectangle =[[(220,220)], [(780,220)], [(780,780)], [(220,780)], [(220,220)]]
      circle = [[(500.0, 800.0), (335.0, 800.0), (665.0, 800.0)], [(800.0, 500.0), (800.0, 665.0), (800.0, 335.0)], [(500.0, 200.0), (665.0, 200.0), (335.0, 200.0)], [(200.0, 500.0), (200.0, 335.0), (200.0, 665.0)], [(500.0, 800.0), (335.0, 800.0), (665.0, 800.0)]]
      
      
      def draw(path):
          bez = BezierPath()
          bez.moveTo(path[0][0])
          for i in range(1, len(path)):
              p0 = path[i-1]
              p1 = path[i]
              if len(p0) == 1 and len(p1) == 1:
                  # straight line between points
                  bez.lineTo(p1[0])
              elif len(p0) == 3 and len(p1) == 1:
                  # from curve point to straight point
                  bez.curveTo(p0[2], p1[0], p1[0])
              elif len(p0) == 1 and len(p1) == 3:
                  # from straight point to curve point
                  bez.curveTo(p0[0], p1[1], p1[0])
              elif len(p0) == 3 and len(p1) == 3:
                  # curve point on both sides
                  bez.curveTo(p0[2], p1[1], p1[0])
          if path[-1] == path[0]:
              bez.closePath()
          drawPath(bez)
      
      
      def round_corners(path, roundness):
          if len(path) > 2:
              new_path = []
              new_path.append(path[0])
              new_path.append(path[1])
              for i in range(1, len(path) - 1):
                  p1 = new_path[i - 1]
                  p2 = path[i]
                  p3 = path[i + 1]
                  p1, p2, p3 = round_segment(p1, p2, p3, roundness)
                  new_path[i - 1] = p1
                  new_path[i] = p2
                  new_path.append(p3)
              # If the path is closed, we need (the handle of) the first point 
              if path[-1] == path[0]:
                  p1 = new_path[-2]
                  p2 = path[0]
                  p3 = new_path[1]
                  p1, p2, p3 = round_segment(p1, p2, p3, roundness)
                  new_path[-2] = p1
                  new_path[-1] = p2
                  new_path[0] = p2
              return new_path
          else:
              return path
      
      
      def round_segment(p1, p2, p3, roundness):
          if roundable(p1, p2, p3):
              p2_in, p2_out = create_handles(p1[0], p2[0], p3[0], roundness)
              p2 = [p2[0], p2_in, p2_out]
          return p1, p2, p3
      
      
      def roundable(p1, p2, p3):
          # If two of the three points are in the same spot,
          # we can’t calculate a curve between two points.
          p1_anchor = p1[0]
          p2_anchor = p2[0]
          p3_anchor = p3[0]
          d12 = distance_between(p1_anchor, p2_anchor)
          d23 = distance_between(p2_anchor, p3_anchor)
          if d12 == 0 or d23 == 0:
              return False
          else:
              return True
      
      
      def create_handles(A, B, C, smoothness):
          # A is the point before point B
          # B is the point to create the handles for
          # C is the point after point B
          d_AB = distance_between(A, B)
          d_BC = distance_between(B, C)
          # Create an isosceles triangle A, B, p4 based on triangle A, B, C.
          # Side B, p4 is the same length as side A, B.
          # Side B, p4 has the same direction as side B, C
          p4_x = ((C[0] - B[0]) * (d_AB / d_BC)) + B[0]
          p4_y = ((C[1] - B[1]) * (d_AB / d_BC)) + B[1]
          p4 = (p4_x, p4_y)
          
          if DEBUG:
              draw_handle(B, p4)
          
          # Calculate a point p5 on the base of the isosceles triangle,
          # exactly in between A and B.
          p5_x = A[0] + ((p4[0] - A[0]) / 2)
          p5_y = A[1] + ((p4[1] - A[1]) / 2)
          p5 = (p5_x, p5_y)
      
          if DEBUG:
              draw_point(p5, 10)
              draw_line(A, p4)
      
          # The line from the top of the isosceles triangle B to 
          # the point p5 on the base of that triangle
          # divides the corner p1, p2, p3 in two equal parts
      
          if DEBUG:
              draw_line(B, p5)
      
          # Direction of the handles is perpendicular
          vx = p5[0] - B[0]
          vy = p5[1] - B[1]
          handle_vx = 0
          handle_vy = 0 
          if vx == 0 and vy == 0:
              # The three points are on one line, so there will never be a curve.
              pass
          elif vx == 0:
              # prevent a possible division by 0
              handle_vx = 1
              handle_vy = 0
          elif vy == 0:
              # prevent a possible division by 0
              handle_vx = 0
              handle_vy = 1
          elif abs(vx) < abs(vy):
              handle_vx = 1
              handle_vy = vx / vy
          else:
              handle_vx = vy / vx
              handle_vy = 1
      
          # Define handles
          handle_a = (B[0] + handle_vx, B[1] - handle_vy)
          handle_b = (B[0] - handle_vx, B[1] + handle_vy)
      
          # The handle closest to point A will be the incoming handle of point B
          d_ha_A = distance_between(A, handle_a)
          d_hb_A = distance_between(A, handle_b)
      
          # I have to make this better. Also, where’s that 0.8 coming from? What was I thinking?
          incoming_handle_lenght = d_AB * smoothness
          outgoing_handle_length = d_BC * smoothness
          total_handle_length = incoming_handle_lenght + outgoing_handle_length
          max_handle_length = 0.8 * total_handle_length
          if incoming_handle_lenght > max_handle_length:
              outgoing_handle_length += incoming_handle_lenght - max_handle_length
              incoming_handle_lenght = max_handle_length
          if outgoing_handle_length > max_handle_length:
              incoming_handle_lenght += outgoing_handle_length - max_handle_length
              outgoing_handle_length = max_handle_length
          
          # finally, the in and out points
          if d_ha_A < d_hb_A:
              B_incoming = (B[0] + handle_vx * incoming_handle_lenght, B[1] - handle_vy * incoming_handle_lenght)
              B_outgoing = (B[0] - handle_vx * outgoing_handle_length, B[1] + handle_vy * outgoing_handle_length) 
          else:
              B_incoming = (B[0] - handle_vx * incoming_handle_lenght, B[1] + handle_vy * incoming_handle_lenght) 
              B_outgoing = (B[0] + handle_vx * outgoing_handle_length, B[1] - handle_vy * outgoing_handle_length)
      
          if DEBUG:
              draw_point(B_incoming, 6)
              draw_point(B_outgoing, 6)
              draw_line(B, B_incoming)
              draw_line(B, B_outgoing)
              draw_line(A, B)
      
          return B_incoming, B_outgoing
      
      
      def distance_between(p1, p2):
          dx = p2[0] - p1[0]
          dy = p2[1] - p1[1]
          return pow((dx * dx + dy * dy), 0.5)
      
      
      def draw_point(point, size):
          with savedState():
              fill(0, 0.7, 1)
              stroke(None)
              oval(point[0] - 0.5 * size, point[1] - 0.5 * size, size, size)
      
      
      def draw_line(a, b):
          with savedState():
              fill(None)
              strokeWidth(1)
              stroke(0, 0.7, 1)
              line((100, 100), (900, 900))
              line(a, b)
      
      
      def draw_handle(p, h):
          draw_point(p, 9)
          draw_line(p, h)
      
      
      fill(None)
      
      # Drawing the original shape and the rounded shape, slightly thicker.
      stroke(1, 0, 0)
      strokeWidth(2)
      draw(jagged_line)
      rounded_line = round_corners(jagged_line, 0.4)
      strokeWidth(4)
      draw(rounded_line)
      
      # A rounding of 0.28 seems to get me as close to a circle as I can get.
      stroke(0, 1, 0)
      strokeWidth(2)
      draw(rectangle)
      rounded_rectangle = round_corners(rectangle, 0.28)
      strokeWidth(4)
      draw(rounded_rectangle)
      
      # Rounding an oval by zero results in a rhombus.
      stroke(0, 0, 1)
      strokeWidth(2)
      draw(circle)
      rounded_circle = round_corners(circle, 0)
      strokeWidth(4)
      draw(rounded_circle)
      
      

      export.png

      posted in Code snippets
      monomonnik
      monomonnik
    • RE: How to code a gif that make the text flash

      flashing.gif

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: How to code a gif that make the text flash

      This will make an animated gif with two frames, one frame with text, the other without, thus creating the illusion of flashing text.

      w = 500
      h = 500
      
      newPage(w,h)
      # Draw a black background
      fill(0)
      rect(0, 0, w, h)
      # Draw gray text
      fill(0.2)
      font("Times-Italic", 190)
      text("FLASH", (w/2, h/2), align="center")
      
      newPage(w,h)
      # Draw a black background
      fill(0)
      rect(0, 0, width(), height())
      
      saveImage("rapid flashing.gif")
      
      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?

      @pi That is exactly my solution. It’s not elegant, but it’s quick and it works.

      Below is the Applescript I used. If you name your paths in Illustrator and put the names in pathNames it will copy the coordinates of the points and corresponding control points to an array you can paste in DrawBot.

      on run
      	set pathNames to {"segment_concave"}
      	set pathsArray to "segment = ["
      	
      	tell application "Adobe Illustrator"
      		activate
      		set docRef to current document
      		
      		repeat with pathName in pathNames
      			set pointsArray to "["
      			set itemRef to path item pathName of docRef
      			
      			set pointsRef to every path point of itemRef
      			repeat with pointRef in pointsRef
      				-- anchor point
      				set pointAsString to "["
      				set x to my string_float(item 1 of anchor of pointRef)
      				set y to my string_float(item 2 of anchor of pointRef)
      				set pointAsString to pointAsString & "(" & x & "," & y & "), "
      				
      				-- left bcp
      				set x to my string_float(item 1 of left direction of pointRef)
      				set y to my string_float(item 2 of left direction of pointRef)
      				set pointAsString to pointAsString & "(" & x & "," & y & "), "
      				
      				-- right bcp
      				set x to my string_float(item 1 of right direction of pointRef)
      				set y to my string_float(item 2 of right direction of pointRef)
      				set pointAsString to pointAsString & "(" & x & "," & y & ")"
      				
      				set pointAsString to pointAsString & "]"
      				set pointsArray to pointsArray & pointAsString & ", "
      			end repeat
      			set pointsArrayLength to length of pointsArray
      			set pointsArray to (characters 1 through (pointsArrayLength - 2) of pointsArray) as string
      			set pointsArray to pointsArray & "]"
      			
      			set pathsArray to pathsArray & pointsArray & ", "
      		end repeat
      		set pathsArrayLength to length of pathsArray
      		set pathsArray to (characters 1 through (pathsArrayLength - 2) of pathsArray) as string
      		set pathsArray to pathsArray & "]"
      		
      	end tell
      	
      	activate
      	set the clipboard to pathsArray
      	log pathsArray
      end run
      
      
      
      on string_float(s)
      	set s to s as string
      	set new_s to ""
      	repeat with c in s
      		if c as string is equal to "," then set c to "."
      		set new_s to new_s & c as string
      	end repeat
      	return new_s
      end string_float
      
      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?

      Almost the same code, but instead of drawing a grid we draw 1000 circles randomly. As they have to stay in the same place, we save their locations in an array.

      (Also in this script some nice code to have if you want to publish animated gifs; a way to reduce the colours in the gif.)

      
      import random
      import struct
      
      frames = 24
      page_size = 1000
      number_of_circles = 1000
      min_dot_size = 10
      max_dot_size = 70
      radius = 200
      
      
      # create color table for gif
      # https://stackoverflow.com/questions/6269765/what-does-the-b-character-do-in-front-of-a-string-literal
      table = b""
      greys = [0,85,170,255]
      for i in greys:
          r = struct.pack(">B", i)
          g = struct.pack(">B", i)
          b = struct.pack(">B", i)
          table += r + g + b # + a
      
      
      def draw_dot(center, diameter): 
          x = center[0] - diameter / 2
          y = center[1] - diameter / 2
          oval(x, y, diameter, diameter)
      
      
      # Generate circles, randomly distributed
      circles = []
      for i in range(number_of_circles):
          x = random.randint(0,page_size)
          y = random.randint(0,page_size)
          circles.append((x,y))
      
      
      for f in range(frames):
          percentage_animated = f / frames
          newPage()
          
          fill(0)
          rect(0, 0, page_size, page_size)
      
          for c in circles:
              x = c[0]
              y = c[1]
              distance = pow( (pow(x - page_size / 2, 2 ) + pow(y - page_size / 2, 2)), 0.5)
              distance = distance - percentage_animated * radius * 2
              distance = distance % (radius * 2)
              percentage = distance / radius
              if percentage > 1:
                  percentage = 2 - percentage
              dot_size = min_dot_size + percentage * (max_dot_size - min_dot_size)
              fill(None)
              stroke(1)
              draw_dot((x,y), dot_size)
              
      saveImage("animated.gif", imageGIFRGBColorTable = table)
      

      animated.gif

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?
      frames = 24
      page_size = 1000
      grid_size = 20
      grid_spacing = page_size / grid_size
      min_dot_size = 10
      max_dot_size = 50
      radius = 100
      
      def draw_dot(center, diameter): 
          x = center[0] - diameter / 2
          y = center[1] - diameter / 2
          oval(x, y, diameter, diameter)
      
      for f in range(frames):
          percentage_animated = f / frames
          newPage()
          
          fill(0)
          rect(0, 0, page_size, page_size)
      
          for row in range(grid_size):
              for column in range(grid_size):
                  x = row * grid_spacing + grid_spacing / 2
                  y = column * grid_spacing + grid_spacing / 2
                  # Calculate distance from (x,y) of the dot to the center of the page
                  distance = pow( (pow(x - page_size / 2, 2 ) + pow(y - page_size / 2, 2)), 0.5)
                  # The wave must seem to move, so for every frame in the 
                  # animation, add a precentage of the length of the wave.
                  # Add or substract to change direction.
                  distance = distance - percentage_animated * radius * 2
                  # Use modulo to get a distance between 0 and double the radius
                  distance = distance % (radius * 2)
                  # The wave has a lenghth of double the radius. In the 
                  # first halve of the lenght, the dots get larger, in the 
                  # second halve the dots get smaller.
                  # So, we calculate a percentage between 0 and 2, and when
                  # the percentage is larger than 1, we count backwards.
                  percentage = distance / radius
                  if percentage > 1:
                      percentage = 2 - percentage
                  dot_size = min_dot_size + percentage * (max_dot_size - min_dot_size)
                  fill(1)
                  draw_dot((x,y), dot_size)
              
      saveImage("animated.gif")
      
      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?

      A simple example of how to move the wave through a grid of dots. The position of the dots does not matter.

      @frederik probably has a better and more robust solution for this also 😃

      animated.gif

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?
      1. I don’t think you need a radial grid.

      If you base the timing of the animation of one hexagon on its distance to the center point, I believe you get the same effect as in your example.

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?
      1. Changing the bezier-handle is a simple as calculating a point between two extremes, based on a percentage, based on how far you are in your animation.

      (Check out easings.net to create smoother animations)

      frames = 12
      
      def draw_segment(percentage):
          first_point = (-500, 0)
          first_control_point_convex = (-500, 185.191467285156)
          first_control_point_concave = (-350.388183585986, 86.410583482659)
          second_control_point_convex = (-399.316345214836, 346.863525390625)
          second_control_point_concave = (-249.704528786313,248.082641579087)
          second_point = (-249.704528808587, 433.274108886719)
          # Calculate in-between points based on percentage
          fcpcx = first_control_point_convex[0] + percentage * (first_control_point_concave[0] - first_control_point_convex[0])
          fcpcy = first_control_point_convex[1] + percentage * (first_control_point_concave[1] - first_control_point_convex[1])
          scpcx = second_control_point_convex[0] + percentage * (second_control_point_concave[0] - second_control_point_convex[0])
          scpcy = second_control_point_convex[1] + percentage * (second_control_point_concave[1] - second_control_point_convex[1])
          first_control_point = (fcpcx, fcpcy)
          second_control_point = (scpcx, scpcy)
          # Make and draw path
          bez = BezierPath()
          bez.moveTo(first_point)
          bez.curveTo(first_control_point, second_control_point, second_point)
          drawPath(bez)
      
      def draw_hexagon(percentage):
          for i in range(6):
              with savedState():
                  translate(500,500)
                  scale(0.5, 0.5)
                  rotate(i * 60, center=(0, 0))
                  draw_segment(percentage)
      
      for i in range(frames):
          newPage()
      
          fill(None)
          stroke(0)
          strokeWidth(10)
      
          percentage = i / frames
          draw_hexagon(percentage)
      
      for i in range(frames, 0, -1):
          newPage()
      
          fill(None)
          stroke(0)
          strokeWidth(10)
      
          percentage = i / frames
          draw_hexagon(percentage)
      
      saveImage("animated.gif")
      

      animated.gif

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?
      1. Without doing too much math, and using some ugly constants, you can draw a segment of the hexagon.

      Draw and rotate it six times, and you have the basis of the hexagon.

      
      def draw_segment():
          # Coordinates based on a circle with diameter 1000 and center at 0,0
          first_point = (-500, 0)
          first_control_point = (-500, 185.191467285156)
          second_control_point = (-399.316345214836, 346.863525390625)
          second_point = (-249.704528808587, 433.274108886719)
          # Make and draw path
          bez = BezierPath()
          bez.moveTo(first_point)
          bez.curveTo(first_control_point, second_control_point, second_point)
          drawPath(bez)
      
      def draw_hexagon():
          for i in range(6):
              with savedState():
                  translate(500,500)
                  scale(0.5, 0.5)
                  rotate(i * 60, center=(0, 0))
                  draw_segment()
      
      newPage()
      
      fill(None)
      stroke(0)
      strokeWidth(10)
      draw_hexagon()
      
      
      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Is it possible do this processing.gif in drawbot?

      @pi I think it’s doable. What do you have so far? To which questions didn’t you find an answer?

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Drawbot Preferences

      @rohernandezz You don’t have ~/Library/Preferences/com.drawbot.plist ?

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: donut

      @MauriceMeilleur

      An arbitrary amount of circles seemingly overlapping:

      import random
      
      number_of_circles = 10
      radius = 100
      wobble = 400
      canvas_size = 1000
      
      circles = []
      drawn_circles = []
      center = canvas_size / 2
      
      # Create circles
      for i in range(number_of_circles):
          x  = center - (wobble * 0.5) + random.random() * wobble
          y  = center - (wobble * 0.5) + random.random() * wobble
          circle = BezierPath()
          circle.oval(x - radius, y - radius, radius * 2, radius * 2)
          circles.append(circle)
      
      # Check if circles overlap
      for circle in circles:
          for i in range(len(drawn_circles)):
              intersecting_shape = circle.intersection(circles[i])
              if len(intersecting_shape.points) > 0:
                  circle = circle.difference(circles[i])
          drawn_circles.append(circle)
      
      # Draw circles, or what’s left of them
      newPage(canvas_size, canvas_size)
      fill(None)
      stroke(0)
      for circle in drawn_circles:
          drawPath(circle)
      
      posted in Code snippets
      monomonnik
      monomonnik
    • donut

      donut.png

      radius = 300
      circle_size = 300
      step = 3
      x, y = 500, 500
      
      fill(1)
      stroke(0)
      
      for d in range(0, 360, step):
          circle1 = BezierPath()
          circle1.oval(x - circle_size / 2, y - circle_size / 2, circle_size, circle_size)
          circle1.translate(0, radius)
          circle1.rotate(d, (x, y))
      
          circle2 = BezierPath()
          circle2.oval(x - circle_size / 2, y - circle_size / 2, circle_size, circle_size)
          circle2.translate(0, radius)
          circle2.rotate(d + step, (x, y))
          
          dif = circle2.difference(circle1)
          drawPath(dif)
      
      

      In response to https://twitter.com/MauriceMeilleur/status/1242196482717110274

      posted in Code snippets
      monomonnik
      monomonnik
    • RE: Add anchor points?

      @frederik You opened a door to a whole new world for me. This is much simpler than I thought. And at the same time, I think it’s going to take me some time to get my head around all this pen-stuff. Thanks!

      posted in General Discussion
      monomonnik
      monomonnik
    • Add anchor points?

      Is there an easy way to add anchor points to a path, like the Add Anchor Points command in Illustrator?

      add anchor points in illustrator.png

      (I looked here and in the documentation, but I can’t find it.)

      posted in General Discussion
      monomonnik
      monomonnik
    • RE: Delay after drawing

      Thanks for the tip @gferreira

      Running the script and saving to a gif is faster, only two seconds though. But not having to wait for the Drawbot app to finish is a great improvement.

      posted in General Discussion
      monomonnik
      monomonnik
    • Delay after drawing

      I create animated gifs with Drawbot. They’re somewhat complex and take some time to proces and draw. After calculating, drawing, and saving all frames to a gif, Drawbot remains unresponsive. It seems it needs considerable time to draw the canvases in the app.

      For example, for my last animation it took 69 seconds to calculate and save 78 frames to a gif, and then an extra 25 seconds for Drawbot to start drawing 78 canvases — the (actual, visible) drawing takes less than a second — and become responsive again.

      I’m a little curious why this is. If the canvases can be saved as a gif, they should be already be there? Why should it take that much time to draw them on screen?

      And, is it possible to only save as gif, and not draw the canvas in the Drawbot app?

      Screen recording on youtube

      Saving of the gif at 1:08

      Drawbot draws canvases and becomes responsive at 1:34

      The gif on twitter.

      posted in General Discussion
      monomonnik
      monomonnik