社攻联盟 - 大型多人在线游戏活动平台

社攻联盟 - 大型多人在线游戏活动平台

shape

程序员眼中的数学之美:高数 - 映射 (单射、满射、双射、逆映射)

  • Home
  • 跨服战场
  • 程序员眼中的数学之美:高数 - 映射 (单射、满射、双射、逆映射)
  • 2026-06-18 07:56:36
  • admin

​

什么是 映射

映射,是现代数学中的一个基本概念 ,包含单射、满射、双射、逆映射、复合映射。

单射:不同的输入映射到不同的输出;

满射:每个可能的输出都有至少一个输入映射到它;

双射:既是单射又是满射;

逆映射:只有一一对应的映射才有逆映射 ,即输入输出对调;

复合映射:把多个映射"串起来",先做A映射,再做B映射。

以下来以动画演示部分概念。

编程工具:VS2022、VSCode、C#、WinUI3(NET8.0)、Compostion API

AI辅助:Claude Code( https://www.cnblogs.com/xingrenh/p/18942223 )

演示:非单射-非满射

如:马赛克聚合效果

处理规则:

1,将多个像素映射到同一个大块(违反单射)

2,只覆盖图像的中心区域,留下边缘空白(违反满射)

视觉效果:中心区域呈现马赛克效果,边缘渐变消失

映射特点:

非单射的特点,是会对原数据损失或扭曲,不管是多到一的损失,还是一到多的想象扩展或变形,都对原数据进行了不可逆的破坏。下面演示由清晰的完整图,演变到细节丢失的、模糊的局部图,即 非单非满的马赛克聚合效果。

部分代码:

private void InitializeComposition()

{

var visual = ElementCompositionPreview.GetElementVisual(TargetCanvas);

_compositor = visual.Compositor;

_containerVisual = _compositor.CreateContainerVisual();

ElementCompositionPreview.SetElementChildVisual(TargetCanvas, _containerVisual);

// 初始化动画计时器

_animationTimer = new DispatcherTimer();

_animationTimer.Interval = TimeSpan.FromMilliseconds(16); // 约60fps

_animationTimer.Tick += AnimationTimer_Tick;

}

private void AnimationTimer_Tick(object sender, object e)

{

if (!_isAnimating || _blockVisuals.Count == 0) return;

// 每帧处理的块数量(基于速度)

var blocksPerFrame = Math.Max(1, (int)(5 * _animationSpeed));

// 更新所有已开始的块

for (int i = 0; i <= _currentBlockIndex && i < _blockVisuals.Count; i++)

{

var block = _blockVisuals[i];

if (block.AnimationProgress < 1.0)

{

// 更新动画进度

block.AnimationProgress = Math.Min(1.0, block.AnimationProgress + 0.03 * _animationSpeed);

// 计算插值位置

var progress = EaseInOutCubic(block.AnimationProgress);

if (block.IsActive)

{

// 活动块:移动到目标位置

var currentX = block.SourcePosition.X + (block.TargetPosition.X - block.SourcePosition.X) * progress;

var currentY = block.SourcePosition.Y + (block.TargetPosition.Y - block.SourcePosition.Y) * progress;

Canvas.SetLeft(block.Rectangle, currentX);

Canvas.SetTop(block.Rectangle, currentY);

block.Rectangle.Opacity = Math.Min(1.0, progress * 1.5); // 更快显示

// 颜色增强效果

var brightness = 1.0 + progress * 0.3;

var enhancedColor = BrightenColor(block.Color, brightness);

block.Rectangle.Fill = new SolidColorBrush(Color.FromArgb(enhancedColor.A, enhancedColor.R, enhancedColor.G, enhancedColor.B));

}

else

{

// 非活动块:淡出效果

block.Rectangle.Opacity = Math.Max(0, 0.3 - progress * 0.25);

}

}

}

// 启动新的块

_currentBlockIndex = Math.Min(_currentBlockIndex + blocksPerFrame, _blockVisuals.Count - 1);

// 检查是否所有动画完成

bool allComplete = true;

foreach (var block in _blockVisuals)

{

if (block.AnimationProgress < 1.0)

{

allComplete = false;

break;

}

}

if (allComplete)

{

_animationTimer.Stop();

_isAnimating = false;

StartButton.Content = "重新开始";

}

}

演示:单射-非满射

如:星云映射/旋涡收缩

处理规则:

1,每个源像素映射到唯一位置(保持单射)

2,映射后的图像分散到目标图像局部,留下很多空白(违反满射)

视觉效果-星云映射:源像素点飞落到不同位置,呈现散落的星云状态。

部分代码:

private void SetupAnimationTimer()

{

_animationTimer = new DispatcherTimer();

_animationTimer.Interval = TimeSpan.FromMilliseconds(16); // 60 FPS

_animationTimer.Tick += AnimationTimer_Tick;

}

private void AnimationTimer_Tick(object sender, object e)

{

if (!_isAnimating) return;

var currentTime = DateTime.Now.TimeOfDay.TotalSeconds;

foreach (var pixel in _pixelMappings)

{

if (!pixel.IsActive)

{

if (currentTime - pixel.StartTime >= pixel.StartDelay)

{

ActivatePixel(pixel);

pixel.IsActive = true;

_mappedCount++;

UpdateStats();

}

continue;

}

if (pixel.Visual != null)

{

var elapsed = (float)(currentTime - pixel.StartTime - pixel.StartDelay);

var progress = Math.Min(elapsed * _animationSpeed / 1.5f, 1.0f);

if (progress < 1.0f)

{

// 动画轨迹

var sourcePos = new Vector2(

pixel.SourcePosition.X * CANVAS_SIZE / _sourceBitmap.Width,

pixel.SourcePosition.Y * CANVAS_SIZE / _sourceBitmap.Height);

var t = EaseInOutCubic(progress);

var currentPos = Vector2.Lerp(sourcePos, pixel.TargetPosition, t);

// 添加轻微的曲线效果

var curve = Math.Sin(t * Math.PI) * 30;

currentPos.Y -= (float)curve;

pixel.Visual.Offset = new Vector3(currentPos.X - pixel.Size/2, currentPos.Y - pixel.Size/2, 0);

pixel.Visual.Opacity = Math.Min(1.0f, progress * 2);

// 更新源高亮

if (pixel.SourceHighlight != null)

{

pixel.SourceHighlight.Opacity = 1.0f - progress;

}

}

else

{

// 到达目标位置

pixel.Visual.Offset = new Vector3(pixel.TargetPosition.X - pixel.Size/2, pixel.TargetPosition.Y - pixel.Size/2, 0);

pixel.Visual.Opacity = 0.7f + pixel.Brightness * 0.3f;

// 闪烁效果

var twinkle = 1.0f + (float)Math.Sin(elapsed * Math.PI * 2) * 0.05f;

pixel.Visual.Scale = new Vector3(twinkle, twinkle, 1.0f);

// 显示占用指示器

if (pixel.OccupancyIndicator != null && pixel.OccupancyIndicator.Opacity < 1)

{

pixel.OccupancyIndicator.Opacity = Math.Min(1.0f, pixel.OccupancyIndicator.Opacity + 0.1f);

}

}

}

}

// 更新星座连线

if (_currentMode == MappingMode.ScatteredConstellation)

{

UpdateConstellationLines();

}

// 检查是否完成

if (_pixelMappings.All(p => p.IsActive))

{

var allReached = true;

foreach (var pixel in _pixelMappings)

{

var elapsed = (float)(currentTime - pixel.StartTime - pixel.StartDelay);

var progress = elapsed * _animationSpeed / 1.5f;

if (progress < 1.0f)

{

allReached = false;

break;

}

}

if (allReached)

{

_isAnimating = false;

_animationTimer.Stop();

StartButton.Content = "开始单射映射";

ShowCompletionStats();

}

}

}

视觉效果-旋涡收缩:原图像发生漩涡状扭曲收缩,局部留有旋涡的缝隙。

部分代码:

private void CompositionTarget_Rendering(object sender, object e)

{

var elapsed = (DateTime.Now - _startTime).TotalSeconds;

var allSpritesFinished = true;

// 更新每个精灵的位置和颜色

for (int i = 0; i < _spriteVisuals.Count; i++)

{

var sprite = _spriteVisuals[i];

var info = sprite.Comment.Split(',');

var targetX = float.Parse(info[0]);

var targetY = float.Parse(info[1]);

var normalizedDistance = float.Parse(info[2]);

var sourceX = float.Parse(info[3]);

var sourceY = float.Parse(info[4]);

// 计算延迟 - 基于距离的波纹效果

var delay = normalizedDistance * 0.2;

var adjustedElapsed = elapsed - delay;

if (adjustedElapsed < 0)

{

// 如果还没到开始时间,保持起点位置和透明

sprite.Opacity = 0;

allSpritesFinished = false;

continue;

}

var progress = Math.Min(adjustedElapsed / _animationDuration.TotalSeconds, 1.0);

if (progress < 1.0)

{

allSpritesFinished = false;

}

// 使用更平滑的缓动函数

var easedProgress = EaseInOutQuart(progress);

// 计算当前位置(线性插值)

var currentX = sourceX + (targetX - sourceX) * easedProgress;

var currentY = sourceY + (targetY - sourceY) * easedProgress;

// 更新位置

sprite.Offset = new Vector3((float)(currentX - sprite.Size.X / 2), (float)(currentY - sprite.Size.Y / 2), 0);

// 更新透明度 - 添加淡入效果

sprite.Opacity = (float)easedProgress;

// 保持原始颜色,只做轻微的亮度调整

var colorProgress = easedProgress;

var brightness = 1.0f + colorProgress * 0.15f; // 非常轻微的亮度变化

var originalColor = (sprite.Brush as CompositionColorBrush).Color;

// 保持原图颜色特征

var newColor = Color.FromArgb(

(byte)(255 * easedProgress),

(byte)Math.Min(255, originalColor.R * brightness),

(byte)Math.Min(255, originalColor.G * brightness),

(byte)Math.Min(255, originalColor.B * brightness)

);

(sprite.Brush as CompositionColorBrush).Color = newColor;

}

if (allSpritesFinished)

{

CompositionTarget.Rendering -= CompositionTarget_Rendering;

_isAnimating = false;

StartButton.Content = "开始动画";

}

}

演示:非单射-满射

例如:网格覆盖效果。

处理规则:

1,多个源像素混合映射到同一目标像素(违反单射)

2,但覆盖整个目标图像(保持满射)

视觉效果: 原图像的细节,像是被风化腐蚀一般,细节损失,却结果完整。

部分代码:

private void AnimationTimer_Tick(object sender, object e)

{

if (!_isAnimating) return;

var currentTime = DateTime.Now.TimeOfDay.TotalSeconds;

// 批量激活像素以提高性能

int activatedThisFrame = 0;

while (_nextActivationIndex < _pixelMappings.Count && activatedThisFrame < _batchSize)

{

var mapping = _pixelMappings[_nextActivationIndex];

if (!mapping.IsActive && currentTime - mapping.StartTime >= mapping.StartDelay)

{

ActivateMapping(mapping);

mapping.IsActive = true;

_activePixels.Add(mapping);

activatedThisFrame++;

}

_nextActivationIndex++;

}

// 只更新活跃的像素

for (int i = _activePixels.Count - 1; i >= 0; i--)

{

var mapping = _activePixels[i];

if (mapping.Visual != null && !mapping.HasReachedTarget)

{

var elapsed = (float)(currentTime - mapping.StartTime - mapping.StartDelay);

var progress = Math.Min(elapsed * _animationSpeed / 0.8f, 1.0f); // 稍微加快动画速度

if (progress < 1.0f)

{

// 简化动画轨迹以提高性能

var sourcePos = new Vector2(

mapping.SourcePosition.X * 400f / _sourceBitmap.Width,

mapping.SourcePosition.Y * 400f / _sourceBitmap.Height);

var currentPos = Vector2.Lerp(sourcePos, mapping.TargetPosition, progress);

// 简化弧线计算

if (progress < 0.5f)

{

var arc = progress * 2 * 50; // 增加弧线高度

currentPos.Y -= arc;

}

else

{

var arc = (1 - progress) * 2 * 50; // 增加弧线高度

currentPos.Y -= arc;

}

mapping.Visual.Offset = new Vector3(currentPos.X, currentPos.Y, 0);

}

else

{

// 到达目标

mapping.Visual.Offset = new Vector3(mapping.TargetPosition.X, mapping.TargetPosition.Y, 0);

mapping.HasReachedTarget = true;

// 更新网格颜色

UpdateGridCell(mapping.TargetCell);

// 从活跃列表中移除

_activePixels.RemoveAt(i);

}

}

}

// 检查是否完成

if (_nextActivationIndex >= _pixelMappings.Count && _activePixels.Count == 0)

{

_isAnimating = false;

_animationTimer.Stop();

StartButton.Content = "开始覆盖";

StartButton.IsEnabled = true;

// 确保最终覆盖率显示正确

DispatcherQueue.TryEnqueue(() =>

{

var finalCoverage = (_filledCells * 100.0) / _totalCells;

CoverageText.Text = $"覆盖率: {finalCoverage:F1}% - 满射完成!";

CoverageProgressBar.Value = finalCoverage;

});

}

}

private void ActivateMapping(PixelMapping mapping)

{

var visual = _compositor.CreateSpriteVisual();

visual.Size = new Vector2(mapping.Size, mapping.Size);

visual.Brush = _compositor.CreateColorBrush(

Color.FromArgb(mapping.Color.A, mapping.Color.R, mapping.Color.G, mapping.Color.B));

// 设置初始位置

var startPos = new Vector2(

mapping.SourcePosition.X * 400f / _sourceBitmap.Width,

mapping.SourcePosition.Y * 400f / _sourceBitmap.Height);

visual.Offset = new Vector3(startPos.X, startPos.Y, 0);

// 设置较低的渲染优先级以提高性能

visual.IsPixelSnappingEnabled = true;

_containerVisual.Children.InsertAtTop(visual);

mapping.Visual = visual;

}

演示:单射-满射

即双射、即一一映射。 如:波浪变形效果

处理规则:

1,每个像素一一对应映射(保持单射)

2,覆盖整个目标图像(保持满射)

视觉效果:优雅的正弦波变形,保持图像完整性。

部分代码:

private void AnimationTimer_Tick(object sender, object e)

{

_totalAnimationTime += 0.016 * _animationSpeed; // 16ms per frame

bool allAnimationsComplete = true;

foreach (var pixelVisual in _pixelVisuals)

{

var elapsedTime = _totalAnimationTime - pixelVisual.Delay;

if (elapsedTime < 0)

{

// 还未开始动画

allAnimationsComplete = false;

continue;

}

// 动画持续时间

var animationDuration = 2.0;

var progress = Math.Min(elapsedTime / animationDuration, 1.0);

if (progress < 1.0)

{

allAnimationsComplete = false;

}

// 使用缓动函数

var easedProgress = EaseInOutSine((float)progress);

// 插值位置

var currentX = pixelVisual.SourcePosition.X + (pixelVisual.TargetPosition.X - pixelVisual.SourcePosition.X) * easedProgress;

var currentY = pixelVisual.SourcePosition.Y + (pixelVisual.TargetPosition.Y - pixelVisual.SourcePosition.Y) * easedProgress;

// 添加动态波浪效果

if (progress > 0.5 && progress < 1.0)

{

float wavePhase = (float)(_totalAnimationTime * 2);

float dynamicWave = (float)(Math.Sin(wavePhase + pixelVisual.Col * 0.1) * 5 * (1 - progress));

currentY += dynamicWave;

}

pixelVisual.Visual.Offset = new Vector3(currentX, currentY, 0);

// 添加轻微的旋转效果

if (progress > 0.3 && progress < 0.7)

{

float rotation = (float)Math.Sin(progress * Math.PI) * 0.1f;

pixelVisual.Visual.RotationAngleInDegrees = rotation * 10;

}

else

{

pixelVisual.Visual.RotationAngleInDegrees = 0;

}

}

if (allAnimationsComplete && _isAnimating)

{

StopAnimation();

}

}

private float EaseInOutSine(float t)

{

return -(float)(Math.Cos(Math.PI * t) - 1) / 2;

}

无演示:逆映射

只有一一映射(双射)才有逆映射 ,逆映射就是把输入输出对调,反向的过程。因此,双射具有可还原性。以上个例子来说,即把扭曲后的图像,还原为原始图像的过程,不再演示。

数学,可以很美,很浪漫 ~

​

Previous Post
‎派派 App

Copyright © 2088 社攻联盟 - 大型多人在线游戏活动平台 All Rights Reserved.

友情链接