# SPDX-FileCopyrightText: 2009-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later

import bpy
from bpy.types import Panel
from bl_ui.utils import PresetPanel
from bl_ui.properties_physics_common import (
    effector_weights_ui,
)


class FLUID_PT_presets(PresetPanel, Panel):
    bl_label = "Fluid Presets"
    preset_subdir = "fluid"
    preset_operator = "script.execute_preset"
    preset_add_operator = "fluid.preset_add"


class PhysicButtonsPanel:
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "physics"

    @staticmethod
    def check_domain_has_unbaked_guide(domain):
        return (
            domain.use_guide and not domain.has_cache_baked_guide and
            ((domain.guide_source == 'EFFECTOR') or
             (domain.guide_source == 'DOMAIN' and not domain.guide_parent))
        )

    @staticmethod
    def poll_fluid(context):
        ob = context.object
        if not ((ob and ob.type == 'MESH') and (context.fluid)):
            return False

        md = context.fluid
        return md and (context.fluid.fluid_type != 'NONE')

    @staticmethod
    def poll_fluid_domain(context):
        if not PhysicButtonsPanel.poll_fluid(context):
            return False

        md = context.fluid
        return md and (md.fluid_type == 'DOMAIN')

    @staticmethod
    def poll_gas_domain(context):
        if not PhysicButtonsPanel.poll_fluid(context):
            return False

        md = context.fluid
        if md and (md.fluid_type == 'DOMAIN'):
            domain = md.domain_settings
            return domain.domain_type == 'GAS'
        return False

    @staticmethod
    def poll_liquid_domain(context):
        if not PhysicButtonsPanel.poll_fluid(context):
            return False

        md = context.fluid
        if md and (md.fluid_type == 'DOMAIN'):
            domain = md.domain_settings
            return domain.domain_type == 'LIQUID'
        return False

    @staticmethod
    def poll_fluid_flow(context):
        if not PhysicButtonsPanel.poll_fluid(context):
            return False

        md = context.fluid
        return md and (md.fluid_type == 'FLOW')

    @staticmethod
    def poll_fluid_flow_outflow(context):
        if not PhysicButtonsPanel.poll_fluid_flow(context):
            return False

        md = context.fluid
        flow = md.flow_settings
        return (flow.flow_behavior == 'OUTFLOW')

    @staticmethod
    def poll_fluid_flow_liquid(context):
        if not PhysicButtonsPanel.poll_fluid_flow(context):
            return False

        md = context.fluid
        flow = md.flow_settings
        return (flow.flow_type == 'LIQUID')


class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
    bl_label = "Fluid"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        ob = context.object
        return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.fluid)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        if not bpy.app.build_options.fluid:
            col = layout.column(align=True)
            col.alignment = 'RIGHT'
            col.label(text="Built without Fluid modifier")
            return
        md = context.fluid

        layout.prop(md, "fluid_type")


class PHYSICS_PT_settings(PhysicButtonsPanel, Panel):
    bl_label = "Settings"
    bl_parent_id = "PHYSICS_PT_fluid"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        ob = context.object
        scene = context.scene

        if md.fluid_type == 'DOMAIN':
            domain = md.domain_settings

            is_baking_any = domain.is_cache_baking_any
            has_baked_data = domain.has_cache_baked_data

            row = layout.row()
            row.enabled = not is_baking_any and not has_baked_data
            row.prop(domain, "domain_type", expand=False)

            flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
            flow.enabled = not is_baking_any and not has_baked_data

            col = flow.column()
            col.enabled = not domain.has_cache_baked_guide
            col.prop(domain, "resolution_max", text="Resolution Divisions")
            col.prop(domain, "time_scale", text="Time Scale")
            col.prop(domain, "cfl_condition", text="CFL Number")

            col = flow.column()
            col.prop(domain, "use_adaptive_timesteps")
            sub = col.column(align=True)
            sub.active = domain.use_adaptive_timesteps
            sub.prop(domain, "timesteps_max", text="Timesteps Maximum")
            sub.prop(domain, "timesteps_min", text="Minimum")

            col.separator()

            col = flow.column()
            if scene.use_gravity:
                sub = col.column()
                sub.enabled = False
                sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA')
            else:
                col.prop(domain, "gravity", text="Gravity")

            col = flow.column()
            if PhysicButtonsPanel.poll_gas_domain(context):
                col.prop(domain, "clipping", text="Empty Space")
            col.prop(domain, "delete_in_obstacle", text="Delete in Obstacle")

            if domain.cache_type == 'MODULAR':
                col.separator()
                label = ""

                # Deactivate bake operator if guides are enabled but not baked yet.
                note_flag = True
                if self.check_domain_has_unbaked_guide(domain):
                    note_flag = False
                    label = "Unbaked Guides: Bake Guides or disable them"
                elif not domain.cache_resumable and not label:
                    label = "Non Resumable Cache: Baking "
                    if PhysicButtonsPanel.poll_liquid_domain(context):
                        label += "mesh or particles will not be possible"
                    elif PhysicButtonsPanel.poll_gas_domain(context):
                        label += "noise will not be possible"
                    else:
                        label = ""

                if label:
                    info = layout.split()
                    note = info.row()
                    note.enabled = note_flag
                    note.alignment = 'RIGHT'
                    note.label(icon='INFO', text=label)

                split = layout.split()
                split.enabled = note_flag and ob.mode == 'OBJECT'

                bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
                if (
                        domain.cache_resumable and
                        domain.has_cache_baked_data and
                        not domain.is_cache_baking_data and
                        bake_incomplete
                ):
                    col = split.column()
                    col.operator("fluid.bake_data", text="Resume")
                    col = split.column()
                    col.operator("fluid.free_data", text="Free")
                elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
                    split.enabled = False
                    split.operator("fluid.pause_bake", text="Baking Data - ESC to pause")
                elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
                    split.operator("fluid.bake_data", text="Bake Data")
                else:
                    split.operator("fluid.free_data", text="Free Data")

        elif md.fluid_type == 'FLOW':
            flow = md.flow_settings

            row = layout.row()
            row.prop(flow, "flow_type", expand=False)

            grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

            col = grid.column()
            col.prop(flow, "flow_behavior", expand=False)
            if flow.flow_behavior in {'INFLOW', 'OUTFLOW'}:
                col.prop(flow, "use_inflow")

            col.prop(flow, "subframes", text="Sampling Substeps")

            if not flow.flow_behavior == 'OUTFLOW' and flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:

                if flow.flow_type in {'SMOKE', 'BOTH'}:
                    col.prop(flow, "smoke_color", text="Smoke Color")

                col = grid.column(align=True)
                col.prop(flow, "use_absolute", text="Absolute Density")

                if flow.flow_type in {'SMOKE', 'BOTH'}:
                    col.prop(flow, "temperature", text="Initial Temperature")
                    col.prop(flow, "density", text="Density")

                if flow.flow_type in {'FIRE', 'BOTH'}:
                    col.prop(flow, "fuel_amount", text="Fuel")

                col.separator()
                col.prop_search(flow, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")

        elif md.fluid_type == 'EFFECTOR':
            effector_settings = md.effector_settings

            row = layout.row()
            row.prop(effector_settings, "effector_type")

            grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

            col = grid.column()
            col.prop(effector_settings, "subframes", text="Sampling Substeps")
            col.prop(effector_settings, "surface_distance", text="Surface Thickness")

            col = grid.column()

            col.prop(effector_settings, "use_effector", text="Use Effector")
            col.prop(effector_settings, "use_plane_init", text="Is Planar")

            if effector_settings.effector_type == 'GUIDE':
                col.prop(effector_settings, "velocity_factor", text="Velocity Factor")
                col.prop(effector_settings, "guide_mode", text="Guide Mode")


class PHYSICS_PT_borders(PhysicButtonsPanel, Panel):
    bl_label = "Border Collisions"
    bl_parent_id = "PHYSICS_PT_settings"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        domain = md.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        col = layout.column(align=True)
        col.enabled = not is_baking_any and not has_baked_data

        col.prop(domain, "use_collision_border_front")
        col.prop(domain, "use_collision_border_back")
        col.prop(domain, "use_collision_border_right")
        col.prop(domain, "use_collision_border_left")
        col.prop(domain, "use_collision_border_top")
        col.prop(domain, "use_collision_border_bottom")


class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
    bl_label = "Gas"
    bl_parent_id = "PHYSICS_PT_fluid"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        domain = md.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_data

        col = flow.column(align=True)
        col.prop(domain, "alpha", text="Buoyancy Density")
        col.prop(domain, "beta", text="Heat")
        col = flow.column()
        col.prop(domain, "vorticity")


class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel):
    bl_label = "Dissolve"
    bl_parent_id = "PHYSICS_PT_smoke"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any

        self.layout.enabled = not is_baking_any
        self.layout.prop(md, "use_dissolve_smoke", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        domain = md.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_data

        layout.active = domain.use_dissolve_smoke

        col = flow.column()
        col.prop(domain, "dissolve_speed", text="Time")

        col = flow.column()
        col.prop(domain, "use_dissolve_smoke_log", text="Slow")


class PHYSICS_PT_fire(PhysicButtonsPanel, Panel):
    bl_label = "Fire"
    bl_parent_id = "PHYSICS_PT_smoke"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        domain = md.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_data

        col = flow.column()
        col.prop(domain, "burning_rate", text="Reaction Speed")
        row = col.row()
        sub = row.column(align=True)
        sub.prop(domain, "flame_smoke", text="Flame Smoke")
        sub.prop(domain, "flame_vorticity", text="Vorticity")

        col = flow.column(align=True)
        col.prop(domain, "flame_max_temp", text="Temperature Maximum")
        col.prop(domain, "flame_ignition", text="Minimum")
        row = col.row()
        row.prop(domain, "flame_smoke_color", text="Smoke Color")


class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel):
    bl_label = "Liquid"
    bl_parent_id = "PHYSICS_PT_fluid"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_liquid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any

        self.layout.enabled = not is_baking_any
        self.layout.prop(md, "use_flip_particles", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        md = context.fluid
        domain = md.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        layout.enabled = not is_baking_any and not has_baked_data
        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

        col = flow.column()
        col.prop(domain, "simulation_method", expand=False)
        if domain.simulation_method == 'FLIP':
            col.prop(domain, "flip_ratio", text="FLIP Ratio")
        col.prop(domain, "sys_particle_maximum", text="System Maximum")
        col = col.column(align=True)
        col.prop(domain, "particle_radius", text="Particle Radius")
        col.prop(domain, "particle_number", text="Sampling")
        col.prop(domain, "particle_randomness", text="Randomness")

        col = flow.column()
        col = col.column(align=True)
        col.prop(domain, "particle_max", text="Particles Maximum")
        col.prop(domain, "particle_min", text="Minimum")

        col.separator()

        col = col.column()
        col.prop(domain, "particle_band_width", text="Narrow Band Width")

        col = col.column()
        col.prop(domain, "use_fractions", text="Fractional Obstacles")
        sub = col.column()
        sub.active = domain.use_fractions
        sub.prop(domain, "fractions_distance", text="Obstacle Distance")
        sub.prop(domain, "fractions_threshold", text="Threshold")


class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel):
    bl_label = "Flow Source"
    bl_parent_id = "PHYSICS_PT_settings"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_flow(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        ob = context.object
        flow = context.fluid.flow_settings

        col = layout.column()
        col.prop(flow, "flow_source", expand=False, text="Flow Source")
        if flow.flow_source == 'PARTICLES':
            col.prop_search(flow, "particle_system", ob, "particle_systems", text="Particle System")

        grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

        col = grid.column()
        if flow.flow_source == 'MESH':
            col.prop(flow, "use_plane_init", text="Is Planar")
            col.prop(flow, "surface_distance", text="Surface Emission")
            if flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:
                col = grid.column()
                col.prop(flow, "volume_density", text="Volume Emission")

        if flow.flow_source == 'PARTICLES':
            col.prop(flow, "use_particle_size", text="Set Size")
            sub = col.column()
            sub.active = flow.use_particle_size
            sub.prop(flow, "particle_size")


class PHYSICS_PT_flow_initial_velocity(PhysicButtonsPanel, Panel):
    bl_label = "Initial Velocity"
    bl_parent_id = "PHYSICS_PT_settings"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_flow(context):
            return False

        if PhysicButtonsPanel.poll_fluid_flow_outflow(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid
        flow_smoke = md.flow_settings

        self.layout.prop(flow_smoke, "use_initial_velocity", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)

        md = context.fluid
        flow_smoke = md.flow_settings

        flow.active = flow_smoke.use_initial_velocity

        col = flow.column()
        col.prop(flow_smoke, "velocity_factor")

        if flow_smoke.flow_source == 'MESH':
            col.prop(flow_smoke, "velocity_normal")
            # col.prop(flow_smoke, "velocity_random")
            col = flow.column()
            col.prop(flow_smoke, "velocity_coord")


class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel):
    bl_label = "Texture"
    bl_parent_id = "PHYSICS_PT_settings"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_flow(context):
            return False

        if PhysicButtonsPanel.poll_fluid_flow_outflow(context):
            return False

        if PhysicButtonsPanel.poll_fluid_flow_liquid(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid
        flow_smoke = md.flow_settings

        self.layout.prop(flow_smoke, "use_texture", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

        ob = context.object
        flow_smoke = context.fluid.flow_settings

        sub = flow.column()
        sub.active = flow_smoke.use_texture
        sub.prop(flow_smoke, "noise_texture")
        sub.prop(flow_smoke, "texture_map_type", text="Mapping")

        col = flow.column()
        sub = col.column()
        sub.active = flow_smoke.use_texture

        if flow_smoke.texture_map_type == 'UV':
            sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")

        if flow_smoke.texture_map_type == 'AUTO':
            sub.prop(flow_smoke, "texture_size")

        sub.prop(flow_smoke, "texture_offset")


class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel):
    bl_label = "Adaptive Domain"
    bl_parent_id = "PHYSICS_PT_settings"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        md = context.fluid
        domain = md.domain_settings
        # Effector guides require a fixed domain size
        if domain.use_guide and domain.guide_source == 'EFFECTOR':
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any

        self.layout.enabled = not is_baking_any and not has_baked_any
        self.layout.prop(md, "use_adaptive_domain", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings
        layout.active = domain.use_adaptive_domain

        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
        flow.enabled = not is_baking_any and not has_baked_any

        col = flow.column()
        col.prop(domain, "additional_res", text="Add Resolution")
        col.prop(domain, "adapt_margin")

        col.separator()

        col = flow.column()
        col.prop(domain, "adapt_threshold", text="Threshold")


class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
    bl_label = "Noise"
    bl_parent_id = "PHYSICS_PT_smoke"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings
        is_baking_any = domain.is_cache_baking_any
        self.layout.enabled = not is_baking_any
        self.layout.prop(md, "use_noise", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        ob = context.object
        domain = context.fluid.domain_settings
        layout.active = domain.use_noise

        is_baking_any = domain.is_cache_baking_any
        has_baked_noise = domain.has_cache_baked_noise

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_noise

        col = flow.column()
        col.prop(domain, "noise_scale", text="Upres Factor")
        col.prop(domain, "noise_strength", text="Strength")

        col = flow.column()
        col.prop(domain, "noise_pos_scale", text="Scale")
        col.prop(domain, "noise_time_anim", text="Time")

        if domain.cache_type == 'MODULAR':
            col.separator()

            # Deactivate bake operator if data has not been baked yet.
            note_flag = True
            if domain.use_noise:
                label = ""
                if not domain.cache_resumable:
                    label = "Non Resumable Cache: Enable resumable option first"
                elif not domain.has_cache_baked_data:
                    label = "Unbaked Data: Bake Data first"

                if label:
                    info = layout.split()
                    note = info.row()
                    note_flag = False
                    note.enabled = note_flag
                    note.alignment = 'RIGHT'
                    note.label(icon='INFO', text=label)

            split = layout.split()
            split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'

            bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end)
            if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete:
                col = split.column()
                col.operator("fluid.bake_noise", text="Resume")
                col = split.column()
                col.operator("fluid.free_noise", text="Free")
            elif not domain.has_cache_baked_noise and domain.is_cache_baking_noise:
                split.enabled = False
                split.operator("fluid.pause_bake", text="Baking Noise - ESC to pause")
            elif not domain.has_cache_baked_noise and not domain.is_cache_baking_noise:
                split.operator("fluid.bake_noise", text="Bake Noise")
            else:
                split.operator("fluid.free_noise", text="Free Noise")


class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel):
    bl_label = "Mesh"
    bl_parent_id = "PHYSICS_PT_liquid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_liquid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings
        is_baking_any = domain.is_cache_baking_any
        self.layout.enabled = not is_baking_any
        self.layout.prop(md, "use_mesh", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        ob = context.object
        domain = context.fluid.domain_settings
        layout.active = domain.use_mesh

        is_baking_any = domain.is_cache_baking_any
        has_baked_mesh = domain.has_cache_baked_mesh

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_mesh

        col = flow.column()

        col.prop(domain, "mesh_scale", text="Upres Factor")
        col.prop(domain, "mesh_particle_radius", text="Particle Radius")

        col = flow.column()
        col.prop(domain, "use_speed_vectors", text="Use Speed Vectors")

        col.separator()
        col.prop(domain, "mesh_generator", text="Mesh Generator")

        if domain.mesh_generator == 'IMPROVED':
            col = flow.column(align=True)
            col.prop(domain, "mesh_smoothen_pos", text="Smoothing Positive")
            col.prop(domain, "mesh_smoothen_neg", text="Negative")

            col = flow.column(align=True)
            col.prop(domain, "mesh_concave_upper", text="Concavity Upper")
            col.prop(domain, "mesh_concave_lower", text="Lower")

        # TODO(@sebbas): for now just interpolate any up-resolution grids, ie not sampling high-resolution grids
        # col.prop(domain, "highres_sampling", text="Flow Sampling:")

        if domain.cache_type == 'MODULAR':
            col.separator()

            # Deactivate bake operator if data has not been baked yet.
            note_flag = True
            if domain.use_mesh:
                label = ""
                if not domain.cache_resumable:
                    label = "Non Resumable Cache: Enable resumable option first"
                elif not domain.has_cache_baked_data:
                    label = "Unbaked Data: Bake Data first"

                if label:
                    info = layout.split()
                    note = info.row()
                    note_flag = False
                    note.enabled = note_flag
                    note.alignment = 'RIGHT'
                    note.label(icon='INFO', text=label)

            split = layout.split()
            split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'

            bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end)
            if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete:
                col = split.column()
                col.operator("fluid.bake_mesh", text="Resume")
                col = split.column()
                col.operator("fluid.free_mesh", text="Free")
            elif not domain.has_cache_baked_mesh and domain.is_cache_baking_mesh:
                split.enabled = False
                split.operator("fluid.pause_bake", text="Baking Mesh - ESC to pause")
            elif not domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh:
                split.operator("fluid.bake_mesh", text="Bake Mesh")
            else:
                split.operator("fluid.free_mesh", text="Free Mesh")


class PHYSICS_PT_particles(PhysicButtonsPanel, Panel):
    bl_label = "Particles"
    bl_parent_id = "PHYSICS_PT_liquid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_liquid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        ob = context.object
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_particles = domain.has_cache_baked_particles
        using_particles = domain.use_spray_particles or domain.use_foam_particles or domain.use_bubble_particles

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any

        sndparticle_combined_export = domain.sndparticle_combined_export
        col = flow.column()
        row = col.row()
        row.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'}
        row.prop(domain, "use_spray_particles", text="Spray")
        row.prop(domain, "use_foam_particles", text="Foam")
        row.prop(domain, "use_bubble_particles", text="Bubbles")

        col.separator()

        col.prop(domain, "sndparticle_combined_export")

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_particles
        flow.active = using_particles

        col = flow.column()
        col.prop(domain, "particle_scale", text="Upres Factor")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_potential_max_wavecrest", text="Wave Crest Potential Maximum")
        col.prop(domain, "sndparticle_potential_min_wavecrest", text="Minimum")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_potential_max_trappedair", text="Trapped Air Potential Maximum")
        col.prop(domain, "sndparticle_potential_min_trappedair", text="Minimum")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_potential_max_energy", text="Kinetic Energy Potential Maximum")
        col.prop(domain, "sndparticle_potential_min_energy", text="Minimum")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_potential_radius", text="Potential Radius")
        col.prop(domain, "sndparticle_update_radius", text="Particle Update Radius")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_sampling_wavecrest", text="Wave Crest Particle Sampling")
        col.prop(domain, "sndparticle_sampling_trappedair", text="Trapped Air Particle Sampling")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_life_max", text="Particle Life Maximum")
        col.prop(domain, "sndparticle_life_min", text="Minimum")
        col.separator()

        col = flow.column(align=True)
        col.prop(domain, "sndparticle_bubble_buoyancy", text="Bubble Buoyancy")
        col.prop(domain, "sndparticle_bubble_drag", text="Bubble Drag")
        col.separator()

        col = flow.column()
        col.prop(domain, "sndparticle_boundary", text="Particles in Boundary")

        if domain.cache_type == 'MODULAR':
            col.separator()

            # Deactivate bake operator if data has not been baked yet.
            note_flag = True
            if using_particles:
                label = ""
                if not domain.cache_resumable:
                    label = "Non Resumable Cache: Enable resumable option first"
                elif not domain.has_cache_baked_data:
                    label = "Unbaked Data: Bake Data first"

                if label:
                    info = layout.split()
                    note = info.row()
                    note_flag = False
                    note.enabled = note_flag
                    note.alignment = 'RIGHT'
                    note.label(icon='INFO', text=label)

            split = layout.split()
            split.enabled = (
                note_flag and
                ob.mode == 'OBJECT' and
                domain.has_cache_baked_data and
                (domain.use_spray_particles or
                 domain.use_bubble_particles or
                 domain.use_foam_particles or
                 domain.use_tracer_particles)
            )

            bake_incomplete = (domain.cache_frame_pause_particles < domain.cache_frame_end)
            if domain.has_cache_baked_particles and not domain.is_cache_baking_particles and bake_incomplete:
                col = split.column()
                col.operator("fluid.bake_particles", text="Resume")
                col = split.column()
                col.operator("fluid.free_particles", text="Free")
            elif not domain.has_cache_baked_particles and domain.is_cache_baking_particles:
                split.enabled = False
                split.operator("fluid.pause_bake", text="Baking Particles - ESC to pause")
            elif not domain.has_cache_baked_particles and not domain.is_cache_baking_particles:
                split.operator("fluid.bake_particles", text="Bake Particles")
            else:
                split.operator("fluid.free_particles", text="Free Particles")


class PHYSICS_PT_viscosity(PhysicButtonsPanel, Panel):
    bl_label = "Viscosity"
    bl_parent_id = "PHYSICS_PT_liquid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        # Fluid viscosity only enabled for liquids
        if not PhysicButtonsPanel.poll_liquid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings
        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any
        self.layout.enabled = not is_baking_any and not has_baked_any
        self.layout.prop(md, "use_viscosity", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings
        layout.active = domain.use_viscosity

        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data

        col = flow.column(align=True)
        col.prop(domain, "viscosity_value", text="Strength")


class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel):
    bl_label = "Diffusion"
    bl_parent_id = "PHYSICS_PT_liquid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        # Fluid diffusion only enabled for liquids (surface tension and viscosity not relevant for smoke)
        if not PhysicButtonsPanel.poll_liquid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings
        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any
        self.layout.enabled = not is_baking_any and not has_baked_any
        self.layout.prop(md, "use_diffusion", text="")

    def draw_header_preset(self, _context):
        FLUID_PT_presets.draw_panel_header(self.layout)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings
        layout.active = domain.use_diffusion

        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data

        col = flow.column(align=True)
        col.prop(domain, "viscosity_base", text="Base")
        col.prop(domain, "viscosity_exponent", text="Exponent", slider=True)

        col = flow.column()
        col.prop(domain, "surface_tension", text="Surface Tension")


class PHYSICS_PT_guide(PhysicButtonsPanel, Panel):
    bl_label = "Guides"
    bl_parent_id = "PHYSICS_PT_fluid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw_header(self, context):
        md = context.fluid.domain_settings
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any

        self.layout.enabled = not is_baking_any
        self.layout.prop(md, "use_guide", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings

        layout.active = domain.use_guide

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_data

        col = flow.column()
        col.prop(domain, "guide_alpha", text="Weight")
        col.prop(domain, "guide_beta", text="Size")
        col.prop(domain, "guide_vel_factor", text="Velocity Factor")

        col = flow.column()
        col.prop(domain, "guide_source", text="Velocity Source")
        if domain.guide_source == 'DOMAIN':
            col.prop(domain, "guide_parent", text="Guide Parent")

        if domain.cache_type == 'MODULAR':
            col.separator()

            if domain.guide_source == 'EFFECTOR':
                split = layout.split()
                bake_incomplete = (domain.cache_frame_pause_guide < domain.cache_frame_end)
                if domain.has_cache_baked_guide and not domain.is_cache_baking_guide and bake_incomplete:
                    col = split.column()
                    col.operator("fluid.bake_guides", text="Resume")
                    col = split.column()
                    col.operator("fluid.free_guides", text="Free")
                elif not domain.has_cache_baked_guide and domain.is_cache_baking_guide:
                    split.enabled = False
                    split.operator("fluid.pause_bake", text="Baking Guides - ESC to pause")
                elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide:
                    split.operator("fluid.bake_guides", text="Bake Guides")
                else:
                    split.operator("fluid.free_guides", text="Free Guides")


class PHYSICS_PT_collections(PhysicButtonsPanel, Panel):
    bl_label = "Collections"
    bl_parent_id = "PHYSICS_PT_fluid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

        col = flow.column()
        col.prop(domain, "fluid_group", text="Flow")

        # col.prop(domain, "effector_group", text="Forces")
        col.prop(domain, "effector_group", text="Effector")


class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
    bl_label = "Cache"
    bl_parent_id = "PHYSICS_PT_fluid"
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout

        ob = context.object
        md = context.fluid
        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_data = domain.has_cache_baked_data
        has_baked_mesh = domain.has_cache_baked_mesh

        col = layout.column()
        col.prop(domain, "cache_directory", text="")
        col.enabled = not is_baking_any

        layout.use_property_split = True

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)

        col = flow.column()
        row = col.row()
        row = row.column(align=True)
        row.prop(domain, "cache_frame_start", text="Frame Start")
        row.prop(domain, "cache_frame_end", text="End")
        row = col.row()
        row.enabled = domain.cache_type in {'MODULAR', 'ALL'}
        row.prop(domain, "cache_frame_offset", text="Offset")

        col.separator()

        col = flow.column()
        col.prop(domain, "cache_type", expand=False)

        row = col.row()
        row.enabled = not is_baking_any and not has_baked_data
        row.prop(domain, "cache_resumable", text="Is Resumable")

        row = col.row()
        row.enabled = not is_baking_any and not has_baked_data
        row.prop(domain, "cache_data_format", text="Format Volumes")

        if md.domain_settings.domain_type == 'LIQUID' and domain.use_mesh:
            row = col.row()
            row.enabled = not is_baking_any and not has_baked_mesh
            row.prop(domain, "cache_mesh_format", text="Meshes")

        if domain.cache_type == 'ALL':
            col.separator()
            split = layout.split()
            split.enabled = ob.mode == 'OBJECT'

            bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
            if (
                    domain.cache_resumable and
                    domain.has_cache_baked_data and
                    not domain.is_cache_baking_data and
                    bake_incomplete
            ):
                col = split.column()
                col.operator("fluid.bake_all", text="Resume")
                col = split.column()
                col.operator("fluid.free_all", text="Free")
            elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
                split.enabled = False
                split.operator("fluid.pause_bake", text="Baking All - ESC to pause")
            elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
                split.operator("fluid.bake_all", text="Bake All")
            else:
                split.operator("fluid.free_all", text="Free All")


class PHYSICS_PT_export(PhysicButtonsPanel, Panel):
    bl_label = "Advanced"
    bl_parent_id = "PHYSICS_PT_cache"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        domain = context.fluid.domain_settings
        if (
                not PhysicButtonsPanel.poll_fluid_domain(context) or
                (domain.cache_data_format != 'OPENVDB' and bpy.app.debug_value != 3001)
        ):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings

        is_baking_any = domain.is_cache_baking_any
        has_baked_any = domain.has_cache_baked_any
        has_baked_data = domain.has_cache_baked_data

        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
        flow.enabled = not is_baking_any and not has_baked_any

        col = flow.column()

        if domain.cache_data_format == 'OPENVDB':
            col.enabled = not is_baking_any and not has_baked_data
            col.prop(domain, "openvdb_cache_compress_type", text="Compression Volumes")

            col = flow.column()
            col.prop(domain, "openvdb_data_depth", text="Precision Volumes")

        # Only show the advanced panel to advanced users who know Mantaflow's birthday :)
        if bpy.app.debug_value == 3001:
            col = flow.column()
            col.prop(domain, "export_manta_script", text="Export Mantaflow Script")


class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel):
    bl_label = "Field Weights"
    bl_parent_id = "PHYSICS_PT_fluid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_fluid_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        domain = context.fluid.domain_settings
        effector_weights_ui(self, domain.effector_weights, 'SMOKE')


class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel):
    bl_label = "Viewport Display"
    bl_parent_id = "PHYSICS_PT_fluid"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (PhysicButtonsPanel.poll_fluid_domain(context))

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)

        domain = context.fluid.domain_settings

        col = flow.column(align=False)
        col.prop(domain, "display_thickness")

        sub = col.column()
        sub.prop(domain, "display_interpolation")

        if domain.use_color_ramp and domain.color_ramp_field == 'FLAGS':
            sub.enabled = False

        col = col.column()
        col.active = not domain.use_slice
        col.prop(domain, "slice_per_voxel")


class PHYSICS_PT_viewport_display_slicing(PhysicButtonsPanel, Panel):
    bl_label = "Slice"
    bl_parent_id = "PHYSICS_PT_viewport_display"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (PhysicButtonsPanel.poll_fluid_domain(context))

    def draw_header(self, context):
        md = context.fluid.domain_settings

        self.layout.prop(md, "use_slice", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings

        layout.active = domain.use_slice

        col = layout.column()
        col.prop(domain, "slice_axis")
        col.prop(domain, "slice_depth")

        sub = col.column()
        sub.prop(domain, "show_gridlines")

        sub.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS'


class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel):
    bl_label = "Grid Display"
    bl_parent_id = "PHYSICS_PT_viewport_display"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (PhysicButtonsPanel.poll_fluid_domain(context))

    def draw_header(self, context):
        md = context.fluid.domain_settings

        self.layout.prop(md, "use_color_ramp", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings
        col = layout.column()
        col.active = domain.use_color_ramp
        col.prop(domain, "color_ramp_field")

        if not domain.color_ramp_field == 'FLAGS':
            col.prop(domain, "color_ramp_field_scale")

        col.use_property_split = False

        if domain.color_ramp_field[:3] != 'PHI' and domain.color_ramp_field not in {'FLAGS', 'PRESSURE'}:
            col = col.column()
            col.template_color_ramp(domain, "color_ramp", expand=True)


class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel):
    bl_label = "Vector Display"
    bl_parent_id = "PHYSICS_PT_viewport_display"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (PhysicButtonsPanel.poll_fluid_domain(context))

    def draw_header(self, context):
        md = context.fluid.domain_settings

        self.layout.prop(md, "show_velocity", text="")

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)

        domain = context.fluid.domain_settings

        col = flow.column()
        col.active = domain.show_velocity
        col.prop(domain, "vector_display_type", text="Display As")

        if not domain.use_guide and domain.vector_field == 'GUIDE_VELOCITY':
            note = layout.split()
            note.label(icon='INFO', text="Enable Guides first! Defaulting to Fluid Velocity")

        if domain.vector_display_type == 'MAC':
            sub = col.column(heading="MAC Grid")
            sub.prop(domain, "vector_show_mac_x")
            sub.prop(domain, "vector_show_mac_y")
            sub.prop(domain, "vector_show_mac_z")
        else:
            col.prop(domain, "vector_scale_with_magnitude")

        col.prop(domain, "vector_field")
        col.prop(domain, "vector_scale")


class PHYSICS_PT_viewport_display_advanced(PhysicButtonsPanel, Panel):
    bl_label = "Advanced"
    bl_parent_id = "PHYSICS_PT_viewport_display"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        domain = context.fluid.domain_settings
        return PhysicButtonsPanel.poll_fluid_domain(context) and domain.use_slice and domain.show_gridlines

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings

        layout.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS'

        col = layout.column()
        col.prop(domain, "gridlines_color_field", text="Color Gridlines")

        if domain.gridlines_color_field == 'RANGE':
            if domain.use_color_ramp and domain.color_ramp_field != 'FLAGS':
                col.prop(domain, "gridlines_lower_bound")
                col.prop(domain, "gridlines_upper_bound")
                col.prop(domain, "gridlines_range_color")
                col.prop(domain, "gridlines_cell_filter")
            else:
                note = layout.split()
                if not domain.use_color_ramp:
                    note.label(icon='INFO', text="Enable Grid Display to use range highlighting!")
                else:
                    note.label(icon='INFO', text="Range highlighting for flags is not available!")


class PHYSICS_PT_fluid_domain_render(PhysicButtonsPanel, Panel):
    bl_label = "Render"
    bl_parent_id = "PHYSICS_PT_fluid"
    bl_options = {'DEFAULT_CLOSED'}
    COMPAT_ENGINES = {
        'BLENDER_RENDER',
        'BLENDER_EEVEE',
        'BLENDER_EEVEE_NEXT',
        'BLENDER_WORKBENCH',
    }

    @classmethod
    def poll(cls, context):
        if not PhysicButtonsPanel.poll_gas_domain(context):
            return False

        return (context.engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        domain = context.fluid.domain_settings
        layout.prop(domain, "velocity_scale")


classes = (
    FLUID_PT_presets,
    PHYSICS_PT_fluid,
    PHYSICS_PT_settings,
    PHYSICS_PT_borders,
    PHYSICS_PT_adaptive_domain,
    PHYSICS_PT_smoke,
    PHYSICS_PT_smoke_dissolve,
    PHYSICS_PT_noise,
    PHYSICS_PT_fire,
    PHYSICS_PT_liquid,
    PHYSICS_PT_viscosity,
    PHYSICS_PT_diffusion,
    PHYSICS_PT_particles,
    PHYSICS_PT_mesh,
    PHYSICS_PT_guide,
    PHYSICS_PT_collections,
    PHYSICS_PT_cache,
    PHYSICS_PT_export,
    PHYSICS_PT_field_weights,
    PHYSICS_PT_flow_source,
    PHYSICS_PT_flow_initial_velocity,
    PHYSICS_PT_flow_texture,
    PHYSICS_PT_viewport_display,
    PHYSICS_PT_viewport_display_slicing,
    PHYSICS_PT_viewport_display_color,
    PHYSICS_PT_viewport_display_debug,
    PHYSICS_PT_viewport_display_advanced,
    PHYSICS_PT_fluid_domain_render,
)


if __name__ == "__main__":  # only for live edit.
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
