| Quintiq file version 2.0 | 
| #parent: PanelSCV/CustomDrawDataLayerSCVNode_549 | 
| Response OnDrawObject ( | 
|   shadow[SCVNode] object | 
| ) id:Response_PanelSCV_CustomDrawDataLayerSCVNode_OnDrawObject | 
| { | 
|   #keys: '[134266.2.4979938]' | 
|   CanBindMultiple: false | 
|   DefinitionID: 'Responsedef_WebCustomDrawDataLayer_OnDrawObject' | 
|   QuillAction | 
|   { | 
|     Body: | 
|     [* | 
|       // Draw SCVNode | 
|        | 
|       // Drawing parameters, node label, node title, label width, title width | 
|       scvconfig := MacroPlan.SCVConfiguration(); | 
|       nodesize := scvconfig.NodeSize(); | 
|        | 
|       nodetitle := '' | 
|       nodelabel := '' | 
|       iconimage := ''; | 
|       textformat := TextFormat::Construct( TextFormat::AlignBottom(), TextFormat::AlignCenter() );  | 
|        | 
|       // Specify labels, iconimage based on draw type | 
|       if( object.istype( shadow[SCVPISPIPNode] ) ) | 
|       { | 
|         pisp := object.astype( shadow[SCVPISPIPNode] ).ProductInStockingPointInPeriod().ProductInStockingPoint_MP(); | 
|         nodetitle := pisp.Product_MP().Name(); | 
|         nodelabel := pisp.StockingPoint_MP().Name(); | 
|         iconimage := ifexpr( pisp.Product_MP().IconName() <> '', pisp.Product_MP().IconName(), 'PRODUCT2' ); | 
|        | 
|       }            | 
|       else if( object.istype( shadow[SCVRoutingStepNode] ) ) | 
|       { | 
|         routingStep := select( object.astype( shadow[SCVRoutingStepNode] ), OperationInNode.Operation.RoutingStep, rs, true ); | 
|         nodelabel := routingStep.Name(); | 
|         iconimage := ifexpr( routingStep.Operation( relsize ) > 1, 'SIZES', 'SHAPE_SQUARE_BLUE' ); | 
|       } | 
|       else if( object.istype( shadow[SCVLaneNode] ) ) | 
|       { | 
|         lane := object.astype( shadow[SCVLaneNode] ).Lane(); | 
|         nodelabel := lane.Name(); | 
|         iconimage := 'DELIVERY_AMBER'; | 
|       } | 
|       else if( object.istype( shadow[SCVISNode] ) ) | 
|       { | 
|         nodelabel := object.Name(); | 
|         iconimage := 'HAND_TRUCK_BOX'; | 
|       } | 
|       else if( object.istype( shadow[SCVSDNode] ) ) | 
|       { | 
|         node := object.astype( shadow[SCVSDNode] ); | 
|         nodelabel := object.Name(); | 
|         iconimage := ifexpr( node.ProductInStockingPointInPeriod().GetAreAllAssociatedSDIPsFulfilled(), 'DOCUMENT', 'DOCUMENT_DIRTY' ); | 
|       } | 
|        | 
|       // Text width, derived from horizontal offset | 
|       textWidth := floor( scvconfig.OffsetX() / 2 ); | 
|       textOffsetX := floor( ( nodesize - textWidth ) / 2 ); | 
|       textHeight := floor( scvconfig.OffsetY() / 2 ); | 
|        | 
|       // Draw node | 
|       if( scvconfig.IsUsingIconName() or not object.istype( shadow[SCVPISPIPNode] ) ) | 
|       { | 
|         surface.Image( region.BoundingRect().Left(), region.BoundingRect().Top(), nodesize, nodesize, iconimage ) | 
|       } | 
|       else | 
|       { | 
|         node := object.astype( shadow[SCVPISPIPNode] ); | 
|         penWidth := 2; | 
|          | 
|         if( node.ProductInStockingPointInPeriod().istype( ProductInStockingPointInPeriodPlanning ) | 
|             and not node.ProductInStockingPointInPeriod().astype( ProductInStockingPointInPeriodPlanning ).IsValidPlannedBalance() ) // violation, no color with red border | 
|         { | 
|           // Draw planning violation indicator | 
|           surface.Pen().Color( ColorScheme.SupplyChainVisualization_NodeViolation() );     | 
|            | 
|           textformat_violation := TextFormat::Construct( TextFormat::AlignCentered() );  | 
|           surface.TextColor( ColorScheme.SupplyChainVisualization_NodeViolation() );                                                                      | 
|           surface.TextZoom( region.BoundingRect().Left(), region.BoundingRect().Top(), nodesize, nodesize, '!', textformat_violation ); | 
|         } | 
|         else if( object.IsRoot() ) // root color, with border | 
|         { | 
|           surface.Pen().Color( Color::Black() ) | 
|           surface.Brush().Color( ColorScheme.SupplyChainVisualization_RootNode() )  | 
|         }  | 
|         else // normal node, node color, no border | 
|         { | 
|           penWidth := 0; | 
|           surface.Brush().Color( ColorScheme.SupplyChainVisualization_Node() );   | 
|         } | 
|          | 
|         surface.Pen().Width( penWidth ); | 
|         circle := Shape::Circle( region.BoundingRect().Left(), region.BoundingRect().Top(), nodesize ); | 
|         surface.Fill( circle ); | 
|         surface.Draw( circle ); | 
|       } | 
|        | 
|       // Update pen parameters | 
|       surface.Font().Name( scvconfig.FontName() ) | 
|                     .Size(scvconfig.FontSize() ); | 
|       surface.TextColor( Color::Black() ); | 
|        | 
|       // Draw text | 
|       if( object.IsRoot() ) | 
|       { | 
|         surface.Font().Bold( true ); | 
|         | 
|         // Draw period below the root node | 
|         periodStart := object.astype( shadow[SCVPISPIPNode] ).ProductInStockingPointInPeriod().Period_MP().StartDate(); | 
|         periodString := periodStart.Format( 'D-MM-YOnMax(-)OnMin(-)' ); | 
|          | 
|         nodelabel := nodelabel + String::NewLine() + periodString; | 
|       } | 
|        | 
|       // For node title, we define a region above the top of the node | 
|       // then align it bottom, so that the text sticks to the top of the node | 
|       // Draw node title | 
|       surface.TextZoom( region.BoundingRect().Left() + textOffsetX, // X | 
|                         region.BoundingRect().Top() - textHeight,   // Y | 
|                         textWidth,                   // Width | 
|                         textHeight,                  // Height | 
|                         nodetitle,                   // Text | 
|                         textformat );                // Text format | 
|        | 
|       // For text label, we make the draw region starts at the bottom of the node then align it top, so that | 
|       // it sticks to the bottom of the node                       | 
|       textformat := TextFormat::Construct( TextFormat::AlignTop(), TextFormat::AlignCenter(), TextFormat::WrapWords() );  | 
|       // Draw node label | 
|       surface.TextZoom( region.BoundingRect().Left() + textOffsetX, | 
|                         region.BoundingRect().Bottom(),              | 
|                         textWidth,                    | 
|                         textHeight,                   | 
|                         nodelabel, | 
|                         textformat ); | 
|        | 
|       // Draw indication for stream limit hit | 
|       isUpstreamLimitHit := object.IsUpstream() and object.IsDepthLimitMet(); | 
|       isDownstreamLimitHit := not object.IsUpstream() and object.IsDepthLimitMet(); | 
|        | 
|       // Special handling for root | 
|       if( object.IsRoot() ) | 
|       { | 
|         node := object.astype( shadow[SCVPISPIPNode] ); | 
|         isUpstreamLimitHit := scvconfig.UpstreamDepthLimit() = 0  | 
|                               and ( ( FormSupplyChainVisualization.GetInputOutputs( node.ProductInStockingPointInPeriod(), true /*isupstream*/ ).Size() > 0 ) | 
|                                       or node.ProductInStockingPointInPeriod().InventorySupplyQuantity() > 0 ); | 
|         isDownstreamLimitHit := scvconfig.DownstreamDepthLimit() = 0  | 
|                                 and ( ( FormSupplyChainVisualization.GetInputOutputs( node.ProductInStockingPointInPeriod(), false /*isupstream*/ ).Size() > 0 ) | 
|                                         or node.ProductInStockingPointInPeriod().SalesDemandInPeriodBase( relsize ) > 0 );                                                                         | 
|       } | 
|        | 
|       textformat := TextFormat::Construct( TextFormat::AlignCentered() ); | 
|        | 
|       if( isUpstreamLimitHit ) | 
|       { | 
|         surface.Font().Bold( true ); | 
|         surface.TextColor( ColorScheme.SupplyChainVisualization_Edge() ); | 
|                | 
|         surface.TextZoom( region.BoundingRect().Left() + 2 * -nodesize, | 
|                           region.BoundingRect().Top(), | 
|                           nodesize, | 
|                           nodesize, | 
|                           '...', | 
|                           textformat ); | 
|       } | 
|        | 
|       if( isDownstreamLimitHit ) | 
|       { | 
|         surface.Font().Bold( true ); | 
|         surface.TextColor( ColorScheme.SupplyChainVisualization_Edge() ); | 
|          | 
|         surface.TextZoom( region.BoundingRect().Left() + 2 * nodesize, | 
|                           region.BoundingRect().Top(), | 
|                           nodesize, | 
|                           nodesize, | 
|                           '...', | 
|                           textformat ); | 
|       } | 
|     *] | 
|     GroupServerCalls: false | 
|   } | 
| } |