1

I am creating a user control for fluid container. with reference to this post https://pptcrafter.wordpress.com/2014/05/14/animation-liquids-filling-bubbling-etc/. I am using on rectangle for fluid(water) and path for container(beaker here). I want to clip water rectangle in a way such that it only displayed in container. I have tried clipping using path geometry and also used opacity mask. But not getting desired output. I think I am missing something. Please help me to find that.
Desired output: _________________________________________________________________________
Without clipping :
_________________________________________________________________________

Clip using path:

_________________________________________________________________________

Using opacitymask:
_________________________________________________________________________


<UserControl
x:Class="FluidFill.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FluidFill"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<UserControl.Resources>
    <PathFigureCollection
        x:Key="fig">M0,0 L10,10 V135 A10,10 0 0 0 20,145 H 130 A10,10 0 0 0 140,135 V5 L145,0 Z</PathFigureCollection>
</UserControl.Resources>
<Grid
    Background="White">

        <Path Panel.ZIndex="1" RenderTransformOrigin="0.5,0.5"
            x:Name="ActualContainer"
            Stroke="Black"
            Fill="Transparent"
            StrokeThickness="2"
            Data="M0,0 L10,10 V135 A10,10 0 0 0 20,145 H 130 A10,10 0 0 0 140,135 V5 L145,0 Z"
            >
            <!--<Path.Effect>
                <DropShadowEffect
                    Color="#FF3C494B"
                    ShadowDepth="3"
                    Opacity="0.6" />
            </Path.Effect>-->
            <Path.RenderTransform>
            <TransformGroup>
                <TranslateTransform
                    X="50"
                    Y="30" />
                <RotateTransform
                    Angle="20" />
            </TransformGroup>
        </Path.RenderTransform>
        </Path>

    <Rectangle x:Name="Wtr"
        Height="200"
        Width="375"
        Fill="#4F81BD" Margin="0,52.5,0,47.5">
        <!--<Rectangle.Clip>
            <PathGeometry Transform="{Binding ElementName=ActualContainer,Path=RenderTransform}" Figures="{StaticResource fig}">                  
            </PathGeometry>
        </Rectangle.Clip>-->
        <!--<Rectangle.OpacityMask>
            <VisualBrush
                TileMode="Tile"
                Stretch="None">
                <VisualBrush.Visual>
                    <Grid
                        x:Name="waveGrid">

                        <Path  RenderTransform="{Binding ElementName=ActualContainer,Path=RenderTransform}"
                            Fill="#FF82C6FF"
                            Data="M0,0 L10,10 V135 A10,10 0 0 0 20,145 H 130 A10,10 0 0 0 140,135 V5 L145,0 Z"
                            />                            
                    </Grid>
                </VisualBrush.Visual>

            </VisualBrush>

        </Rectangle.OpacityMask>-->
    </Rectangle>


</Grid>

Himanshu Mange
  • 104
  • 1
  • 10

2 Answers2

1

Instead of setting the Path's RenderTransform, better set the Transform property of the Path's Geometry.

Now you can easily adjust the height of the water level by the Y value of the Rect in the second Path's RectangleGeometry.

<Grid Background="White">
    <Path x:Name="ActualContainer" Panel.ZIndex="1" Stroke="Black" StrokeThickness="2">
        <Path.Data>
            <PathGeometry Figures="M0,0 L10,10 V135 A10,10 0 0 0 20,145 H 130 A10,10 0 0 0 140,135 V5 L145,0 Z">
                <PathGeometry.Transform>
                    <TransformGroup>
                        <TranslateTransform X="50" Y="30"/>
                        <RotateTransform Angle="20"/>
                    </TransformGroup>
                </PathGeometry.Transform>
            </PathGeometry>
        </Path.Data>
    </Path>

    <Path Data="{Binding Data, ElementName=ActualContainer}" Fill="#4F81BD">
        <Path.Clip>
            <RectangleGeometry Rect="0,125,200,200"/>
        </Path.Clip>
    </Path>
</Grid>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Thanks @Clemens for quick solution. But can you explain me how you calculated bounds of RectangleGeometry rect ? – Himanshu Mange Mar 13 '19 at 09:16
  • 1
    It's not calculated, just large enough at the right and bottom. The top bound then simply defines the water level. It has of course to be aligned with the position of the container. – Clemens Mar 13 '19 at 09:18
1

The Water can be easily drawn with a CombinedGeometry. With this, you have the option to draw an Area where two Geometry overlap/intersect. Now you can draw a huge Rectangle as Water-Level + the Beaker and the resulting Area is your desired output.

XAML:

<UserControl.Resources>
    <!-- Form of the Beaker with Rotation -->
    <PathGeometry x:Key="BeakerForm" Figures="M0,0 L10,10 V135 A10,10 0 0 0 20,145 H 130 A10,10 0 0 0 140,135 V5 L145,0 Z">
        <PathGeometry.Transform>
            <!-- Angle of the Beaker Rotation (Note: CenterX must be the highest X-Coordinate of the Figures-Data) -->
            <RotateTransform Angle="10" CenterX="145"/>
        </PathGeometry.Transform>
    </PathGeometry>
</UserControl.Resources>

<!-- Canvas to draw the Beaker -->
<Canvas Background="White" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="100,100">
    <!-- Beaker Fill-Water -->
    <Path Fill="#4F81BD" RenderTransformOrigin="1,0">
        <Path.Data>
            <!-- A Rectangle and a Beaker wil be drawn and the blue areais only 
                 visible where theese two figures intersect (overlap) -->
            <CombinedGeometry GeometryCombineMode="Intersect">
                <CombinedGeometry.Geometry1>
                    <StaticResource ResourceKey="BeakerForm"/>
                </CombinedGeometry.Geometry1>
                <CombinedGeometry.Geometry2>
                    <RectangleGeometry Rect="-65,0 315,150" />
                </CombinedGeometry.Geometry2>
            </CombinedGeometry>
        </Path.Data>
    </Path>

    <!-- Beaker line in the foreground -->
    <Path RenderTransformOrigin="1,0" Stroke="Black" Fill="Transparent" StrokeThickness="2" Data="{StaticResource BeakerForm}"/>
</Canvas>

With this, you can adjust the Angle of the BeakerForm and the Water-Level will adapt to it.

enter image description here

Edit

The Beaker has a Size of x=150 and y=150 (rounded). It reaches its maximum Width, when it is rotatet for 45° and is sqrt(150^2 + 150^2) = ~215 in width. The height is unaffected due we set the water Level to 0. So we need a Rectangle which covers the area (Red) from (150 - 215) = -65 to 150 (315) and from 0 to 150 (Height) as illustrated below:

enter image description here

Then we can use theese Number a get Rect="-65,0 315,150".

Note: The Rotation Center is in the upper right corner of the Beaker/Square amd the Roation is ClockWise (cw).

LittleBit
  • 1,076
  • 6
  • 14
  • Thanks @LittleBit for help. Can you explain "" how to calculate Rect bounds for RectangleGeometry ? – Himanshu Mange Mar 13 '19 at 09:17
  • 1
    I haven't calculated them, i simply set them to a big number for a proof of concept. But you could calculate how much space the beaker needs when he is rotated and set its boundaries to it. I'll update my answer. – LittleBit Mar 13 '19 at 09:20