text: # ##### BEGIN GPL LICENSE BLOCK #####
  #
  #  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 2
  #  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, write to the Free Software Foundation,
  #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  #
  # ##### END GPL LICENSE BLOCK #####
  
  import bpy
  import math
  
  from collections import namedtuple
  
  from bpy.props import (
      BoolProperty,
      IntProperty,
      FloatProperty,
      PointerProperty
  )
  
  
  ScatterSettings = namedtuple("ScatterSettings",
      ["seed", "density", "radius", "scale", "random_scale",
       "rotation", "normal_offset", "use_normal_rotation"])
  
  class ObjectScatterProperties(bpy.types.PropertyGroup):
      seed: IntProperty(
          name="Seed",
          default=0,
          description="Change it to get a different scatter pattern",
      )
  
      density: FloatProperty(
          name="Density",
          default=10,
          min=0,
          soft_max=50,
          description="The amount of objects per unit on the line",
      )
  
      radius: FloatProperty(
          name="Radius",
          default=1,
          min=0,
          soft_max=5,
          subtype='DISTANCE',
          unit='LENGTH',
          description="Maximum distance of the objects to the line",
      )
  
      scale: FloatProperty(
          name="Scale",
          default=0.3,
          min=0.00001,
          soft_max=1,
          description="Size of the generated objects",
      )
  
      random_scale_percentage: FloatProperty(
          name="Random Scale Percentage",
          default=80,
          min=0,
          max=100,
          subtype='PERCENTAGE',
          precision=0,
          description="Increase to get a larger range of sizes",
      )
  
      rotation: FloatProperty(
          name="Rotation",
          default=0.5,
          min=0,
          max=math.pi * 2,
          soft_max=math.pi / 2,
          subtype='ANGLE',
          unit='ROTATION',
          description="Maximum rotation of the generated objects",
      )
  
      normal_offset: FloatProperty(
          name="Normal Offset",
          default=0,
          soft_min=-1.0,
          soft_max=1.0,
          description="Distance from the surface",
      )
  
      use_normal_rotation: BoolProperty(
          name="Use Normal Rotation",
          default=True,
          description="Rotate the instances according to the surface normals",
      )
  
      def to_settings(self):
          return ScatterSettings(
              seed=self.seed,
              density=self.density,
              radius=self.radius,
              scale=self.scale,
              random_scale=self.random_scale_percentage / 100,
              rotation=self.rotation,
              normal_offset=self.normal_offset,
              use_normal_rotation=self.use_normal_rotation,
          )
  
  
  class ObjectScatterPanel(bpy.types.Panel):
      bl_idname = "OBJECT_PT_object_scatter"
      bl_label = "Object Scatter"
      bl_space_type = 'VIEW_3D'
      bl_region_type = 'UI'
      bl_category = "Tool"
  
      def draw(self, context):
          layout = self.layout
          layout.use_property_split = True
          scatter = context.scene.scatter_properties
  
          layout.prop(scatter, "density", slider=True)
          layout.prop(scatter, "radius", slider=True)
  
          col = layout.column(align=True)
          col.prop(scatter, "scale", slider=True)
          col.prop(scatter, "random_scale_percentage", text="Randomness", slider=True)
  
          layout.prop(scatter, "use_normal_rotation")
          layout.prop(scatter, "rotation", slider=True)
          layout.prop(scatter, "normal_offset", text="Offset", slider=True)
          layout.prop(scatter, "seed")
  
  def draw_menu(self, context):
      layout = self.layout
      layout.operator("object.scatter")
  
  classes = (
      ObjectScatterProperties,
      ObjectScatterPanel,
  )
  
  def register():
      for cls in classes:
          bpy.utils.register_class(cls)
      bpy.types.Scene.scatter_properties = PointerProperty(type=ObjectScatterProperties)
      bpy.types.VIEW3D_MT_object.append(draw_menu)
  
  def unregister():
      for cls in classes:
          bpy.utils.unregister_class(cls)
      del bpy.types.Scene.scatter_properties
      bpy.types.VIEW3D_MT_object.remove(draw_menu)
  