| Quintiq file version 2.0 | 
| #parent: CustomDrawComponentSupplyChainVisualization/CustomDrawDataLayerNode | 
| Response OnDrawObject ( | 
|   shadow[SCVNode] object | 
| ) id:Response_CustomDrawComponentSupplyChainVisualization_CustomDrawDataLayerNode_OnDrawObject | 
| { | 
|   #keys: '[132894.0.851837207]' | 
|   Body: | 
|   [* | 
|     // Draw SCVNode | 
|      | 
|     // Drawing parameters, node label, node title, label width, title width | 
|     scvconfig := DataHolderSCVConfiguration.Data(); | 
|     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.Left(), region.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.SCVNodeViolation() );     | 
|          | 
|         textformat := TextFormat::Construct( TextFormat::AlignCentered() );  | 
|         surface.TextColor( ColorScheme.SCVNodeViolation() );                                                                      | 
|         surface.TextZoom( region.Left(), region.Top(), nodesize, nodesize, '!', textformat );   | 
|       } | 
|       else if( object.IsRoot() ) // root color, with border | 
|       { | 
|         surface.Pen().Color( Color::Black() ) | 
|         surface.Brush().Color( ColorScheme.SCVRootNode() )  | 
|       }  | 
|       else // normal node, node color, no border | 
|       { | 
|         penWidth := 0; | 
|         surface.Brush().Color( ColorScheme.SCVNode() );   | 
|       } | 
|        | 
|       surface.Pen().Width( penWidth ); | 
|       circle := Shape::Circle( region.Left(), region.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 := scvconfig.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.Left() + textOffsetX, // X | 
|                       region.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() );  | 
|     // Draw node label | 
|     surface.TextZoom( region.Left() + textOffsetX, | 
|                       region.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.SCVEdge() ); | 
|              | 
|       surface.TextZoom( region.Left() + 2 * -nodesize, | 
|                         region.Top(), | 
|                         nodesize, | 
|                         nodesize, | 
|                         '...', | 
|                         textformat ); | 
|     } | 
|      | 
|     if( isDownstreamLimitHit ) | 
|     { | 
|       surface.Font().Bold( true ); | 
|       surface.TextColor( ColorScheme.SCVEdge() ); | 
|        | 
|       surface.TextZoom( region.Left() + 2 * nodesize, | 
|                         region.Top(), | 
|                         nodesize, | 
|                         nodesize, | 
|                         '...', | 
|                         textformat ); | 
|     } | 
|   *] | 
|   CanBindMultiple: false | 
|   DefinitionID: 'Responsedef_CustomDrawDataLayer_OnDrawObject' | 
| } |