1492 lines
40 KiB
C++
1492 lines
40 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2008 INESC Porto
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation;
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Gustavo Carneiro <gjc@inescporto.pt>
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include "pyviz.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/config.h"
|
|
#include "ns3/node-list.h"
|
|
#include "ns3/wifi-net-device.h"
|
|
#include "ns3/ppp-header.h"
|
|
#include "ns3/wifi-mac-header.h"
|
|
#include "ns3/ethernet-header.h"
|
|
#include "ns3/log.h"
|
|
#include "ns3/abort.h"
|
|
|
|
#include "visual-simulator-impl.h"
|
|
|
|
#include <sstream>
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("PyViz");
|
|
|
|
#define NUM_LAST_PACKETS 10
|
|
|
|
static
|
|
std::vector<std::string>
|
|
PathSplit (std::string str)
|
|
{
|
|
std::vector<std::string> results;
|
|
size_t cutAt;
|
|
while ((cutAt = str.find_first_of ('/')) != str.npos)
|
|
{
|
|
if(cutAt > 0)
|
|
{
|
|
results.push_back (str.substr (0,cutAt));
|
|
}
|
|
str = str.substr (cutAt+1);
|
|
}
|
|
if (str.length () > 0)
|
|
{
|
|
results.push_back (str);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
namespace ns3 {
|
|
|
|
static PyViz* g_visualizer = NULL; ///< the visualizer
|
|
|
|
|
|
/**
|
|
* PyVizPacketTag structure
|
|
*/
|
|
struct PyVizPacketTag : public Tag
|
|
{
|
|
static TypeId GetTypeId (void);
|
|
virtual TypeId GetInstanceTypeId (void) const;
|
|
virtual uint32_t GetSerializedSize (void) const;
|
|
virtual void Serialize (TagBuffer buf) const;
|
|
virtual void Deserialize (TagBuffer buf);
|
|
virtual void Print (std::ostream &os) const;
|
|
PyVizPacketTag ();
|
|
|
|
uint32_t m_packetId; ///< packet id
|
|
};
|
|
|
|
|
|
/**
|
|
* \brief Get the type ID.
|
|
* \return the object TypeId
|
|
*/
|
|
TypeId
|
|
PyVizPacketTag::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::PyVizPacketTag")
|
|
.SetParent<Tag> ()
|
|
.SetGroupName ("Visualizer")
|
|
.AddConstructor<PyVizPacketTag> ()
|
|
;
|
|
return tid;
|
|
}
|
|
TypeId
|
|
PyVizPacketTag::GetInstanceTypeId (void) const
|
|
{
|
|
return GetTypeId ();
|
|
}
|
|
uint32_t
|
|
PyVizPacketTag::GetSerializedSize (void) const
|
|
{
|
|
return 4;
|
|
}
|
|
void
|
|
PyVizPacketTag::Serialize (TagBuffer buf) const
|
|
{
|
|
buf.WriteU32 (m_packetId);
|
|
}
|
|
void
|
|
PyVizPacketTag::Deserialize (TagBuffer buf)
|
|
{
|
|
m_packetId = buf.ReadU32 ();
|
|
}
|
|
void
|
|
PyVizPacketTag::Print (std::ostream &os) const
|
|
{
|
|
os << "PacketId=" << m_packetId;
|
|
}
|
|
PyVizPacketTag::PyVizPacketTag ()
|
|
: Tag ()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
PyViz::PyViz ()
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (g_visualizer == NULL);
|
|
g_visualizer = this;
|
|
|
|
// WiFi
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx",
|
|
MakeCallback (&PyViz::TraceNetDevTxWifi, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx",
|
|
MakeCallback (&PyViz::TraceNetDevRxWifi, this));
|
|
|
|
// CSMA
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacTx",
|
|
MakeCallback (&PyViz::TraceNetDevTxCsma, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacRx",
|
|
MakeCallback (&PyViz::TraceNetDevRxCsma, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacPromiscRx",
|
|
MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
|
|
|
|
// Generic queue drop
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/TxQueue/Drop",
|
|
MakeCallback (&PyViz::TraceDevQueueDrop, this));
|
|
// IPv4 drop
|
|
Config::ConnectFailSafe ("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
|
|
MakeCallback (&PyViz::TraceIpv4Drop, this));
|
|
|
|
// Point-to-Point
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx",
|
|
MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacRx",
|
|
MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
|
|
|
|
// WiMax
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Tx",
|
|
MakeCallback (&PyViz::TraceNetDevTxWimax, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Rx",
|
|
MakeCallback (&PyViz::TraceNetDevRxWimax, this));
|
|
|
|
// LTE
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Tx",
|
|
MakeCallback (&PyViz::TraceNetDevTxLte, this));
|
|
|
|
Config::ConnectFailSafe ("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Rx",
|
|
MakeCallback (&PyViz::TraceNetDevRxLte, this));
|
|
}
|
|
|
|
void
|
|
PyViz::RegisterCsmaLikeDevice (std::string const &deviceTypeName)
|
|
{
|
|
TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
|
|
|
|
std::ostringstream sstream;
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/MacTx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxCsma, this));
|
|
|
|
sstream.str ("");
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxCsma, this));
|
|
|
|
sstream.str ("");
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/PromiscRx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
|
|
}
|
|
|
|
void
|
|
PyViz::RegisterWifiLikeDevice (std::string const &deviceTypeName)
|
|
{
|
|
TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
|
|
|
|
std::ostringstream sstream;
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Tx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxWifi, this));
|
|
|
|
sstream.str ("");
|
|
sstream <<"/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxWifi, this));
|
|
}
|
|
|
|
void
|
|
PyViz::RegisterPointToPointLikeDevice (std::string const &deviceTypeName)
|
|
{
|
|
TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
|
|
|
|
std::ostringstream sstream;
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
|
|
|
|
sstream.str ("");
|
|
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
|
|
Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
|
|
}
|
|
|
|
void
|
|
PyViz::SetPacketCaptureOptions (uint32_t nodeId, PacketCaptureOptions options)
|
|
{
|
|
NS_LOG_DEBUG (" SetPacketCaptureOptions " << nodeId
|
|
<< " PacketCaptureOptions (headers size = " << options.headers.size ()
|
|
<< " mode = " << options.mode << " numLastPackets = " << options.numLastPackets
|
|
<< ")");
|
|
m_packetCaptureOptions[nodeId] = options;
|
|
}
|
|
|
|
void
|
|
PyViz::RegisterDropTracePath (std::string const &tracePath)
|
|
{
|
|
Config::Connect (tracePath, MakeCallback (&PyViz::TraceDevQueueDrop, this));
|
|
}
|
|
|
|
PyViz::~PyViz ()
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
|
|
NS_ASSERT (g_visualizer == this);
|
|
g_visualizer = NULL;
|
|
}
|
|
|
|
void PyViz::DoPause (std::string const &message)
|
|
{
|
|
m_pauseMessages.push_back (message);
|
|
m_stop = true;
|
|
NS_LOG_LOGIC (Simulator::Now ().As (Time::S) << ": Have "
|
|
<< g_visualizer->m_pauseMessages.size () << " pause messages");
|
|
}
|
|
|
|
void PyViz::Pause (std::string const &message)
|
|
{
|
|
NS_ASSERT (g_visualizer);
|
|
g_visualizer->DoPause (message);
|
|
}
|
|
|
|
std::vector<std::string>
|
|
PyViz::GetPauseMessages () const
|
|
{
|
|
NS_LOG_LOGIC (Simulator::Now ().As (Time::S) << ": GetPauseMessages: have "
|
|
<< g_visualizer->m_pauseMessages.size () << " pause messages");
|
|
return m_pauseMessages;
|
|
}
|
|
|
|
|
|
void
|
|
PyViz::CallbackStopSimulation ()
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
if (m_runUntil <= Simulator::Now ())
|
|
{
|
|
Simulator::Stop (Seconds (0)); // Stop right now
|
|
m_stop = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
PyViz::SimulatorRunUntil (Time time)
|
|
{
|
|
NS_LOG_LOGIC ("SimulatorRunUntil " << time << " (now is " << Simulator::Now () << ")");
|
|
|
|
m_pauseMessages.clear ();
|
|
m_transmissionSamples.clear ();
|
|
m_packetDrops.clear ();
|
|
|
|
Time expirationTime = Simulator::Now () - Seconds (10);
|
|
|
|
// Clear very old transmission records
|
|
for (std::map<TxRecordKey, TxRecordValue>::iterator iter = m_txRecords.begin ();
|
|
iter != m_txRecords.end ();)
|
|
{
|
|
if (iter->second.time < expirationTime)
|
|
{
|
|
m_txRecords.erase (iter++);
|
|
}
|
|
else
|
|
{
|
|
iter++;
|
|
}
|
|
}
|
|
|
|
// Clear very old packets of interest
|
|
for (std::map<uint32_t, Time>::iterator iter = m_packetsOfInterest.begin ();
|
|
iter != m_packetsOfInterest.end ();)
|
|
{
|
|
if (iter->second < expirationTime)
|
|
{
|
|
m_packetsOfInterest.erase (iter++);
|
|
}
|
|
else
|
|
{
|
|
iter++;
|
|
}
|
|
}
|
|
|
|
if (Simulator::Now () >= time)
|
|
{
|
|
return;
|
|
}
|
|
// Schedule a dummy callback function for the target time, to make
|
|
// sure we stop at the right time. Otherwise, simulations with few
|
|
// events just appear to "jump" big chunks of time.
|
|
NS_LOG_LOGIC ("Schedule dummy callback to be called in " << (time - Simulator::Now ()));
|
|
m_runUntil = time;
|
|
m_stop = false;
|
|
Simulator::ScheduleWithContext (Simulator::NO_CONTEXT, time - Simulator::Now (), &PyViz::CallbackStopSimulation, this);
|
|
|
|
Ptr<SimulatorImpl> impl = Simulator::GetImplementation ();
|
|
Ptr<VisualSimulatorImpl> visualImpl = DynamicCast<VisualSimulatorImpl> (impl);
|
|
if (visualImpl)
|
|
{
|
|
visualImpl->RunRealSimulator ();
|
|
}
|
|
else
|
|
{
|
|
impl->Run ();
|
|
}
|
|
}
|
|
|
|
bool PyViz::TransmissionSampleKey::operator < (PyViz::TransmissionSampleKey const &other) const
|
|
{
|
|
if (this->transmitter < other.transmitter)
|
|
{
|
|
return true;
|
|
}
|
|
if (this->transmitter != other.transmitter)
|
|
{
|
|
return false;
|
|
}
|
|
if (this->receiver < other.receiver)
|
|
{
|
|
return true;
|
|
}
|
|
if (this->receiver != other.receiver)
|
|
{
|
|
return false;
|
|
}
|
|
if (this->channel < other.channel)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PyViz::TransmissionSampleKey::operator == (PyViz::TransmissionSampleKey const &other) const
|
|
{
|
|
bool retval = (transmitter == other.transmitter) &&
|
|
(receiver == other.receiver) &&
|
|
(channel == other.channel);
|
|
return retval;
|
|
}
|
|
|
|
|
|
PyViz::NetDeviceStatistics &
|
|
PyViz::FindNetDeviceStatistics (int node, int interface)
|
|
{
|
|
std::map<uint32_t, std::vector<NetDeviceStatistics> >::iterator nodeStatsIter = m_nodesStatistics.find (node);
|
|
std::vector<NetDeviceStatistics> *stats;
|
|
if (nodeStatsIter == m_nodesStatistics.end ())
|
|
{
|
|
stats = &m_nodesStatistics[node];
|
|
stats->resize (NodeList::GetNode (node)->GetNDevices ());
|
|
}
|
|
else
|
|
{
|
|
stats = &(nodeStatsIter->second);
|
|
}
|
|
NetDeviceStatistics &devStats = (*stats)[interface];
|
|
return devStats;
|
|
}
|
|
|
|
bool PyViz::GetPacketCaptureOptions (uint32_t nodeId, const PacketCaptureOptions **outOptions) const
|
|
{
|
|
std::map<uint32_t, PacketCaptureOptions>::const_iterator iter = m_packetCaptureOptions.find (nodeId);
|
|
if (iter == m_packetCaptureOptions.end ())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*outOptions = &iter->second;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool PyViz::FilterPacket (Ptr<const Packet> packet, const PacketCaptureOptions &options)
|
|
{
|
|
switch (options.mode)
|
|
{
|
|
case PACKET_CAPTURE_DISABLED:
|
|
return false;
|
|
|
|
case PACKET_CAPTURE_FILTER_HEADERS_OR:
|
|
{
|
|
PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
|
|
while (metadataIterator.HasNext ())
|
|
{
|
|
PacketMetadata::Item item = metadataIterator.Next ();
|
|
if (options.headers.find (item.tid) != options.headers.end ())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
case PACKET_CAPTURE_FILTER_HEADERS_AND:
|
|
{
|
|
std::set<TypeId> missingHeaders (options.headers);
|
|
PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
|
|
while (metadataIterator.HasNext ())
|
|
{
|
|
PacketMetadata::Item item = metadataIterator.Next ();
|
|
std::set<TypeId>::iterator missingIter = missingHeaders.find (item.tid);
|
|
if (missingIter != missingHeaders.end ())
|
|
{
|
|
missingHeaders.erase (missingIter);
|
|
}
|
|
}
|
|
if (missingHeaders.size () == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
default:
|
|
NS_FATAL_ERROR ("should not be reached");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
PyViz::TraceDevQueueDrop (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
NS_LOG_FUNCTION (context << packet->GetUid ());
|
|
std::vector<std::string> splitPath = PathSplit (context);
|
|
int nodeIndex = std::atoi (splitPath[1].c_str ());
|
|
Ptr<Node> node = NodeList::GetNode (nodeIndex);
|
|
|
|
if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
|
|
{
|
|
// if the transmitting node is not "of interest", we still
|
|
// record the transmission if it is a packet of interest.
|
|
if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
|
|
{
|
|
NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ---- "last packets"
|
|
const PacketCaptureOptions *captureOptions;
|
|
if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
|
|
{
|
|
LastPacketsSample &last = m_lastPackets[nodeIndex];
|
|
PacketSample lastPacket;
|
|
lastPacket.time = Simulator::Now ();
|
|
lastPacket.packet = packet->Copy ();
|
|
lastPacket.device = NULL;
|
|
last.lastDroppedPackets.push_back (lastPacket);
|
|
while (last.lastDroppedPackets.size () > captureOptions->numLastPackets)
|
|
{
|
|
last.lastDroppedPackets.erase (last.lastDroppedPackets.begin ());
|
|
}
|
|
}
|
|
|
|
std::map<Ptr<Node>, uint32_t>::iterator iter = m_packetDrops.find (node);
|
|
if (iter == m_packetDrops.end ())
|
|
{
|
|
m_packetDrops[node] = packet->GetSize ();
|
|
}
|
|
else
|
|
{
|
|
iter->second += packet->GetSize ();
|
|
}
|
|
}
|
|
|
|
void
|
|
PyViz::TraceIpv4Drop (std::string context, ns3::Ipv4Header const &hdr, Ptr<const Packet> packet,
|
|
ns3::Ipv4L3Protocol::DropReason reason, Ptr<Ipv4> dummy_ipv4, uint32_t interface)
|
|
{
|
|
Ptr<Packet> packetCopy = packet->Copy ();
|
|
packetCopy->AddHeader (hdr);
|
|
TraceDevQueueDrop (context, packetCopy);
|
|
}
|
|
|
|
|
|
// --------- TX device tracing -------------------
|
|
|
|
void
|
|
PyViz::TraceNetDevTxCommon (std::string const &context, Ptr<const Packet> packet,
|
|
Mac48Address const &destinationAddress)
|
|
{
|
|
NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
|
|
|
|
std::vector<std::string> splitPath = PathSplit (context);
|
|
int nodeIndex = std::atoi (splitPath[1].c_str ());
|
|
int devIndex = std::atoi (splitPath[3].c_str ());
|
|
Ptr<Node> node = NodeList::GetNode (nodeIndex);
|
|
Ptr<NetDevice> device = node->GetDevice (devIndex);
|
|
|
|
// ---- statistics
|
|
NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
|
|
++stats.transmittedPackets;
|
|
stats.transmittedBytes += packet->GetSize ();
|
|
|
|
// ---- "last packets"
|
|
const PacketCaptureOptions *captureOptions;
|
|
if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
|
|
{
|
|
LastPacketsSample &last = m_lastPackets[nodeIndex];
|
|
TxPacketSample lastPacket;
|
|
lastPacket.time = Simulator::Now ();
|
|
lastPacket.packet = packet->Copy ();
|
|
lastPacket.device = device;
|
|
lastPacket.to = destinationAddress;
|
|
last.lastTransmittedPackets.push_back (lastPacket);
|
|
while (last.lastTransmittedPackets.size () > captureOptions->numLastPackets)
|
|
{
|
|
last.lastTransmittedPackets.erase (last.lastTransmittedPackets.begin ());
|
|
}
|
|
}
|
|
|
|
// ---- transmissions records
|
|
|
|
if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
|
|
{
|
|
// if the transmitting node is not "of interest", we still
|
|
// record the transmission if it is a packet of interest.
|
|
if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
|
|
{
|
|
NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We will follow this packet throughout the network.
|
|
m_packetsOfInterest[packet->GetUid ()] = Simulator::Now ();
|
|
}
|
|
|
|
TxRecordValue record = { Simulator::Now (), node, false };
|
|
if (destinationAddress == device->GetBroadcast ())
|
|
{
|
|
record.isBroadcast = true;
|
|
}
|
|
|
|
m_txRecords[TxRecordKey (device->GetChannel (), packet->GetUid ())] = record;
|
|
|
|
PyVizPacketTag tag;
|
|
//packet->RemovePacketTag (tag);
|
|
tag.m_packetId = packet->GetUid ();
|
|
packet->AddByteTag (tag);
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevTxWifi (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
|
|
|
|
/*
|
|
* To DS From DS Address 1 Address 2 Address 3 Address 4
|
|
*----------------------------------------------------------------------
|
|
* 0 0 Destination Source BSSID N/A
|
|
* 0 1 Destination BSSID Source N/A
|
|
* 1 0 BSSID Source Destination N/A
|
|
* 1 1 Receiver Transmitter Destination Source
|
|
*/
|
|
WifiMacHeader hdr;
|
|
NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
|
|
Mac48Address destinationAddress;
|
|
if (hdr.IsToDs () && !hdr.IsFromDs ())
|
|
{
|
|
destinationAddress = hdr.GetAddr3 ();
|
|
}
|
|
else if (!hdr.IsToDs () && hdr.IsFromDs ())
|
|
{
|
|
destinationAddress = hdr.GetAddr1 ();
|
|
}
|
|
else if (!hdr.IsToDs () && !hdr.IsFromDs ())
|
|
{
|
|
destinationAddress = hdr.GetAddr1 ();
|
|
}
|
|
else
|
|
{
|
|
destinationAddress = hdr.GetAddr3 ();
|
|
}
|
|
TraceNetDevTxCommon (context, packet, destinationAddress);
|
|
}
|
|
|
|
|
|
void
|
|
PyViz::TraceNetDevTxCsma (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
EthernetHeader ethernetHeader;
|
|
NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
|
|
TraceNetDevTxCommon (context, packet, ethernetHeader.GetDestination ());
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevTxPointToPoint (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
TraceNetDevTxCommon (context, packet, Mac48Address ());
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------- RX device tracing -------------------
|
|
|
|
void
|
|
PyViz::TraceNetDevRxCommon (std::string const &context, Ptr<const Packet> packet, Mac48Address const &from)
|
|
{
|
|
uint32_t uid;
|
|
PyVizPacketTag tag;
|
|
if (packet->FindFirstMatchingByteTag (tag))
|
|
{
|
|
uid = tag.m_packetId;
|
|
}
|
|
else
|
|
{
|
|
//NS_ASSERT (0);
|
|
NS_LOG_WARN ("Packet has no byte tag; wimax link?");
|
|
uid = packet->GetUid ();
|
|
}
|
|
|
|
NS_LOG_FUNCTION (context << uid);
|
|
std::vector<std::string> splitPath = PathSplit (context);
|
|
int nodeIndex = std::atoi (splitPath[1].c_str ());
|
|
int devIndex = std::atoi (splitPath[3].c_str ());
|
|
|
|
// ---- statistics
|
|
NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
|
|
++stats.receivedPackets;
|
|
stats.receivedBytes += packet->GetSize ();
|
|
|
|
Ptr<Node> node = NodeList::GetNode (nodeIndex);
|
|
Ptr<NetDevice> device = node->GetDevice (devIndex);
|
|
|
|
// ---- "last packets"
|
|
const PacketCaptureOptions *captureOptions;
|
|
if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
|
|
{
|
|
LastPacketsSample &last = m_lastPackets[nodeIndex];
|
|
RxPacketSample lastPacket;
|
|
lastPacket.time = Simulator::Now ();
|
|
lastPacket.packet = packet->Copy ();
|
|
lastPacket.device = device;
|
|
lastPacket.from = from;
|
|
last.lastReceivedPackets.push_back (lastPacket);
|
|
while (last.lastReceivedPackets.size () > captureOptions->numLastPackets)
|
|
{
|
|
last.lastReceivedPackets.erase (last.lastReceivedPackets.begin ());
|
|
}
|
|
}
|
|
|
|
// ---- transmissions
|
|
if (m_packetsOfInterest.find (uid) == m_packetsOfInterest.end ())
|
|
{
|
|
NS_LOG_DEBUG ("RX Packet " << uid << " is not of interest");
|
|
return;
|
|
}
|
|
|
|
Ptr<Channel> channel = device->GetChannel ();
|
|
|
|
std::map<TxRecordKey, TxRecordValue>::iterator recordIter =
|
|
m_txRecords.find (TxRecordKey (channel, uid));
|
|
|
|
if (recordIter == m_txRecords.end ())
|
|
{
|
|
NS_LOG_DEBUG ("RX Packet " << uid << " was not transmitted?!");
|
|
return;
|
|
}
|
|
|
|
TxRecordValue &record = recordIter->second;
|
|
|
|
if (record.srcNode == node)
|
|
{
|
|
NS_LOG_WARN ("Node " << node->GetId () << " receiving back the same packet (UID=" << uid
|
|
<< ") it had previously transmitted, on the same channel!");
|
|
return;
|
|
}
|
|
|
|
TransmissionSampleKey key = { record.srcNode, node, channel };
|
|
|
|
#ifdef NS3_LOG_ENABLE
|
|
NS_LOG_DEBUG ("m_transmissionSamples begin:");
|
|
if (g_log.IsEnabled (ns3::LOG_DEBUG))
|
|
{
|
|
for (std::map<TransmissionSampleKey,TransmissionSampleValue>::const_iterator iter
|
|
= m_transmissionSamples.begin (); iter != m_transmissionSamples.end (); iter++)
|
|
{
|
|
NS_LOG_DEBUG (iter->first.transmitter<<"/"<<iter->first.transmitter->GetId () << ", "
|
|
<< iter->first.receiver<<"/"<<iter->first.receiver->GetId ()
|
|
<< ", " << iter->first.channel << " => " << iter->second.bytes << " (@ " << &iter->second << ")");
|
|
}
|
|
}
|
|
NS_LOG_DEBUG ("m_transmissionSamples end.");
|
|
#endif
|
|
|
|
std::map<TransmissionSampleKey,TransmissionSampleValue>::iterator
|
|
iter = m_transmissionSamples.find (key);
|
|
|
|
if (iter == m_transmissionSamples.end ())
|
|
{
|
|
TransmissionSampleValue sample = { packet->GetSize () };
|
|
NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId () << " to "
|
|
<< key.receiver<<"/"<<key.receiver->GetId ()
|
|
<< " channel " << channel << ": " << packet->GetSize ()
|
|
<< " bytes more. => new sample with " << packet->GetSize () << " bytes.");
|
|
m_transmissionSamples[key] = sample;
|
|
}
|
|
else
|
|
{
|
|
TransmissionSampleValue &sample = iter->second;
|
|
NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId () << " to "
|
|
<< key.receiver<<"/"<<key.receiver->GetId ()
|
|
<< " channel " << channel << ": " << packet->GetSize ()
|
|
<< " bytes more. => sample " << &sample << " with bytes " << sample.bytes);
|
|
|
|
sample.bytes += packet->GetSize ();
|
|
}
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevRxWifi (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
NS_LOG_FUNCTION (context << packet->GetUid ());
|
|
|
|
|
|
/*
|
|
* To DS From DS Address 1 Address 2 Address 3 Address 4
|
|
*----------------------------------------------------------------------
|
|
* 0 0 Destination Source BSSID N/A
|
|
* 0 1 Destination BSSID Source N/A
|
|
* 1 0 BSSID Source Destination N/A
|
|
* 1 1 Receiver Transmitter Destination Source
|
|
*/
|
|
WifiMacHeader hdr;
|
|
NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
|
|
Mac48Address sourceAddress;
|
|
if (hdr.IsToDs () && !hdr.IsFromDs ())
|
|
{
|
|
sourceAddress = hdr.GetAddr2 ();
|
|
}
|
|
else if (!hdr.IsToDs () && hdr.IsFromDs ())
|
|
{
|
|
sourceAddress = hdr.GetAddr3 ();
|
|
}
|
|
else if (!hdr.IsToDs () && !hdr.IsFromDs ())
|
|
{
|
|
sourceAddress = hdr.GetAddr2 ();
|
|
}
|
|
else
|
|
{
|
|
sourceAddress = hdr.GetAddr4 ();
|
|
}
|
|
|
|
TraceNetDevRxCommon (context, packet, sourceAddress);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
PyViz::TraceNetDevRxCsma (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
EthernetHeader ethernetHeader;
|
|
NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
|
|
TraceNetDevRxCommon (context, packet, ethernetHeader.GetSource ());
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevRxPointToPoint (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
TraceNetDevRxCommon (context, packet, Mac48Address ());
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevPromiscRxCsma (std::string context, Ptr<const Packet> packet)
|
|
{
|
|
EthernetHeader ethernetHeader;
|
|
NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
|
|
|
|
NetDevice::PacketType packetType = NetDevice::PACKET_OTHERHOST; // FIXME
|
|
|
|
// Other packet types are already being received by
|
|
// TraceNetDevRxCsma; we don't want to receive them twice.
|
|
if (packetType == NetDevice::PACKET_OTHERHOST)
|
|
{
|
|
TraceNetDevRxCommon (context, packet, ethernetHeader.GetDestination ());
|
|
}
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevTxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &destination)
|
|
{
|
|
NS_LOG_FUNCTION (context);
|
|
TraceNetDevTxCommon (context, packet, destination);
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevRxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &source)
|
|
{
|
|
NS_LOG_FUNCTION (context);
|
|
TraceNetDevRxCommon (context, packet, source);
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevTxLte (std::string context, Ptr<const Packet> packet, Mac48Address const &destination)
|
|
{
|
|
NS_LOG_FUNCTION (context);
|
|
TraceNetDevTxCommon (context, packet, destination);
|
|
}
|
|
|
|
void
|
|
PyViz::TraceNetDevRxLte (std::string context, Ptr<const Packet> packet, Mac48Address const &source)
|
|
{
|
|
NS_LOG_FUNCTION (context);
|
|
TraceNetDevRxCommon (context, packet, source);
|
|
}
|
|
|
|
// ---------------------
|
|
|
|
PyViz::TransmissionSampleList
|
|
PyViz::GetTransmissionSamples () const
|
|
{
|
|
NS_LOG_DEBUG ("GetTransmissionSamples BEGIN");
|
|
TransmissionSampleList list;
|
|
for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator
|
|
iter = m_transmissionSamples.begin ();
|
|
iter != m_transmissionSamples.end ();
|
|
iter++)
|
|
{
|
|
TransmissionSample sample;
|
|
sample.transmitter = iter->first.transmitter;
|
|
sample.receiver = iter->first.receiver;
|
|
sample.channel = iter->first.channel;
|
|
sample.bytes = iter->second.bytes;
|
|
NS_LOG_DEBUG ("from " << sample.transmitter->GetId () << " to " << sample.receiver->GetId ()
|
|
<< ": " << sample.bytes << " bytes.");
|
|
list.push_back (sample);
|
|
}
|
|
NS_LOG_DEBUG ("GetTransmissionSamples END");
|
|
return list;
|
|
}
|
|
|
|
PyViz::PacketDropSampleList
|
|
PyViz::GetPacketDropSamples () const
|
|
{
|
|
NS_LOG_DEBUG ("GetPacketDropSamples BEGIN");
|
|
PacketDropSampleList list;
|
|
for (std::map<Ptr<Node>, uint32_t>::const_iterator
|
|
iter = m_packetDrops.begin ();
|
|
iter != m_packetDrops.end ();
|
|
iter++)
|
|
{
|
|
PacketDropSample sample;
|
|
sample.transmitter = iter->first;
|
|
sample.bytes = iter->second;
|
|
NS_LOG_DEBUG ("in " << sample.transmitter->GetId ()
|
|
<< ": " << sample.bytes << " bytes dropped.");
|
|
list.push_back (sample);
|
|
}
|
|
NS_LOG_DEBUG ("GetPacketDropSamples END");
|
|
return list;
|
|
}
|
|
|
|
void
|
|
PyViz::SetNodesOfInterest (std::set<uint32_t> nodes)
|
|
{
|
|
m_nodesOfInterest = nodes;
|
|
}
|
|
|
|
std::vector<PyViz::NodeStatistics>
|
|
PyViz::GetNodesStatistics () const
|
|
{
|
|
std::vector<PyViz::NodeStatistics> retval;
|
|
for (std::map<uint32_t, std::vector<NetDeviceStatistics> >::const_iterator iter = m_nodesStatistics.begin ();
|
|
iter != m_nodesStatistics.end (); iter++)
|
|
{
|
|
NodeStatistics stats = { iter->first, iter->second };
|
|
retval.push_back (stats);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
PyViz::LastPacketsSample
|
|
PyViz::GetLastPackets (uint32_t nodeId) const
|
|
{
|
|
NS_LOG_DEBUG ("GetLastPackets: " << nodeId);
|
|
|
|
std::map<uint32_t, LastPacketsSample>::const_iterator
|
|
iter = m_lastPackets.find (nodeId);
|
|
if (iter != m_lastPackets.end ())
|
|
{
|
|
return iter->second;
|
|
}
|
|
else
|
|
{
|
|
return LastPacketsSample ();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
{
|
|
/// Adapted from http://en.wikipedia.org/w/index.php?title=Line_clipping&oldid=248609574
|
|
class FastClipping
|
|
{
|
|
public:
|
|
/// Vector2 structure
|
|
struct Vector2
|
|
{
|
|
double x; ///< X
|
|
double y; ///< Y
|
|
};
|
|
|
|
Vector2 m_clipMin; ///< clip minimum
|
|
Vector2 m_clipMax; ///< clip maximum
|
|
|
|
/// Line structure
|
|
struct Line
|
|
{
|
|
Vector2 start; ///< start
|
|
Vector2 end; ///< end
|
|
double dx; ///< dX
|
|
double dy; ///< dY
|
|
};
|
|
|
|
private:
|
|
|
|
/**
|
|
* Clip start top function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipStartTop (Line &line)
|
|
{
|
|
line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
|
|
line.start.y = m_clipMin.y;
|
|
}
|
|
|
|
/**
|
|
* Clip start bottom function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipStartBottom (Line &line)
|
|
{
|
|
line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
|
|
line.start.y = m_clipMax.y;
|
|
}
|
|
|
|
/**
|
|
* Clip start right function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipStartRight (Line &line)
|
|
{
|
|
line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
|
|
line.start.x = m_clipMax.x;
|
|
}
|
|
|
|
/**
|
|
* Clip start left function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipStartLeft (Line &line)
|
|
{
|
|
line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
|
|
line.start.x = m_clipMin.x;
|
|
}
|
|
|
|
/**
|
|
* Clip end top function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipEndTop (Line &line)
|
|
{
|
|
line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
|
|
line.end.y = m_clipMin.y;
|
|
}
|
|
|
|
/**
|
|
* Clip end bottom function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipEndBottom (Line &line) {
|
|
line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
|
|
line.end.y = m_clipMax.y;
|
|
}
|
|
|
|
/**
|
|
* Clip end right function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipEndRight (Line &line)
|
|
{
|
|
line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
|
|
line.end.x = m_clipMax.x;
|
|
}
|
|
|
|
/**
|
|
* Clip end left function
|
|
* \param line the clip line
|
|
*/
|
|
void ClipEndLeft (Line &line)
|
|
{
|
|
line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
|
|
line.end.x = m_clipMin.x;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param clipMin minimum clipping vector
|
|
* \param clipMax maximum clipping vector
|
|
*/
|
|
FastClipping (Vector2 clipMin, Vector2 clipMax)
|
|
: m_clipMin (clipMin), m_clipMax (clipMax)
|
|
{
|
|
}
|
|
|
|
|
|
/**
|
|
* Clip line function
|
|
* \param line the clip line
|
|
* \returns true if clipped
|
|
*/
|
|
bool ClipLine (Line &line)
|
|
{
|
|
uint8_t lineCode = 0;
|
|
|
|
if (line.end.y < m_clipMin.y)
|
|
lineCode |= 8;
|
|
else if (line.end.y > m_clipMax.y)
|
|
lineCode |= 4;
|
|
|
|
if (line.end.x > m_clipMax.x)
|
|
lineCode |= 2;
|
|
else if (line.end.x < m_clipMin.x)
|
|
lineCode |= 1;
|
|
|
|
if (line.start.y < m_clipMin.y)
|
|
lineCode |= 128;
|
|
else if (line.start.y > m_clipMax.y)
|
|
lineCode |= 64;
|
|
|
|
if (line.start.x > m_clipMax.x)
|
|
lineCode |= 32;
|
|
else if (line.start.x < m_clipMin.x)
|
|
lineCode |= 16;
|
|
|
|
// 9 - 8 - A
|
|
// | | |
|
|
// 1 - 0 - 2
|
|
// | | |
|
|
// 5 - 4 - 6
|
|
switch (lineCode)
|
|
{
|
|
// center
|
|
case 0x00:
|
|
return true;
|
|
|
|
case 0x01:
|
|
ClipEndLeft (line);
|
|
return true;
|
|
|
|
case 0x02:
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
case 0x04:
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x05:
|
|
ClipEndLeft (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x06:
|
|
ClipEndRight (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x08:
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x09:
|
|
ClipEndLeft (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x0A:
|
|
ClipEndRight (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
// left
|
|
case 0x10:
|
|
ClipStartLeft (line);
|
|
return true;
|
|
|
|
case 0x12:
|
|
ClipStartLeft (line);
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
case 0x14:
|
|
ClipStartLeft (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x16:
|
|
ClipStartLeft (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
ClipEndBottom (line);
|
|
if (line.end.x > m_clipMax.x)
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
case 0x18:
|
|
ClipStartLeft (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x1A:
|
|
ClipStartLeft (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
ClipEndTop (line);
|
|
if (line.end.x > m_clipMax.x)
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
// right
|
|
case 0x20:
|
|
ClipStartRight (line);
|
|
return true;
|
|
|
|
case 0x21:
|
|
ClipStartRight (line);
|
|
ClipEndLeft (line);
|
|
return true;
|
|
|
|
case 0x24:
|
|
ClipStartRight (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x25:
|
|
ClipStartRight (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
ClipEndBottom (line);
|
|
if (line.end.x < m_clipMin.x)
|
|
ClipEndLeft (line);
|
|
return true;
|
|
|
|
case 0x28:
|
|
ClipStartRight (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x29:
|
|
ClipStartRight (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
ClipEndTop (line);
|
|
if (line.end.x < m_clipMin.x)
|
|
ClipEndLeft (line);
|
|
return true;
|
|
|
|
// bottom
|
|
case 0x40:
|
|
ClipStartBottom (line);
|
|
return true;
|
|
|
|
case 0x41:
|
|
ClipStartBottom (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
return false;
|
|
ClipEndLeft (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x42:
|
|
ClipStartBottom (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
return false;
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
case 0x48:
|
|
ClipStartBottom (line);
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x49:
|
|
ClipStartBottom (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
return false;
|
|
ClipEndLeft (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
case 0x4A:
|
|
ClipStartBottom (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
return false;
|
|
ClipEndRight (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
// bottom-left
|
|
case 0x50:
|
|
ClipStartLeft (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
ClipStartBottom (line);
|
|
return true;
|
|
|
|
case 0x52:
|
|
ClipEndRight (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
return false;
|
|
ClipStartBottom (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
ClipStartLeft (line);
|
|
return true;
|
|
|
|
case 0x58:
|
|
ClipEndTop (line);
|
|
if (line.end.x < m_clipMin.x)
|
|
return false;
|
|
ClipStartBottom (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
ClipStartLeft (line);
|
|
return true;
|
|
|
|
case 0x5A:
|
|
ClipStartLeft (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
ClipEndRight (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
return false;
|
|
if (line.start.y > m_clipMax.y)
|
|
ClipStartBottom (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
return true;
|
|
|
|
// bottom-right
|
|
case 0x60:
|
|
ClipStartRight (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
ClipStartBottom (line);
|
|
return true;
|
|
|
|
case 0x61:
|
|
ClipEndLeft (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
return false;
|
|
ClipStartBottom (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
ClipStartRight (line);
|
|
return true;
|
|
|
|
case 0x68:
|
|
ClipEndTop (line);
|
|
if (line.end.x > m_clipMax.x)
|
|
return false;
|
|
ClipStartRight (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
ClipStartBottom (line);
|
|
return true;
|
|
|
|
case 0x69:
|
|
ClipEndLeft (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
return false;
|
|
ClipStartRight (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
return false;
|
|
if (line.end.y < m_clipMin.y)
|
|
ClipEndTop (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
ClipStartBottom (line);
|
|
return true;
|
|
|
|
// top
|
|
case 0x80:
|
|
ClipStartTop (line);
|
|
return true;
|
|
|
|
case 0x81:
|
|
ClipStartTop (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
return false;
|
|
ClipEndLeft (line);
|
|
return true;
|
|
|
|
case 0x82:
|
|
ClipStartTop (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
return false;
|
|
ClipEndRight (line);
|
|
return true;
|
|
|
|
case 0x84:
|
|
ClipStartTop (line);
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x85:
|
|
ClipStartTop (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
return false;
|
|
ClipEndLeft (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
case 0x86:
|
|
ClipStartTop (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
return false;
|
|
ClipEndRight (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
// top-left
|
|
case 0x90:
|
|
ClipStartLeft (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
return true;
|
|
|
|
case 0x92:
|
|
ClipEndRight (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
return false;
|
|
ClipStartTop (line);
|
|
if (line.start.x < m_clipMin.x)
|
|
ClipStartLeft (line);
|
|
return true;
|
|
|
|
case 0x94:
|
|
ClipEndBottom (line);
|
|
if (line.end.x < m_clipMin.x)
|
|
return false;
|
|
ClipStartLeft (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
return true;
|
|
|
|
case 0x96:
|
|
ClipStartLeft (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
ClipEndRight (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
return false;
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
return true;
|
|
|
|
// top-right
|
|
case 0xA0:
|
|
ClipStartRight (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
return true;
|
|
|
|
case 0xA1:
|
|
ClipEndLeft (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
return false;
|
|
ClipStartTop (line);
|
|
if (line.start.x > m_clipMax.x)
|
|
ClipStartRight (line);
|
|
return true;
|
|
|
|
case 0xA4:
|
|
ClipEndBottom (line);
|
|
if (line.end.x > m_clipMax.x)
|
|
return false;
|
|
ClipStartRight (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
return true;
|
|
|
|
case 0xA5:
|
|
ClipEndLeft (line);
|
|
if (line.end.y < m_clipMin.y)
|
|
return false;
|
|
ClipStartRight (line);
|
|
if (line.start.y > m_clipMax.y)
|
|
return false;
|
|
if (line.end.y > m_clipMax.y)
|
|
ClipEndBottom (line);
|
|
if (line.start.y < m_clipMin.y)
|
|
ClipStartTop (line);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
|
|
void
|
|
PyViz::LineClipping (double boundsX1, double boundsY1, double boundsX2, double boundsY2,
|
|
double &lineX1, double &lineY1, double &lineX2, double &lineY2)
|
|
{
|
|
FastClipping::Vector2 clipMin = { boundsX1, boundsY1}, clipMax = { boundsX2, boundsY2};
|
|
FastClipping::Line line = { { lineX1, lineY1 }, { lineX2, lineY2 }, (lineX2-lineX1), (lineY2-lineY1) };
|
|
|
|
FastClipping clipper (clipMin, clipMax);
|
|
clipper.ClipLine (line);
|
|
lineX1 = line.start.x;
|
|
lineX2 = line.end.x;
|
|
lineY1 = line.start.y;
|
|
lineY2 = line.end.y;
|
|
}
|
|
|
|
|
|
}
|
|
|